# zkKYC - Know Your Customer

{% hint style="success" %}
This page covers the **compliance-related** features of [zkKYC](https://docs.zk.me/hub/what/zkkyc). If you want to integrate MeID, please jump to [meid](https://docs.zk.me/hub/start/onboarding/integration/js-sdk/zkkyc/meid "mention")for the MeID-specific guide.
{% endhint %}

## Use Case

To reduce the development cost for the project side, the project can use zkKYC capability by simply accessing the link. Users can complete full KYC verification directly on the web/H5, reducing user churn by minimizing the need to navigate to another page.

***

## **zkMe-Widget KYC Process**

**Step 1:** Enter the service authorization Widget page; the user confirms and goes to the next step

**Step 2:** E-mail verification login

**Step 3:** Verify the SBT to confirm that it is authenticated

**Step 4:** Depending on the KYC configuration of the project, determine whether the user needs to undergo different verification processes.

***

## Interaction Instructions

<figure><img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/1SWp7mXLu54Prujjnn1s/20230360.png" alt=""><figcaption></figcaption></figure>

***

## Integration via NPM

You can refer to [@zkmelabs/widget](https://www.npmjs.com/package/@zkmelabs/widget?activeTab=versions) and please make sure to use the latest version.

### Installation

```sh
pnpm add @zkmelabs/widget

# or
yarn add @zkmelabs/widget

# or
npm install @zkmelabs/widget
```

### Getting Started

#### **Step 1. Import styles**

```typescript
import '@zkmelabs/widget/dist/style.css'
```

#### **Step 2. Create a new `ZkMeWidget` instance**

{% tabs %}
{% tab title="Cross-chain" %}
{% code fullWidth="false" expandable="true" %}

```typescript
import { ZkMeWidget, type Provider } from '@zkmelabs/widget'

const provider: Provider = {
  async getAccessToken() {
    // -------------------------TODO-------------------------
    // Request a new token from your backend service and return it to the widget.
    // For the access token, see https://docs.zk.me/hub/start/onboarding/integration/js-sdk/zkkyc#access-token
    // ------------------------------------------------------
    return fetchNewToken()
  },

  async getUserAccounts() {
    // -------------------------TODO-------------------------
    // If your project is a Dapp,
    // you need to return the user's connected wallet address.
    const userConnectedAddress = await connect()
    return [userConnectedAddress ]

    // If not,
    // you should return the user's e-mail address, phone number or any other unique identifier.
    //
    // return ['email address']
    // or
    // return ['phone number']
    // or
    // return ['unique identifier']
    // ------------------------------------------------------
  },

}

const zkMeWidget = new ZkMeWidget(
  // -------------------------TODO-------------------------
  appId, // This parameter means the same thing as "mchNo"
  'YourDappName',
  '137', // chainId. No changes are needed here if the account is configured for cross-chain.
  provider,
  {
      lv: 'zkKYC'
      programNo: 'YourProgramNo' // You can find the Program No in the ‘Configuration’ section of your dashboard
      // For other options, please refer to the table below
  }
  // ------------------------------------------------------
)
```

{% endcode %}
{% endtab %}

{% tab title="On-chain Mint / On-chain Transactional " %}
{% code expandable="true" %}

```typescript
import { ZkMeWidget, type Provider } from '@zkmelabs/widget'

const provider: Provider = {
  async getAccessToken() {
    // -------------------------TODO-------------------------
    // Request a new token from your backend service and return it to the widget.
    // For the access token, see docs.zk.me/zkme-dochub/verify-with-zkme-protocol/integration-guide/javascript-sdk/zkkyc-compliance-suite#how-to-generate-an-access-token-with-api_key
    // ------------------------------------------------------
    return fetchNewToken()
  },

  async getUserAccounts() {
    // -------------------------TODO-------------------------
    // If your project is a Dapp,
    // you need to return the user's connected wallet address.
    const userConnectedAddress = await connect()
    return [userConnectedAddress]

    // If not,
    // you should return the user's e-mail address, phone number or any other unique identifier.
    //
    // return ['email address']
    // or
    // return ['phone number']
    // or
    // return ['unique identifier']
    // ------------------------------------------------------
  },

  // -------------------------TODO-------------------------
  // According to which blockchain your project is integrated with,
  // choose and implement the corresponding methods as shown below.

  // EVM
  async delegateTransaction(tx) {
    const txResponse = await signer.sendTransaction(tx)
    return txResponse.hash
  },

  // Cosmos
  async delegateCosmosTransaction(tx) {
    const txResponse = await signingCosmWasmClient.execute(
      tx.senderAddress,
      tx.contractAddress,
      tx.msg,
      'auto'
    )
    return txResponse.transactionHash
  },

  // Aptos
  async delegateAptosTransaction(tx) {
    const txResponse = await aptos.signAndSubmitTransaction(tx)
    return txResponse.hash
  },

  // TON
  async delegateTonTransaction(tx) {
    const { boc } = await tonConnectUI.sendTransaction({
      validUntil: Date.now() + 5 * 60 * 1000, // You can customize this value
      messages: [tx]
    })
    const { hash } = Cell.fromBase64(boc)
    return hash().toString('hex')
  },
  
  // Solana
  async delegateSolanaTransaction({ message }) {
    const tx = Transaction.populate(Message.from(bs58.decode(message))) 
    // Replace with your actual signer
    const txid = await signer.signAndSendTransaction(tx)
    return txid
  },

  // ...
  // See the Provider interface definition for more details on other chains.
  // ------------------------------------------------------
}


const zkMeWidget = new ZkMeWidget(
  // -------------------------TODO-------------------------
  appId, // This parameter means the same thing as "mchNo"
  'YourDappName',
  chainId, // 
  provider,
  {
      lv: 'zkKYC'
      programNo: 'YourProgramNo' // You can find the Program No in the ‘Configuration’ section of your dashboard
      // For other options, please refer to the table below
  }
  // ------------------------------------------------------
)
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**NOTE:** The specific configuration for the "option" parameter is shown in the table below
{% endhint %}

<table><thead><tr><th width="195">Param</th><th width="164">Type</th><th>Description</th></tr></thead><tbody><tr><td>options.lv</td><td>VerificationLevel?</td><td><code>"zkKYC"</code> or <code>"MeID"</code>, default <code>"zkKYC"</code></td></tr><tr><td>options.programNo</td><td>string?</td><td><p>If you have activated multiple programs running in parallel, please pay attention to this setting:<br></p><p>The param can be found in <a href="https://dashboard.zk.me">Dashboard</a> and please make sure the program is enabled. The SDK will take the number of the first activated program as the default value if this parameter is not provided in the code.</p></td></tr><tr><td>options.theme</td><td>Theme?</td><td><code>"auto"</code>, <code>"light"</code> or <code>"dark"</code>, default <code>"auto"</code>. </td></tr><tr><td>options.locale</td><td>Language?</td><td><code>"en"</code> or <code>"zh-hk"</code>, default <code>"en"</code>.</td></tr></tbody></table>

#### **Step 3. Listen to the `kycFinished` widget events to detect when the user has completed the zkKYC process.**

```typescript
function handleFinished(results) {
  const { isGrant, associatedAccount } = results

  if (
    isGrant &&
    associatedAccount === userConnectedAddress.toLowerCase()
  ) {
    // -------------------------TODO-------------------------
    // Prompts the user that zkKYC verification has been completed
    // ------------------------------------------------------
  }
}

zkMeWidget.on('kycFinished', handleFinished)
```

#### **Step 4. Launch the zkMe widget and it will be displayed in the center of your webpage.**

```typescript
// This is the code to launch our widget on your page
button.addEventListener('click', () => {
  zkMeWidget.launch()
})
```

***

## Helper functions

### verifyKycWithZkMeServices()

Before launching the widget, you should check the zkKYC status of the user and launch the widget when the check result is `false`.

```typescript
import { verifyKycWithZkMeServices } from '@zkmelabs/widget'

// zkKYC
const { isGrant } = await verifyKycWithZkMeServices(
  appId,
  userAccount,
  // Optional configurations are detailed in the table below
  options
)
```

<table><thead><tr><th width="192">Param</th><th width="89">Type</th><th>Description</th></tr></thead><tbody><tr><td>appId</td><td>string</td><td>This parameter means the same thing as "mchNo"</td></tr><tr><td>userAccount</td><td>string</td><td>The <code>userAccount</code> info (such as wallet address, email, phone number, or unique identifier) must match the format of accounts returned by <code>provider.getUserAccounts</code>.</td></tr><tr><td>options.programNo</td><td>string?</td><td>If you have activated multiple programs running in parallel, please pay attention to this setting:<br><br>The param can be found in <a href="https://dashboard.zk.me">Dashboard</a> and please make sure the program is enabled. The SDK will take the number of the first activated program as the default value if this parameter is not provided in the code.</td></tr></tbody></table>

If the level of your Dashboard account is not Cross-Chain, then you can also query users' zkKYC status from zkMe Verify & Certify Smart Contract [here](https://github.com/zkMeLabs/zkme-sdk-js/tree/main/packages/verify-abi#readme).

## **How to Generate an Access Token with API\_KEY**

To use your API\_KEY to obtain an accessToken, you will need to make a specific HTTP request. Here's how you can do it:

#### a. **Endpoint**: Send a <mark style="color:orange;">`POST`</mark> request to the token exchange endpoint.

```powershell
POST https://nest-api.zk.me/api/token/get
```

{% hint style="info" %}
Please remember to modify the <mark style="color:blue;">`Content-Type`</mark> in the request header to <mark style="color:blue;">`application/json`</mark>. Failing to do so might result in a <mark style="color:red;">`Parameter Error`</mark> response.
{% endhint %}

#### b. **Request Body**:

<table><thead><tr><th width="193">Parameter Name</th><th width="136">Required</th><th width="150">Type</th><th>Desc</th></tr></thead><tbody><tr><td>apiKey</td><td>True</td><td>string</td><td>The API_KEY provided by zkMe.</td></tr><tr><td>appId</td><td>True</td><td>string</td><td>A unique identifier (mchNo) to DApp provided by zkMe.</td></tr><tr><td>apiModePermission</td><td>True</td><td>number</td><td>0 - email login (Only support email login)</td></tr><tr><td>lv</td><td>True</td><td>number</td><td>1 - zkKYC <br>2 - MeID</td></tr></tbody></table>

{% hint style="info" %} <mark style="color:orange;">`API_KEY`</mark>can be found in [the Configuration section](https://dashboard.zk.me/integration) of the Integration on the zkMe Dashboard.
{% endhint %}

#### c. **Response**

<details>

<summary><mark style="color:green;"><strong>Success</strong></mark></summary>

<pre class="language-json"><code class="lang-json">{
    "code": 80000000,
    "data": {
        "accessToken": "8641259808779c53de65c3698e42b402b112cfe3856202189c37eae9f0b23babbcc1429ea9adcb52283dca4dab024a640651f855d8c78c7bde308f721a6e0cb80d51dab7c775ebfe0ae74eb9ab02f503094a9b2a2e2aeabf70e03a0cac9773a93dba743ca0dc3fa4af77375351bc48f76515d72dbee3a8bd5c034e6ffb94bd97"
<strong>    },
</strong>    "msg": "success",
    "timestamp": 1691732474552
}
</code></pre>

</details>

<details>

<summary><mark style="color:red;"><strong>Exception (AppId and API_KEY not matched)</strong></mark></summary>

```json
{
    "code": 81000014,
    "data": null,
    "msg": "AppID and API Key do not match. Access token generation failed",
    "timestamp": 1691732568774
}
```

</details>

<details>

<summary><mark style="color:red;"><strong>Exception (Parameter Error)</strong></mark></summary>

```json
{
    "code": 80000002,
    "data": null,
    "msg": "parameter error",
    "timestamp": 1691732593484
}
```

</details>

<details>

<summary><mark style="color:red;"><strong>Exception (System Error)</strong></mark></summary>

```json
{
    "code": 80000001,
    "data": null,
    "msg": "system error",
    "timestamp": 1691732593484
}
```

</details>

***

## ZkMeWidget instance methods

<details>

<summary>launch()</summary>

Launch the zkMe widget and it will be displayed in the center of your webpage.

<pre class="language-typescript"><code class="lang-typescript"><strong>launch(): void
</strong></code></pre>

</details>

<details>

<summary>on()</summary>

Listen to zkMe widget events.

```typescript
on(event: 'kycFinished', callback: KycFinishedHook): void;
on(event: 'close', callback: () => void): void;
```

</details>

<details>

<summary>switchChain()</summary>

If your DApp integrates multiple chains, use this method to synchronize the new chain to the zkMe widget when the user switches chains in your DApp.

```typescript
switchChain(chainId: string): void
```

</details>

<details>

<summary>hide()</summary>

Hide the zkMe widget.

```typescript
hide(): void
```

</details>

<details>

<summary>destroy()</summary>

Remove the message event listener registered by the zkMe widget from the window and destroy the DOM node.

```typescript
destroy(): void
```

</details>

***

## Common Response & Exceptions

<details>

<summary><mark style="color:green;">Success</mark></summary>

If the user has passed the KYC verification and the user’s SBT could be accessed by your project, the following interface will be seen. Meanwhile, there will be a message with KYC results sent to your DApp.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/VjVsxfk0yDe7SsUsWF4P/Group_48097419.png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Camera Permission Denied Error</mark></summary>

The following screen will be displayed for possible issues such as the user denying browser camera access or not having a camera on the device.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/F6Rx4GT2578yDUkclYFn/Frame_48096348.png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">OCR Scan Error</mark></summary>

The following screen will be displayed when an exception occurs during the OCR process.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/fNn9xWnHgaXoerSS4yCo/Frame_48096348_(5).png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Face Recognition Error</mark></summary>

The following screen will be displayed for possible problems such as eyes closed detected, art mask detected etc.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/uzczOa12jUIaTAAFJG0F/Frame_48096348_(1).png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Face Mismatch Error</mark></summary>

The following screen will be displayed when the face could not match the uploaded ID.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/LltMrG9Ar6LIMw18DClr/Frame_48096348_(6).png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Faceprint Mismatch Error</mark></summary>

The following screen will be displayed for the possible problem that the fully homomorphically encrypted faceprint does not match the one associated with this MeID.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/nyh9239IA7GMa1saQShP/Frame_48096348_(7).png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Faceprint Recognition Server Error</mark></summary>

The following screen will be displayed when something goes wrong on the faceprint recognition server.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/npdN7YY6B340bXzYCG5Z/Frame_48096348_(4).png" alt="" data-size="original">

</details>

<details>

<summary><mark style="color:red;">Unknown Error</mark></summary>

The following screen will be displayed when something goes wrong not listed above.

<img src="https://content.gitbook.com/content/3QFkeleLOltSYnf8uj0t/blobs/C26CmsRAHaW7eLReqKj6/1014.png" alt="" data-size="original">

</details>
