---
title: Web App 集成
description: 如何在客户端集成 Web & SDK 合约——重定向流与 iFrame SDK 配置。
canonical: https://developer.unico.io/zh-CN/human-token/integrations/web/web-integration/
locale: zh-CN
generated_by: markdown-export
---

本页介绍 Unico 旅程的工作方式以及将其嵌入应用程序的可用集成模型。

**旅程**是用户完成身份验证所经历的一系列步骤。例如：拍摄证件照片并进行面部捕获（活体检测）。

Unico 管理整个体验。集成工作量极小：通过 **CreateProcess** 创建旅程，将用户引导至该旅程，
并在结束时接收结果。期间的一切——页面、指示、验证——均已由 Unico 构建和维护。

## 两种集成模型

每个客户有不同的需求。Unico 提供**两种模型**将用户引导至旅程。

| 模型 | 最适合 |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| **直接访问** | 已使用 WebView 的移动应用程序，或旅程可以在主页面之外进行的 Web 流程 |
| **Journeys SDK** | 需要集成且无缝体验的 Web 应用程序，保持用户在同一环境中 |

### 直接访问

用户被**重定向到 Unico 托管的链接**，在那里进行旅程。完成后，用户被返回到在流程创建期间
定义的 URL（`callbackUri` 参数）。

这是最简单的采用方式：不需要安装任何库，在旅程不需要在应用程序自身页面内进行时效果很好。
另一方面，将用户带离客户环境往往会引入更多摩擦，从而导致更高的流失率。

创建流程后，API 响应包含 Unico 托管旅程的 URL。有两种常见方式将用户引导至该 URL：

- **标准重定向。** 用户直接重定向到旅程 URL。完成后，Unico 将用户重定向回流程创建期间
  定义的 `callbackUri`。
- **使用 `window.open()` 打开新标签页。** 旅程在新的浏览器标签页中打开，使用户保持在单独的
  上下文中。在这种情况下，建议监控 URL 变更至 `callbackUri` 并在流程完成后关闭标签页。
  有关 API 的详细信息，请参阅
  [MDN 文档](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)。

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant Frontend as Customer frontend application
    participant Hosted as Unico hosted frontend application

    Frontend->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>Frontend: return Unico process info
    Frontend->>Hosted: redirect user to process url
    Note over Hosted: User journey
    Hosted->>Frontend: redirect user to callback url

    critical Get process result
        Frontend->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

在移动应用程序中，通常使用 **WebView** 直接打开旅程，无需额外的重定向。在这种情况下，
`callbackUri` 也接受 **deeplink**，允许旅程完成时触发打开原生应用程序中的特定页面。
只需将 deeplink 配置为返回目的地，操作系统会负责将用户路由到正确的位置。

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant App as Customer mobile application
    participant Hosted as Unico hosted frontend application

    App->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>App: return Unico process info
    App->>Hosted: open the process url in a webview
    Note over Hosted: User journey
    Hosted->>App: redirect user to the deeplink (callback url)

    critical Get process result
        App->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

### Journeys SDK

旅程在**应用程序内部**进行，无需将用户带离其上下文。**Journeys SDK** 安装在应用程序中，
并在需要时用于打开旅程。

这是获得更集成和无缝体验的推荐方式，在整个过程中保持用户在同一环境中，这往往会减少流程中的
摩擦和流失。

Unico 提供了一个与现代浏览器兼容的 JavaScript 库，只需几行代码即可将旅程集成到几乎任何
应用程序中。

#### 兼容性

该库设计为无障碍地适应任何项目，无论使用何种技术栈：

- **任何 Web 应用程序。** 以 UMD 格式分发，通过现代打包工具（如 webpack 或 Vite）导入时即可工作。兼容
  任何框架（React、Angular、Vue）或纯 JavaScript。
- **现代浏览器。** 该库已包含 Promises 和 `async/await` 等功能所需的 polyfill，同时也扩展了
  与旧版浏览器的兼容性。
- **标准 Web API。** 旅程运行在浏览器的原生能力上，不依赖项目中的插件或外部库。

#### SDK 的内部工作原理

当旅程被打开时，SDK 会在页面中插入一个 iFrame，并从该点起控制整个视觉体验。每个步骤的
页面、脚本和资源都在此 iFrame 内运行，从用户开始到流程完成。

