# Nextjs

## Accura IDScan Plugin — Next.js Integration Guide

This guide walks you through integrating the **Accura IDScan Plugin** into a Next.js project using the App Router.

### Step 1: Initialize Project

If you do not have an existing Next.js project, create one using the official CLI:

```bash
npx create-next-app@latest my-id-app
cd my-id-app
```

***

### Step 2: Install Plugin

Install the Accura IDScan Plugin package from the npm registry:

```bash
npm install accuraidscanplugin
```

***

### Step 3: TypeScript Support

Since the `accuraidscanplugin` package does not ship with TypeScript declarations, create a type definition file at the project root to resolve module errors:

```typescript
// types.d.ts (place in the project root)
declare module 'accuraidscanplugin';
```

Ensure this file is referenced in your `tsconfig.json`'s `include` array:

```json
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types.d.ts"]
```

***

### Step 4: Implementation

Create `components/IDScanner.tsx`. The snippet below demonstrates only the **plugin import and instantiation** — the minimal code required to activate the ID scan engine in a Next.js client component:

```tsx
"use client"; // Required: marks this as a Client Component for browser-only execution

import { useEffect, useRef } from "react";

export default function IDScanner() {
    const pluginRef = useRef(null);        // Persists the plugin instance across renders
    const initialized = useRef(false);    // Prevents double-init from React Strict Mode

    useEffect(() => {
        // Exit early if the plugin has already been initialized
        if (initialized.current) return;
        initialized.current = true;

        // Dynamically import the plugin at runtime to avoid SSR-related errors.
        // Next.js renders components on the server by default; browser-dependent APIs
        // (camera, DOM manipulation) are unavailable in that environment.
        // Dynamic import defers execution to the client side exclusively.
        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;

            // Instantiate the plugin with:
            //   1. The capture callback: invoked when scan is complete
            //   2. Configuration: specifies the target country, card template, and UI styling
            pluginRef.current = new IDCardPlugin(
                handleCapture,   // Called automatically upon successful ID card scan
                {
                    countryCode: "UGA",    // country code of the card
                    cardCode: "UGNIDF",    // Card code of the card
                    topTextSize: "",       // Top instruction overlay text size (default if empty)
                    topTextColor: "",      // Top instruction overlay text color (default if empty)
                    topTextWeight: "",     // Top instruction overlay font weight (default if empty)
                    bottomTextSize: "",    // Bottom instruction overlay text size (default if empty)
                    bottomTextColor: "",   // Bottom instruction overlay text color (default if empty)
                    bottomTextWeight: "",  // Bottom instruction overlay font weight (default if empty)
                }
            );

            // Launch the camera interface and begin the ID card detection session.
            pluginRef.current.start().then(() => {
                console.log("ID Scanner Ready");
            });
        }).catch(err => console.error("Plugin failed to load:", err));

        // Cleanup: invoked when the component unmounts (e.g., page navigation).
        // Ensures the camera stream is released and all plugin resources are freed.
        return () => {
            if (pluginRef.current) {
                pluginRef.current.destroy();
            }
        };
    }, []);

    return <></>;
}
```

***

### Step 5: Response Handling

When the plugin completes an ID card scan, it invokes the **`handleCapture`** callback with a payload object. This object may contain one or both of the following properties:

| Property | Type     | Description                                                     |
| -------- | -------- | --------------------------------------------------------------- |
| `front`  | `string` | Base64 Data URL of the front side of the ID card                |
| `back`   | `string` | Base64 Data URL of the back side of the ID card (if applicable) |

**What is Base64?** Base64 is a binary-to-text encoding scheme that converts raw binary image data into a sequence of printable ASCII characters. Each scanned card image is delivered as a Data URL string (e.g., `data:image/jpeg;base64,/9j/...`), combining a MIME type prefix with the encoded image payload. Before transmitting to a server, this string must be decoded back into binary form (a `Blob`) to construct a valid multipart HTTP request.

The following demonstrates the **base64-to-Blob conversion** and API submission:

```tsx
// Utility: converts a base64 Data URL string into a binary Blob.
// Multipart form uploads require raw binary data rather than text-encoded base64.
const base64ToBlob = (base64DataURL: string) => {
    // Separate the MIME type header from the encoded payload at the comma boundary.
    // meta    = "data:image/jpeg;base64"
    // content = "/9j/4AAQSkZJRgAB..."
    const [meta, content] = base64DataURL.split(",");

    // Parse the MIME type from the header (e.g., "image/jpeg").
    const mimeMatch = meta.match(/:(.*?);/);
    const mime = mimeMatch ? mimeMatch[1] : "image/jpeg";

    // Decode the base64 payload into raw ASCII binary characters using atob().
    const binary = atob(content);

    // Allocate a typed byte array of the same length as the decoded binary content.
    const array = new Uint8Array(binary.length);

    // Reconstruct the original binary bytes by mapping each character to its code point.
    for (let i = 0; i < binary.length; i++) {
        array[i] = binary.charCodeAt(i);
    }

    // Wrap the binary array in a Blob with the correct content type for server handling.
    return new Blob([array], { type: mime });
};

// Sends a card image Blob to the server-side verification endpoint via multipart POST.
const sendToAPI = async (blob: Blob, isface: string, card_code: string, filename: string) => {
    const formData = new FormData();
    formData.append("scan_image", blob, filename); // The binary image file
    formData.append("isface", isface);             // "front" or "back" — card side identifier
    formData.append("country_code", "UGA");        // ISO country code of the scanned ID
    formData.append("card_code", card_code);       // Card template identifier
    formData.append("passport", "false");          // Set "true" if the document is a passport
    formData.append("webcam", "false");            // Set "true" if the image was captured via webcam

    try {
        const response = await fetch("http://ip:port/doc_liveness.php", {
            method: "POST",
            body: formData,
        });

        const data = await response.json();
        console.log(`API Response (${isface}):`, data);

        // Inspect the document authenticity/liveness score from the response.
        if (data && data.score !== undefined) {
            console.log(`Score (${isface}): ${data.score}`);
        }
    } catch (error) {
        console.error(`Error sending ${isface} to API:`, error);
    }
};

// Primary capture callback — invoked automatically when the plugin completes a scan.
// Receives: base64 — payload object with front and/or back card image Data URLs.
const handleCapture = async (base64: any) => {
    console.log("Capture result:", base64);

    // Convert and dispatch the front card image if present in the payload.
    if (base64.front) {
        const frontBlob = base64ToBlob(base64.front);
        await sendToAPI(frontBlob, "front", "UGNIDF", "front.jpg");
    }

    // Convert and dispatch the back card image if present in the payload.
    if (base64.back) {
        const backBlob = base64ToBlob(base64.back);
        await sendToAPI(backBlob, "back", "UGNIDB", "back.jpg");
    }
};
```

***

### Step 6: Demo Implementation

The following is the **complete, production-ready component**. Copy and paste it directly into `components/IDScanner.tsx`. The original logic is preserved exactly as-is.

```tsx
"use client";

import { useEffect, useRef, useState } from "react";

export default function IDScanner() {
    const pluginRef = useRef(null);
    const [ready, setReady] = useState(false);
    const initialized = useRef(false);

    const base64ToBlob = (base64DataURL: string) => {
        const [meta, content] = base64DataURL.split(",");
        const mimeMatch = meta.match(/:(.*?);/);
        const mime = mimeMatch ? mimeMatch[1] : "image/jpeg";
        const binary = atob(content);
        const array = new Uint8Array(binary.length);
        for (let i = 0; i < binary.length; i++) {
            array[i] = binary.charCodeAt(i);
        }
        return new Blob([array], { type: mime });
    };

    const sendToAPI = async (blob: Blob, isface: string, card_code: string, filename: string) => {
        const formData = new FormData();
        formData.append("scan_image", blob, filename); //Upload your image file
        formData.append("isface", isface); //put card side either the card image is front or back
        formData.append("country_code", "UGA"); //put country_code of the card image
        formData.append("card_code", card_code); //put card_code of the card image
        formData.append("passport", "false"); //if image is a passport put true else put false
        formData.append("webcam", "false"); //if image is captured from a webcam put true else if image is captured from a mobile put  false

        try {
            const response = await fetch("http://ip:port/doc_liveness.php", {
                method: "POST",
                body: formData,
            });

            const data = await response.json();
            console.log(`API Response (${isface}):`, data);

            if (data && data.score !== undefined) {
                console.log(`Score (${isface}): ${data.score}`);
            }
        } catch (error) {
            console.error(`Error sending ${isface} to API:`, error);
        }
    };


    const handleCapture = async (base64: any) => {
        console.log("Capture result:", base64);

        if (base64.front) {
            const frontBlob = base64ToBlob(base64.front);
            await sendToAPI(frontBlob, "front", "UGNIDF", "front.jpg");
        }
        if (base64.back) {
            const backBlob = base64ToBlob(base64.back);
            await sendToAPI(backBlob, "back", "UGNIDB", "back.jpg");
        }
    };

    useEffect(() => {
        if (initialized.current) return;
        initialized.current = true;

        import("accuraidscanplugin").then((Module) => {
            const IDCardPlugin = Module.default;

            pluginRef.current = new IDCardPlugin(handleCapture, {
                countryCode: "UGA",
                cardCode: "UGNIDF",
                topTextSize: "",
                topTextColor: "",
                topTextWeight: "",
                bottomTextSize: "",
                bottomTextColor: "",
                bottomTextWeight: "",
            });

            pluginRef.current.start().then(() => {
                console.log("ID Scanner Ready");
                setReady(true);
            });
        }).catch(err => console.error("Plugin failed to load:", err));

        return () => {
            if (pluginRef.current) {
                pluginRef.current.destroy();
            }
        };
    }, []);

    return (
        <></>
    );
}
```

***

### Step 7: Usage

Import and render the component in `app/page.tsx`:

```tsx
import IDScanner from "./components/IDScanner";

export default function Home() {
  return (
    <main>
      <IDScanner />
    </main>
  );
}
```

***

### Step 8: Running the Project

```bash
npm run dev
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.accurascan.com/language/web-plugin/id-plugin/nextjs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
