关于 sumsub-integrate-id-connect
用于集成 Sumsub ID Connect 的开发技能,面向需要在应用中请求并接收已验证身份信息的团队。它强调先确认 Sumsub 端配置并通过预检,再编写前后端 OIDC 流程。
主要功能
- 01核对集成前置条件
- 02预检工作区与验证等级
- 03引导 OIDC 授权与 PKCE
- 04编排令牌交换和身份复用
适用场景
- 01在应用中加入 Sumsub ID 验证入口
- 02验证 OIDC 客户端与回调地址配置
- 03接收授权结果并交换令牌
- 04将 Sumsub ID 身份复用为申请人
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sumsub/ agent-skills --skill sumsub-integrate-id-connect安装后在 Claude Code 中输入/sumsub-integrate-id-connect确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/express-callback.jsoidc-button.htmloidc-modal.htmloidc-redirect.htmlreferences/scopes-and-claims.mdscripts/preflight.sh详细介绍
Sumsub — ID Connect 集成
端到端嵌入“通过 Sumsub ID 验证”OIDC 流程 — 前提条件 → 预检 → 按钮 → 后端交换 → 同意 → 工作区中的申请人。
⚠️ 仅限沙盒令牌
接入时不要使用生产环境的 App Token / 密钥。务必从
https://cockpit.sumsub.com/checkus/home?sbx=true 获取一对沙盒令牌 —
Connect Sumsub to
your AI agent -> Build & configure -> Generate token。令牌和密钥只会在创建时显示一次;关闭对话框前请复制二者。预检会通过 sbx: 前缀检查强制执行此要求。
更深入的身份验证机制:sumsub-api-auth。
⛔ 操作顺序 — 不要先搭建应用
这是本技能中最重要的一条规则。在下面两个关卡都通过之前,不要编写任何前端或后端代码,不要创建演示项目,也不要搭建任何内容。客户端的 Sumsub 设置(阶段 0a)必须手动完成,你无法代劳 — 在它尚未完成时就开始写代码,只会得到一个无法验证身份、无法重定向且无法复用的应用。务必遵循以下顺序:
- 逐项向用户确认阶段 0a 的前提条件。 不要假设任何项目已经完成。逐项询问用户是否已完成;如果没有,停止并告知他们具体的手动操作步骤和操作位置(支持邮件模板、Dev Space → OIDC Settings 注册、App Token 创建页面、级别创建交接)。这些操作只能在 Sumsub 控制面板或通过支持团队完成 — 没有对应的公共 API,因此你无法代用户完成。等待所有项目都得到确认后再继续。
- 运行阶段 0b 预检,并要求退出码为 0。 只有确认前提条件后,才能运行
scripts/preflight.sh。解决所有 FAIL(必要时返回阶段 0a)。只要有任何 FAIL,就不要继续。 - 现在才能开始构建。 两个关卡都通过后,继续阶段 1,搭建前端和后端。
如果用户在关卡通过前说“直接搭建演示”,请简要说明为什么现在还无法运行,并先带他们完成缺失的前提条件。代码是最后一步,不是第一步。
什么是 ID Connect(以及它不是什么)
Sumsub ID Connect 允许企业从最终用户的 Sumsub ID 账户请求特定的已验证身份声明 — 年龄、姓名、国籍、电子邮件、居住国家/地区、真人身份。Sumsub 托管整个面向用户的流程:登录、创建账户(如有需要)、电子邮件 OTP、任何尚未完成的验证步骤,以及针对每位接收者的同意页面。你的应用会收到签名的 OIDC 令牌作为结果;此外,还可以通过 Reusable KYC 在工作区中生成申请人。
这不是 WebSDK。WebSDK 会在你的页面内运行完整的 KYC 信息采集流程;ID Connect 则将整个用户体验交由 Sumsub 处理。
生命周期一览
┌──────────────────────────────┐
│ Stage 0a Preconditions check │ manual setup on Sumsub side
│ Stage 0b scripts/preflight.sh │ automated validation
└──────────────┬───────────────┘
│ clientId, levelName, allowlist
┌──────────────▼───────────────┐
│ Stage 1: Frontend "Verify…" │ authorize redirect with PKCE
│ button │
└──────────────┬───────────────┘
│ onSuccess({ code, state })
┌──────────────▼───────────────┐
│ Stage 2: code → access_token │ POST id.sumsub.com/api/snsId/oauth/token
│ (client_secret_basic) │
└──────────────┬───────────────┘
│ access_token
┌──────────────▼───────────────┐
│ Stage 3a: → share token │ POST /resources/accessTokens/sumsubIdShareToken
└──────────────┬───────────────┘
│ sharingAllowed?
┌───────┴────────┐
│ false │
▼ ▼ true
┌──────────────────┐ ┌──────────────────────────────┐
│ Stage 3b: │ │ Stage 4: reuse/preview + │
│ shareLink │ │ reuse/commit │
│ redirect + retry │ │ → applicant in your workspace │
└────────┬─────────┘ └──────────────────────────────┘
│
└──────► back to Stage 3a (retry — now sharingAllowed: true)
端点
| METHOD | URL | Auth | Stage |
|---|---|---|---|
| (浏览器重定向) | https://id.sumsub.com/api/snsId/oauth/authorize | 无 | 1 |
| GET | https://id.sumsub.com/.well-known/openid-configuration | 无 | 0b |
| GET | https://id.sumsub.com/.well-known/jwks.json | 无 | (id_token 验证) |
| POST | https://id.sumsub.com/api/snsId/oauth/token | client_secret_basic | 2 |
| POST | https://api.sumsub.com/resources/accessTokens/sumsubIdShareToken | App Token + HMAC | 3a |
| POST | https://api.sumsub.com/resources/snsId/oauth/shareLink | App Token + HMAC + Bearer | 3b |
| GET | https://api.sumsub.com/resources/api/reusableIdentity/reuse/preview | App Token + HMAC | 4 |
| POST | https://api.sumsub.com/resources/api/reusableIdentity/reuse | App Token + HMAC | 4 |
“Enable Sumsub ID Connect”开关、OIDC 客户端注册(client_secret + redirect_uri 允许列表)以及共享合作伙伴注册,只能通过 Sumsub 控制面板 UI 完成 — 没有对应的公共 API。
阶段 0a — 前置条件检查清单
开始编写代码或执行预检前,Sumsub 端必须已具备以下条件。每一项都对应下方的一项检查,若未满足则会导致运行时失败。
-
Sumsub 支持团队已为你的工作区启用 ID Connect。 这不能自行在界面中切换——支持团队启用之前,无法创建 OIDC 客户端,预检会报告
connect_token: ID Connect not enabled for this workspace。请先联系你的客户成功经理(CSM)或[email protected],并等待确认。模板:``` Subject: 为 clientId <YOUR_CLIENT_ID> 启用 Sumsub ID Connect(sandbox) Hi Sumsub team, Please enable Sumsub ID Connect for our workspace. Environment: sandbox clientId: <YOUR_CLIENT_ID> (Dashboard top-left) Intended scopes: openid, share, name (adjust as needed) Intended recipients: self (or list partner clientIds) Use case: <one-line description of where the button will live> Once enabled we'll register the OIDC client + redirect URIs in Dev Space → OIDC Settings. Thanks ``` -
App Token 和 secret(sandbox)。 通过 https://cockpit.sumsub.com/checkus/home?sbx=true,依次选择 Connect Sumsub to your AI agent -> Build & configure -> Generate token 生成。创建时这两个值只显示一次。→ 导出为
SUMSUB_APP_TOKEN/SUMSUB_SECRET_KEY。 -
已在 Dashboard → Dev Space → OIDC Settings 注册 OIDC 客户端。 记录以下三项: -
client_id(公开信息——会放在浏览器中;在常见的接收方为自己的场景下,作为共享令牌的forClientId) -client_secret(创建时只显示一次;如果丢失,必须重新生成,这会使现有集成失效)。Stage 2 的 code→token 交换需要此项——只能保存在服务端。 - 已填写两个回调地址的redirect_uri允许列表(Sumsub 会逐字节匹配——尾部斜杠、大小写、端口和查询参数都会影响匹配): - 你的 authorize 回调(例如https://yourapp.com/auth/callback) - 你的 shareLink 回调(例如https://yourapp.com/share-complete) Sumsub 会将浏览器重定向回这些 URL,并要求使用公开的 HTTPS 地址——单独的http://localhost:3000无法用于本地测试。若要在自己的机器上开发,请使用公开的 HTTPS 隧道(ngrok)暴露本地服务,并将该地址加入允许列表——参见下文的使用 ngrok 进行本地开发。 -
此工作区中至少存在一个验证级别。 记录
levelName。如果没有,请先移交给sumsub-create-level——没有验证级别,Stage 4 就无法创建申请人。 -
至少有一个包含已验证数据的 Sumsub ID 账户用于端到端测试。仅有电子邮件的账户(未完成 KYC)会导致 Stage 4 失败,并返回 errorCode
10521 reusable-kyc-inactive-sumsub-id-account——参见 Stage 4 末尾的错误表。
阶段 0b — 预检
scripts/preflight.sh 会通过机器验证可在不生成 OIDC 代码或创建申请人的情况下探测的前置条件。它不会修改任何内容。
SUMSUB_APP_TOKEN=sbx:... SUMSUB_SECRET_KEY=... \
bash scripts/preflight.sh
检查项(彼此独立——一项失败不会中止其他检查):
| 检查项 | 验证内容 |
|---|---|
connect_token | App-Token HMAC 签名已被接受,且此工作区已启用 ID Connect——通过已知错误代码探测端点,并预期返回 401 Invalid code(若返回 404 invalid clientId,则表示未启用 ID Connect)。这是连通性/权限探测,不是阶段 2 的交换路由。 |
oidc_discovery | https://id.sumsub.com/.well-known/openid-configuration 返回有效的 OIDC 发现文档 |
levels | 此工作区中至少存在一个验证级别 |
全部 PASS 或 PASS+WARN 时退出码为 0,任何 FAIL 时退出码为 10。开始阶段 1 前,请解决所有 FAIL。
使用 ngrok 进行本地开发
ID Connect 无法针对 http://localhost 进行测试。Sumsub 会在授权流程(阶段 1)和 shareLink 同意流程(阶段 3b)之后,将浏览器重定向回你的 redirect_uri,并要求该 URL 必须是公开的 HTTPS URL,且与 OIDC 客户端 redirect_uri 允许列表中的条目逐字节完全一致。裸 localhost URL 既不公开也不是 HTTPS,因此重定向会失败。进行本地开发时,请使用 ngrok 在本地服务器前建立一个公开的 HTTPS 隧道,并将该来源添加到允许列表。
-
在本地启动后端(示例监听
PORT=3000):PORT=3000 node examples/express-callback.js -
建立到该端口的隧道。 建议使用保留(静态)域名,这样 URL 在重启后仍然有效——免费的随机 ngrok URL 每次启动都会变化,因此每次都得重新编辑允许列表:
ngrok http 3000 --domain=your-reserved-name.ngrok-free.app # or, ephemeral URL (changes each restart): ngrok http 3000ngrok 会打印一个转发 URL,例如
https://your-reserved-name.ngrok-free.app。 -
在 ngrok 来源处将两个回调都添加到允许列表:进入 Dashboard → Dev Space → OIDC Settings(沙盒工作区)——逐字节保持一致,包括末尾的斜杠:
https://your-reserved-name.ngrok-free.app/ https://your-reserved-name.ngrok-free.app/share-complete -
让应用使用公开 URL,而不是 localhost。 浏览器的
redirect_uri和服务端的 shareLinkredirectUri必须基于 同一个公开来源,因此请设置PUBLIC_BASE_URL并重启:PUBLIC_BASE_URL=https://your-reserved-name.ngrok-free.app \ PORT=3000 node examples/express-callback.js然后在浏览器中打开 ngrok URL(而不是
localhost:3000),这样它发送的redirect_uri才会与允许列表匹配。
⚠️ 每次 URL 变更都要重新注册。 如果使用临时 ngrok URL,每次 ngrok 重启时都必须更新允许列表条目和
PUBLIC_BASE_URL。使用保留域名可避免这种反复操作。ngrok 的免费插页(“You are about to visit…”)只影响 API/XHR 调用,不影响顶层浏览器导航,因此不会阻止 OIDC 重定向。使用保留域名或付费套餐可完全移除此插页。
阶段 1 — 前端:按钮
⛔ 关卡。 在阶段 0a 的前置条件全部经用户确认,且阶段 0b 预检以 0 退出之前,不要开始此阶段。如果这两项都尚未完成,请返回——参见上文“操作顺序”。这是第一个需要编写代码的阶段;在此之前,你必须先验证所有设置。
架构 — 哪些由你负责,哪些由 Sumsub 负责
Sumsub 提供:OIDC 服务(id.sumsub.com)、HMAC API(api.sumsub.com),以及可选的 @sumsub/id-connect JS 库,用于挂载按钮。
集成的前后两部分都由你编写:
- 前端(本阶段)— 带按钮的页面。生成 PKCE,启动到
id.sumsub.com的 OIDC 重定向,在回调中接收?code=,并向你自己的后端 POST{ code, codeVerifier }。 - 后端(阶段 2-4)— 前端向其发送 POST 请求的 HTTP 端点。使用你的 App Token + 密钥为 Sumsub API 调用签名(这些凭据绝不能传到浏览器),协调
code → access_token → share token → reuse流程,并返回已物化的申请人信息。
前后两部分通过一个路由名称关联 — 示例中为 POST /api/sumsub/id-connect/exchange;请根据你的路由配置进行重命名。匹配的参考实现:
examples/oidc-button.html(前端)+
examples/express-callback.js(后端)。
后端是一个精简的流程协调器(约 180 行 Node/Express);可移植到你选择的技术栈 — 无论使用何种语言,线协议格式都相同。
⚠️
onSuccess不是验证信号。 它仅表示用户已完成 OIDC 授权步骤,且 Sumsub 已签发授权码。 实际验证结果来自阶段 4 —/reuse响应中的applicant.review.reviewStatus(如果你的接收方等级不运行额外检查,则会立即返回),或applicantReviewedwebhook(对于会在复用后运行检查的任何等级,该 webhook 都具有权威性)。 绝不要根据onSuccess触发来授予访问权限或解锁功能。
提供三种方案 — 默认使用 方法 1(createButton);其他方案适用于设计系统集成或无需打包器的场景。
方法 1 — 通过 @sumsub/id-connect 使用预构建按钮(推荐)
import { createButton } from '@sumsub/id-connect';
// PKCE helpers (S256 — full versions in examples/oidc-button.html).
const b64url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const sha256 = (s) => crypto.subtle.digest('SHA-256', new TextEncoder().encode(s));
// Generate the PKCE pair yourself, store the verifier under YOUR key — Stage 2
// on the backend needs that verifier to exchange the code for an access token.
const verifier = b64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64url(await sha256(verifier));
sessionStorage.setItem('myapp.pkce.verifier', verifier);
createButton({
clientId: '<your-client-id>',
permissions: ['openid', 'share', 'name'],
container: document.getElementById('button-container'),
codeChallenge: challenge, // library uses YOUR challenge — skips its own PKCE gen
loginHint: '[email protected]', // optional, prefills the email field
onSuccess: async ({ code, state }) => {
const v = sessionStorage.getItem('myapp.pkce.verifier');
await fetch('/api/sumsub/id-connect/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, codeVerifier: v, redirectUri: location.href }),
});
},
onError: (err) => console.error('sumsub-id-connect error', err),
});
为何自行生成 PKCE:阶段 2 的令牌交换需要 verifier。如果让库生成,你就需要依赖库内部的 sessionStorage 键(每个次要版本都未记录其文档),才能在 onSuccess 中读回它。传入自己的 codeChallenge,就能将 verifier 存储在由你掌控的键下。方法 2 和 3 也可使用相同模式。
可运行示例:examples/oidc-button.html。
方法 2 — 使用自己的触发元素 + 库模态框
使用同一个库,但由你掌控触发元素;库通过 openModal({...}) 在叠加层中打开流程。如果你的设计系统已有自己的按钮,请使用此方案。
可运行示例:examples/oidc-modal.html。
方法 3 — 原始 OIDC 重定向(不使用库)
直接导航到 OIDC 授权端点,页面中不加载 Sumsub 脚本。当 CSP 禁止第三方脚本、你所处环境仅支持 SSR,或你需要跨多个提供方维护通用 OIDC 客户端时,可使用此方案。
GET https://id.sumsub.com/api/snsId/oauth/authorize
?client_id=<your-client-id>
&response_type=code
&redirect_uri=<allowlisted URI>
&scope=openid+share+name
&state=<random CSRF nonce>
&code_challenge=<base64url(SHA256(code_verifier))>
&code_challenge_method=S256
&login_hint=<optional email>
你需要生成 code_verifier(43–128 个字符,URL 安全),推导出 code_challenge = base64url(SHA256(verifier)),存储 verifier(sessionStorage),并在回调后将其转发给后端。Sumsub 只接受 code_challenge_method=S256(不接受 plain),且 redirect_uri 必须与允许列表逐字节完全一致。
可运行示例:examples/oidc-redirect.html。
选择作用域
默认值为 ['openid', 'share', 'name']。仅当下游逻辑确实会读取 email、profile 或 offline_access(刷新令牌)时才添加 — 额外作用域会在面向用户的流程中触发额外检查。完整表格:
references/scopes-and-claims.md。
阶段 2 — 后端:代码 → access_token
在 OIDC 令牌端点上交换授权码。该代码来自浏览器授权流程,因此对应的是标准 OIDC 令牌端点——使用 OIDC client_secret(client_secret_basic)进行身份验证,而不是 HMAC。
POST https://id.sumsub.com/api/snsId/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(<client_id>:<client_secret>)
grant_type=authorization_code
&code=<the code your frontend got>
&redirect_uri=<exact-match URI from Stage 1 — byte-for-byte>
&code_verifier=<PKCE verifier matched to the code_challenge from Stage 1>
响应(有效期为 24 小时的 access_token):
{
"access_token": "snd-id-con-a-...",
"refresh_token": "snd-id-con-r-...", // only with `offline_access`
"token_type": "Bearer",
"expires_in": 86400,
"id_token": "eyJhbGc..." // only with `openid` — RS256-signed
}
之后通过 grant_type: "refresh_token" 刷新;刷新时请求的 scope 不能超过用户同意时授予的范围。
client_secret必须保留在服务器端——绝不能放进浏览器资源包中。交换方式是client_secret_basic/client_secret_post;不要使用 HMAC 签名。(Stage 3a/4 中仍会使用 App-Token + HMAC 身份验证来处理 share token 和 reuse 调用。)
注意事项
Invalid code401 有三个常见原因:PKCE 不匹配(verifier 的哈希值与授权时发送的 challenge 不一致)、代码已使用(代码只能使用一次;刷新回调页面会导致重复触发),或代码已过期(有效期很短——请立即交换)。redirect_uri必须与 Stage 1 逐字节完全一致——尾随斜杠、大小写、端口和查询参数都必须匹配,否则交换会失败。id_token验证——使用成熟的库(jose、pyjwt、jjwt),根据.well-known/openid-configuration中的jwks_uri进行验证。绝不要信任未经验证的 JWT 内容。
阶段 3 — 后端:access_token → share token + 同意
创建一个限定接收方(forClientId)的 share token。务必实现同意回退流程(Stage 3b)——只要用户尚未授权与该特定 forClientId 共享,就会返回 sharingAllowed: false。在常见的自有接收方场景中,通常会在 Stage 1 的同意页面上一次点击授予这两项同意;但如果生产代码只处理 sharingAllowed: true,那么用户重新进入流程或接收方配置发生变化时,流程首次就会失败。
阶段 3a — 创建 share token
POST https://api.sumsub.com/resources/accessTokens/sumsubIdShareToken
{
"sumsubIdConnectToken": "<access_token from Stage 2>",
"forClientId": "<recipient clientId>",
"ttlInSecs": 1800
}
forClientId:
- 自有接收方(常见情况):你自己的工作区
clientId,可在 Dashboard 左上角查看。 - 第三方接收方:合作伙伴的
clientId(必须预先在 Dashboard 中注册为合作伙伴)。
响应:
{
"token": "_act-snsId-...",
"forClientId": "<echoed>",
"sharingAllowed": true // or false → continue to Stage 3b
}
阶段 3b — 合作伙伴同意 shareLink
如果 sharingAllowed: false,创建同意链接并重定向用户:
POST https://api.sumsub.com/resources/snsId/oauth/shareLink
Authorization: Bearer <access_token from Stage 2>
X-App-Token + X-App-Access-Ts + X-App-Access-Sig
{
"redirectUri": "<your shareLink callback URL — must be in the allowlist>",
"forClientId": "<same recipient clientId>",
"displayMode": "page",
"state": "<csrf-or-correlation-id>"
}
响应:{ "link": "https://id.sumsub.com/share/<id>" }——5 分钟后过期。
在服务器端按 state 保存 access_token(演示项目可以使用内存 Map;生产环境请使用 Redis / 加密 Cookie / 会话表)。将用户重定向到 link。用户同意后,Sumsub 会将用户重定向回 redirectUri,并附带 ?token=<JWT>——这是一个经过 RS256 签名的 JWT,其载荷中包含 state(以及 sharingAllowed: true 和 forClientId)。
⚠️
state位于 JWT 载荷中,而不是顶层的?state=查询参数中。 解码tokenJWT 以读取state(首先根据jwks_uri验证其签名),然后通过该state查找已保存的access_token。再次调用 Stage 3a——此时 share token 会返回sharingAllowed: true。解码后的载荷结构:{ iss, sub, aud, iat, exp, sharingAllowed, state, forClientId }。
完整的参考实现:
examples/express-callback.js(使用以 state 为键的内存 pendingShare Map,以及 /share-complete 处理器)。
阶段 4 — 创建申请人
获得可用的 share token 后,在你的工作区中创建申请人。务必先预览。
预览(无副作用)
GET https://api.sumsub.com/resources/api/reusableIdentity/reuse/preview
?shareToken=<token>
&levelName=<your-level>
&userId=<externalUserId>
返回与 /reuse 将创建的相同 ApplicantPublicDto 结构——可用于在提交前显示兼容性错误。
提交(创建申请人)
POST https://api.sumsub.com/resources/api/reusableIdentity/reuse
?shareToken=<token>
&levelName=<your-level>
&userId=<externalUserId>
userId 是 Sumsub 存储在申请人记录中的 externalUserId——每个真实用户对应一个稳定的标识,对用户不透明,并与您的身份验证系统关联。规则与 WebSDK 相同。
Reuse 错误代码
所有 400 响应均包含 errorCode(整数)和 errorName(kebab-case),
适合用于代码分支。401 表示通用的令牌有效性问题——不包含 errorCode,
只有 description。
| HTTP / errorCode | name | 原因 | 操作 |
|---|---|---|---|
| 401 / — | Token is invalid | 共享令牌已过期(超出 TTL)、已使用或格式错误 | 通过第 3a 阶段重新生成 — 使用仍有效的 access_token 再次调用 /sumsubIdShareToken |
400 / 10501 | not-in-partners-list | forClientId 未注册为工作区的共享合作伙伴 | 在 Dashboard 中将接收方添加为合作伙伴(仅限 Sumsub UI — 无公开 API) |
400 / 10502 | invalid-partner-id | 从共享令牌中提取的合作伙伴 ID 无效 | 重新检查 forClientId;针对真实合作伙伴重新生成共享令牌 |
400 / 10503 | invalid-share-token | 共享令牌格式错误或已过期 | 通过第 3a 阶段重新生成 |
400 / 10504 | non-suitable-share-token | 共享令牌不适用于在指定 levelName 下重复使用(范围不匹配) | 使用匹配的范围重新生成共享令牌,然后针对同一等级重试 |
400 / 10505 | reusable-kyc-disabled | 此工作区已禁用 Reusable KYC | 联系 Sumsub 支持,为工作区启用 Reusable KYC |
400 / 10506 | reusable-kyc-not-reusable | 通用回退 — 数据提供方不符合重复使用资格,且未匹配到更具体的原因 | 在 Dashboard 中检查数据提供方;选择其他数据提供方,或回退到 WebSDK |
400 / 10507 | reusable-kyc-not-approved-applicant | 数据提供方申请人尚未处于已批准状态(待处理/已拒绝/暂停处理) | 等待数据提供方的 KYC 获批;使用状态为已批准的数据提供方 |
400 / 10508 | reusable-kyc-not-active-applicant | 数据提供方申请人处于非活动状态(已停用/已封锁) | 使用其他数据提供方 — 重新激活需由 Sumsub 处理 |
400 / 10509 | reusable-kyc-not-kyc-moderation-type | 数据提供方的源审核类型不适合重复使用 | 数据提供方需要通过标准 KYC 审核;专用流程无法重复使用 |
400 / 10510 | reusable-kyc-not-selfie-or-identity-doc | 数据提供方缺少必需的自拍照或身份证明文件 | 选择不需要该文件集的等级,或让数据提供方重新验证 |
400 / 10511 | reusable-kyc-not-selfie-liveness | 接收方等级要求活体检测;数据提供方的自拍照未通过活体检测拍摄 | 使用 SELFIE 上未设置 videoRequired: passiveLiveness 的等级,或让数据提供方通过活体检测重新验证 |
400 / 10512 | reusable-kyc-not-req-doc-overlap | 数据提供方与接收方等级要求的文件类型没有交集 | 选择所需文件属于数据提供方文件子集的接收方等级 |
400 / 10513 | reusable-kyc-not-compatible-doc-type | 接收方等级要求的数据提供方未提供的文件类型 | 使用要求较低的等级,或回退到 WebSDK 以完整采集资料 |
400 / 10514 | reusable-kyc-not-actual-poi-date | 数据提供方的身份证明(POI)已过期,不适合重复使用 | 数据提供方必须重新提交最新 POI,或放宽接收方等级的 POI 时效窗口 |
400 / 10515 | reusable-kyc-not-actual-poa-date | 数据提供方的地址证明(POA)已过期,不适合重复使用 | 数据提供方必须重新提交最新 POA,或放宽接收方等级的 POA 时效窗口 |
400 / 10516 | reusable-kyc-not-acceptable-age | 数据提供方不符合接收方等级的年龄标准 | 无法恢复 — 需要更换用户 |
400 / 10517 | reusable-kyc-incompatible-capture-settings | 数据提供方与接收方之间的采集设置(文件上传与实时摄像头等)不匹配 | 将接收方等级的 captureParams 调整为与数据提供方使用的设置一致,或让数据提供方重新采集 |
400 / 10518 | reusable-kyc-email-mismatch | 数据提供方的电子邮件与接收方申请人已有的电子邮件不匹配 | 解决申请人重复冲突 — 使用不同的 externalUserId,或使电子邮件保持一致 |
400 / 10519 | reusable-kyc-phone-mismatch | 数据提供方的电话号码与接收方申请人已有的电话号码不匹配 | 与 10518 相同 — 解决冲突 |
400 / 10521 | reusable-kyc-inactive-sumsub-id-account | 数据提供方的 Sumsub ID 账户没有存储的文件(仅有电子邮件,从未完成 KYC) | 数据提供方必须先在 id.sumsub.com 完成完整 KYC;测试时请使用 Sumsub 提供的已验证测试账户 |
当申请人被创建后,applicantReviewed Webhook 是验证结果的权威信号,请按 WebSDK 的相同方式接入。有关 Webhook 的资料,请参阅 | |||
sumsub-integrate-websdk;有关沙盒 Webhook 订阅设置,请参阅 | |||
sumsub-manage-webhooks。 |
上线检查清单
当用户表示“我们准备好上线了”时:
- Sumsub 支持团队已为生产环境的
clientId启用 Sumsub ID Connect(与沙盒环境分开)。 - 生产环境的
redirectUri(authorize 和 shareLink 回调)已加入生产工作区 Dashboard 的允许列表。 - 后端令牌交换端点已设为需要身份验证(只有经过身份验证的用户才能兑换为其本人签发的代码)。
- 已根据生产环境的 JWKS 验证
id_token签名。 -
externalUserId对每个真实用户都保持稳定(不是邮箱,也不是显示名称)。 - 在调用
/reuse前先调用/reuse/preview,让兼容性错误以 UX 问题的形式呈现,而不是产生 4xx 噪声。 - 已实现并测试第 3b 阶段的 shareLink 工作流——不要发布只处理
sharingAllowed: true的代码。 - shareLink 回调使用的内存状态已替换为真实的会话存储(Redis / 加密 Cookie / DB)。
- App Token、密钥和 OIDC
client_secret已存入生产环境的密钥存储,不在代码库中,也不在浏览器包中。 - 刷新令牌(如有使用)仅在服务端存储、静态加密,并采用单租户模式。
另请参阅
references/scopes-and-claims.md— 完整的 scope 表、令牌端点响应格式和错误代码。scripts/preflight.sh— 前置条件验证器。examples/oidc-button.html— 方法 1(createButton)——推荐。examples/oidc-modal.html— 方法 2(openModal)。examples/oidc-redirect.html— 方法 3(使用 PKCE 的原始 OIDC 重定向)。examples/express-callback.js— 完整后端(第 2 至 4 阶段,包括第 3b 阶段的同意流程)。sumsub-api-auth— 身份验证签名参考。sumsub-create-level— 工作区尚无接收方级别时使用。sumsub-integrate-websdk— 相关技能;申请人创建后,重用其中的 Webhook 资料。