这一架构决策是有意为之：iFrame 隔离确保 Unico 旅程不会干扰应用程序的样式或行为。没有脚本
泄漏到外部上下文，没有 CSS 规则与应用程序自身的样式冲突。结果是为最终用户提供一致的体验，
对客户产品的影响最小。

由于 Unico 负责创建和管理 iFrame，旅程的改进——无论是性能、体验还是验证方面——都会自动
交付给所有用户，无需在集成的应用程序中进行任何更改。集成将始终以最佳可用优化运行，
无需跟踪或响应每次平台更新。

#### 开始使用

****步骤 1** — 安装**

`idpay-b2b-sdk` 包在 IDPay 支付旅程和身份验证旅程之间共享。对于身份验证用例，请按照
以下步骤导入 `ByUnicoSDK` 类。

```bash
  npm install idpay-b2b-sdk
```

安装 Journeys SDK 的推荐方式是通过 **npm** 或 **yarn** 等包管理器，从 **npm registry**
上的可用包安装。除了简化安装和依赖管理外，这种方式还提供了对所用版本的清晰控制，并在
新版本发布时轻松更新。

SDK 遵循**语义化版本控制（SemVer）**，这意味着补丁和次要更新不会引入破坏性变更。可以安全地
配置项目以自动接收这些更新。可能需要集成更新的更改保留给主要版本，并始终附带迁移指南。

:::tip[保持 SDK 更新]
保持最新版本尤为重要，原因有二。第一是**安全性**：每当发现漏洞或出现加强通信协议的
机会时，都会发布安全补丁。运行过时版本意味着错过这些修复，将流程暴露于不必要的风险中。
第二是**稳定性**：错误修复以相同方式分发，较旧版本可能表现出在较新版本中已解决的行为。
:::

开始之前，请向 Unico 支持团队注册您的域名。所有域名必须使用 HTTPS。

****步骤 2** — 配置 CSP 和 Permissions-Policy**

如果您的页面已发送 `Content-Security-Policy` 或 `Permissions-Policy` 头部，请添加以下指令，以便 SDK 的 iFrame、脚本和相机访问能正确工作。

**Content-Security-Policy:**

```
frame-src https://*.unico.io https://*.unico.app;
script-src https://idpay.unico.io;
style-src https://idpay.unico.io;
connect-src https://api.cadastro.unico.app;
```

**Permissions-Policy:**

```
camera=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io");
geolocation=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io");
```

:::info
如果您的页面不发送 CSP 或 Permissions-Policy 头部，则可以跳过此步骤——浏览器默认不应用任何限制。
:::

****步骤 3** — 调用 `init(options)`**

初始化 SDK 并预加载旅程正常运行所需的脚本，为最终用户创建更流畅的体验。在流程中尽早调用此方法。

| 参数 | 必需 | 描述 |
| --------- | -------- | ------------------------------------------------ |
| `token` | 是 | Create Process API 返回的流程令牌 |
| `env` | 否 | 仅在测试环境中设置为 `'uat'` |

```javascript
import { ByUnicoSDK } from 'idpay-b2b-sdk';

ByUnicoSDK.init({
  token,
  // env: 'uat' // only for test environments
});
```

****步骤 4** — 调用 `open(options)`**

显示 iFrame 并为用户启动旅程。从此刻起，一切都在 iFrame 内自动进行，无需管理任何中间步骤。

| 参数 | 必需 | 描述 |
| -------------------------- | -------- | ---------------------------------------------------------- |
| `transactionId` | 是 | Create Process API 返回的流程 ID |
| `token` | 是 | Create Process API 返回的流程令牌 |
| `onFinish` | 是 | 旅程结束或关闭时执行的回调 |
| `onWidgetVisibilityChange` | 否 | 小部件可见性状态变化时执行的回调 |

与应用程序的下一次交互发生在旅程结束时，无论用户是完成了还是关闭了旅程。此时，SDK 调用
在 `open` 中作为参数传递的 **`onFinish`** 回调。从那里，应用程序可以调用 **`getProcess`** API
来检查结果，或者如果首选异步方式，则等待 **Webhook** 通知。

除了查询结果外，建议使用 `onFinish` 来处理应用程序的前端状态：

- **避免循环。** 防止在旅程结束后用户立即再次触发流程时，不必要地重新创建流程。
- **流程管理。** 确保用户被引导到应用程序的下一步，避免在旅程关闭后出现死胡同页面。

:::warning
`onFinish` 回调表示用户已完成旅程，但不保证获得批准。流程可能因 Unico 验证规则之一的
失败而结束。通过 `getProcess` 查询或接收 Webhook 通知不是可选的：这些是实际结果的唯一
来源，应用程序的行为必须基于它们。不得单独使用 `onFinish` 来确定用户是否获得批准。
:::

`onFinish` 回调接收一个描述旅程如何结束的对象：

| 字段 | 类型 | 描述 |
| ------------------------- | ------------------- | ----------------------------------------------------------------------------------------- |
| `type` | string | 旅程如何结束：`'FINISH'`（已完成）或 `'CLOSE'`（用户在完成前关闭） |
| `transaction` | object \| undefined | 当 `type` 为 `'FINISH'` 时存在；当 `type` 为 `'CLOSE'` 时为 `undefined` |
| `transaction.id` | string | 流程标识符（与传入的 `transactionId` 相同） |
| `transaction.redirectUrl` | string | 旅程后重定向用户的 URL |

处理 **`onWidgetVisibilityChange`** 回调是可选的，可能与您的用例无关。它在小部件的可见性
状态每次变化时被调用，仅在特定场景中有用：某些旅程显示透明背景，使应用程序页面在体验后面
可见。在验证流程期间显示自定义模态框的应用程序（例如，作为跨多个 KYC 提供商编排的一部分）
可能会在 Unico 小部件后面显示该模态框，从而降低视觉体验。在这种情况下，回调允许应用程序
在 Unico 旅程活跃期间抑制任何额外的视觉元素，并在结束后恢复它们。如果您的应用程序没有
可能与小部件重叠的 UI，则可以安全地省略它。

```javascript
ByUnicoSDK.open({
  transactionId: '9bc22bac-1e64-49a5-94d6-9e4f8ec9a1bf',
  token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
  onFinish: ({ transaction, type }) => {
    if (type === 'FINISH') {
      // Journey completed (transaction = { id, redirectUrl }) — continue your flow here.
    }
    // type === 'CLOSE' → user closed before finishing;
  },
  // Optional: only needed if your app shows UI that could overlap the widget.
  onWidgetVisibilityChange: (visible) => {
    // suppress or restore your modal based on widget visibility
  },
});

// To close the SDK explicitly at any point:
ByUnicoSDK.close();
```

下面的时序图演示了如何使用 SDK 和 API 结果来配置 iFrame：

```mermaid
sequenceDiagram
    participant Unico as Unico backend service
    participant Backend as Customer backend server
    participant Frontend as Customer frontend application
    participant SDK as ByUnicoSDK

    Frontend->>Backend: start kyc flow
    Backend->>Unico: call createProcess
    Unico->>Backend: return Unico process info
    Backend->>Frontend: return Unico process info
    Note over Frontend: Initialize the Unico journey as soon as it is determined that the KYC flow is required

    critical Unico journey initialization
        Frontend->>SDK: byUnicoSDK.init
        SDK-->>Frontend: validates customer domain and initialize
    end

    critical Unico journey exhibition
        Frontend->>SDK: byUnicoSDK.open
        SDK-->>Frontend: show byUnico experience
    end
    Note over SDK: User journey

    critical Unico journey completion
        SDK-->>Frontend: call onFinish
    end

    critical Get process result
        Frontend->>Backend: signals that journey was finished
        Backend->>Unico: call getProcess
        opt Webhook
            Note over Backend: In addition to getProcess, you can use the Webhook implementation to ensure a fallback in obtaining the process result.
        end
    end
```

#### 示例应用

| 语言 / 框架 | 描述 | 仓库 |
| --------------------- | ------------ | ---------- |
| Angular | 实现 Journeys SDK 的 Angular PoC | [GitHub — unico-cbu-poc-angular](https://github.com/unico-labs/unico-cbu-poc-angular) |
| JS Vanilla | 实现 Journeys SDK 的 JS Vanilla PoC | [GitHub — unico-cbu-poc-js](https://github.com/unico-labs/unico-cbu-poc-js) |
| React | 实现 Journeys SDK 的 React PoC | [GitHub — unico-cbu-poc-react](https://github.com/unico-labs/unico-cbu-poc-react) |
| Vue JS | 实现 Journeys SDK 的 Vue JS PoC | [GitHub — unico-cbu-poc-vuejs](https://github.com/unico-labs/unico-cbu-poc-vuejs) |

#### 安全性

该模型中的安全性是分层构建的，从 SDK 与在 iFrame 内运行的应用程序之间的通信协议开始。

当旅程加载时，双方执行**握手**以建立通信。在此过程中，Unico 应用程序验证通过 `postMessage`
接收的数据注入消息的来源，对照按环境（UAT 和 PROD）分类的授权域名封闭列表。来自未批准
来源的消息会立即被丢弃，防止旅程被嵌入未授权页面，消除了 **clickjacking** 等漏洞的攻击面。

除了来源验证外，流程仅在有效的交易令牌下继续：由 Unico 后端签发和签名的一次性 JWT。
这确保即使是授权来源也无法使用过期、重用或伪造的令牌进行操作。

握手后，令牌被注入 iFrame，双方之间不再传输敏感信息。所有剩余通信仅用于界面控制——
打开、关闭和屏幕转换——防止流程数据在旅程期间被拦截或泄漏。

iFrame 隔离还在运行时保护 Unico 脚本的完整性。由于代码在与页面分离的上下文中运行，
外部脚本无法访问或修改它，确保旅程完全按照构建的方式执行，不受干扰。

##### CSP 和 Permissions-Policy

如果您的页面发送 `Content-Security-Policy` 或 `Permissions-Policy` 头部，请确保以下指令存在，以便 SDK 的 iFrame、脚本和相机访问能正确工作。

**Content-Security-Policy:**

```
frame-src https://*.unico.io https://*.unico.app;
script-src https://idpay.unico.io;
style-src https://idpay.unico.io;
connect-src https://api.cadastro.unico.app;
```

**Permissions-Policy:**

```
camera=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io");
geolocation=(self "https://human-token.unico.io" "https://cadastro.unico.app" "https://id.unico.io" "https://retry.unico.io" "https://ultimatum.unico.io");
```

如果您的页面不发送这些头部，浏览器默认不应用任何限制，无需任何配置。

#### SDK 特定故障排除

本节涵盖集成过程中遇到的最常见问题及推荐的排查方法。

##### 意外行为或流程中断

检查是否有任何应用程序脚本在 DOM 中直接操作 iFrame。SDK 在页面 `body` 中创建和管理
iFrame，任何外部修改——无论是对作用域、定位还是属性——都可能干扰旅程的生命周期并导致
不可预测的行为。

##### 视觉体验与预期不同

检查应用程序中是否有任何全局样式表覆盖了 iFrame 内的属性。SDK 使用动态 ID 和以 `unico`
为前缀的类名创建 iFrame 及其所有内部元素，这显著降低了通过 ID 或类选择器产生冲突的风险。
但是，广泛的 CSS 规则——例如标签选择器——可以到达 iFrame 内的元素并改变提供给用户的视觉体验。

##### SDK 库文件被直接修改

检查是否有任何库文件在包管理器之外被修改。库必须专门通过 **npm** 或 **yarn** 管理，
不得直接编辑已安装的文件。手动修改可能产生难以重现的异常行为，并将阻止 Unico 支持团队
提供帮助。

#### 在捕获测试期间不要保持 DevTools 打开

Unico 应用程序使用 Capture SDK（unico-webframe）进行面部捕获，该 SDK 会将打开的 DevTools 检测为潜在欺诈信号并阻止提交。在运行端到端捕获测试之前，请关闭 DevTools。

:::warning[不支持的集成]
本文档中描述的模型——直接访问和 Journeys SDK——是 Unico 官方支持的唯一集成方式。偏离这些
标准的集成可能导致意外行为、安全流程失败和旅程中断，且不会得到 Unico 支持团队的覆盖。

不支持方式的示例：

- 在移动应用程序中**将 SDK 嵌入 WebView 内**。在这些情况下，正确的方式是使用**直接访问**
  模型，在 WebView 中直接打开旅程链接，而不涉及 Journeys SDK。
- **通过 `<iframe>` HTML 标签直接加载 iFrame**，不通过 Journeys SDK。iFrame 是 SDK 的
  内部实现细节，不得手动实例化。正确的方式是使用 **Journeys SDK**，它以安全的方式在
  预期标准内管理 iFrame 生命周期。

如果对某种方式是否符合支持标准有任何疑问，请在继续实施之前查阅文档或联系支持团队。
:::