概述
客户端提交基于三项保障:- 防伪造的主体绑定。 令牌在生成时即限定于一个
vendor_data(您的用户 ID)。使用该令牌提交的每笔交易,其主体身份都由服务器端根据此绑定强制执行。因此,被篡改的客户端无法代表其他用户提交交易,而且 payload 中无需包含任何凭证。 - 自动设备信息。 SDK 会采集设备指纹并随提交一同发送,Didit 则在服务器端推导出地址和用户代理。组合后的设备上下文会附加到交易的主体上,无需额外的库、单独的设备令牌,也无需您自行实现。它可用于基于设备的规则,以及跨会话和交易的设备关联分析。
- 自动启动的钱包所有权证明。 当已创建的交易要求终端用户证明其控制某个钱包时,响应会包含类型为
wallet_ownership的action_required块,SDK 会立即打开该流程,并在完成后刷新交易。您无需自行构建轮询循环或链接分发。必需的verification_session操作则不同:它永远不会被自动启动,而是在同一个块中返回,由您的应用使用自有的 Didit 验证集成来启动。
当没有终端用户参与时,服务器端的
POST /v3/transactions/ 仍是正确的选择,例如后台转账、批量导入,或由您自有系统上报的事件。
两种路径会创建相同的交易记录,运行相同的规则,并发出相同的 Webhook。流程
1
生成交易令牌
您的后端调用
POST /v3/transactions/sdk-token/,并提供您的 API 密钥和用户的 vendor_data,然后将返回的 sdk_token 交给您的应用。2
从设备提交
应用在 SDK 上调用
submitTransaction,该调用会携带 X-Transaction-Token 请求头,并自动补充设备信息,将 payload 发送到 POST /v1/transactions/。3
处理任何必需的操作
如果
action_required.type 为 wallet_ownership,SDK 会自动启动组件,然后通过 GET /v1/transactions/{transaction_id}/ 刷新交易。
如果为 verification_session,SDK 不会启动任何流程。请读取 action_required,并使用其 session_token(在 Web 端则为 url)启动您自有的验证流程。生成交易令牌
请在您的后端使用POST /v3/transactions/sdk-token/ 生成令牌。认证方式与其他 /v3/ 交易接口相同,均使用 API 密钥。
vendorData、ttlSeconds 和 maxUses 这几个驼峰命名别名同样可用,与 /v3/ API 的其他接口一致。
从设备提交
SDK 会将请求发送到POST /v1/transactions/,并在 X-Transaction-Token 请求头中携带 token。
传输载荷采用 camelCase 格式。submitTransaction 方法会为您构建该载荷,因此只有在您使用自己的 HTTP 客户端直接调用该接口时,才需要自行处理。
POST /v3/transactions/ 中的等效字段:
响应与
/v3/ 接口返回的 snake_case 交易详情相同,并额外包含 action_required 块:
声明自托管钱包
上面的示例将目标声明为用户自己的自托管钱包:交易对手的paymentMethod.type 为 unhosted_wallet。
此声明会改变 Travel Rule 信息交换 的运行方式:
- Didit 会完全跳过交易对手 VASP 路由,因为自托管钱包背后不存在 VASP,并直接以
UNCONFIRMED_OWNERSHIP状态创建转账。 - 当您的 Travel Rule 设置 中的
auto_wallet_verification开启时(默认开启),系统会自动生成 钱包所有权组件 的会话,并如上方响应所示,在action_required中返回。 - SDK 会自动拉起该组件。用户证明对钱包的控制权后,转账会执行姓名匹配,并推进至
COMPLETED;若姓名与钱包地址簿持有人不一致,则推进至COUNTERPARTY_MISMATCHED_DATA。
主体约束
以下三项始终由 token 决定,而非由载荷决定:- 创建交易所属的应用。
- 主体的身份:载荷中的
subject.externalUserId会被忽略,并替换为 token 的vendor_data。 - 主体的设备上下文(见下文);服务端推导的字段在与
subject.device中提供的内容冲突时以服务端为准。
401 表示 token 被拒绝:缺失、未知、已撤销、超出其 max_uses,或已过期。
过期 token 的响应错误详情中始终包含 expired 一词,SDK 正是据此区分 expired_token 与 invalid_token 错误。
读取交易详情
GET /v1/transactions/{transaction_id}/ 使用相同的 X-Transaction-Token 认证方式,返回相同的详情。
SDK 会在已拉起的操作流程完成或其窗口关闭后自动调用该接口,因此最终状态不依赖于单个事件或通知。
只要 token 仍然有效,您随时都可以自行调用此接口,以重新检查 status 和 action_required;在您的后端,GET /v3/transactions/{transaction_id}/ 会返回相同的记录。
自动设备情报
每次提交都会携带设备情报,您无需进行任何额外集成:- SDK 会收集一份注重隐私的设备指纹(
didit-fp-v2结构:平台与 navigator 信号、持久设备 ID、机器人分数以及复合哈希),并随请求一同发送。 - Didit 在服务端根据连接本身推导 IP 地址、用户代理和 accept-language,因此无法通过载荷伪造这些信息。
/v3/ 交易仍可以为每个参与方传入显式的 device_context 对象;在 SDK 路径下,您无需额外操作,即可获得同类数据的更强版本。
用户所需操作
有些交易需要终端用户先完成某些操作才能结算。规则可能要求进行验证会话,或者 Travel Rule 转账可能需要用户证明目标钱包确实属于其本人。 这两种情况都会在同一位置显示:提交响应中的action_required 块(以及之后对该交易的每次读取):
如果两种情况可能同时适用于同一笔交易,则以
wallet_ownership 为准,因为缺少所有权证明会阻止 Travel Rule 信息交换本身。
待处理操作解决后,action_required 会变为 null。
现有的 remediation 字段为向后兼容而保留;action_required 是标准的数据块。
自动启动约定
autoLaunchAction(默认为 true)仅自动启动 wallet_ownership 操作:
- Web:在 SDK 自带的浮层弹窗中打开,通过经过来源校验的消息传递,并结合有上限的交易轮询来检测完成状态(绝不依赖单一事件)。
- iOS / Android(以及委托给原生 SDK 的 React Native 和 Flutter 封装):当
useNativeWalletOwnership为true(默认值)时,以 SDK 的原生钱包所有权体验(SwiftUI / Jetpack Compose)呈现;如果无法使用原生钱包所有权体验,则回退到SFSafariViewController/ Chrome Custom Tab,最后使用默认浏览器。流程关闭或应用回到前台后,SDK 会自动重新读取交易。
verification_session 操作从不自动启动:它始终在 action_required 中返回,由您的应用使用自己的 Didit 验证集成来发起。
SDK 自动启动的钱包所有权流程完成或关闭后,会重新读取交易,并将刷新后的结果传递给您的 onActionCompleted 回调。
verification_session 操作不属于这一循环,因为它由您的应用发起,详见下一节。
启动验证会话
当action_required.type 为 verification_session 时,从 result.actionRequired 读取该块,并将其交给您现有的 Didit 验证集成:
GET /v1/transactions/{transaction_id}/(或等待交易 Webhook)以获取该交易的最新状态。
示例:为高额提现进行生物识别升级验证
这是该流程最典型的应用场景:对风险较高的交易(大额交易、新收款人、新设备)设置门槛,要求与您在入驻时验证过的同一个人再次进行生物识别复核。 账户接管看起来正是这样发生的:密码正确,2FA 验证码正确(但已被钓鱼获取或 SIM 卡被调换),并向一个您从未见过的钱包提现。 到这一步,所有基于凭证的防御都已失效。 而人脸识别则不然:攻击者无法通过活体检测自拍,因为自拍必须与合法所有者在 KYC 入驻时采集的人脸一致。1
在控制台中创建一次规则
Transactions → Rules → Create Rule:条件为
amount gte 5000,操作为 Change status → AWAITING_USER,并在该操作上选择您的 生物识别认证 工作流。
您还可以添加其他风险条件,例如首次交易对手、新设备指纹、IP 国家变更,可作为额外条件或额外规则添加。2
照常提交提现请求
提交代码无需任何改动。规则触发后,响应会返回
status: "AWAITING_USER",以及类型为 verification_session 的 action_required 块:3
启动复核
将
session_token(或 url)交给您现有的 Didit 验证集成,操作方式与启动验证会话中所述一致。
由于该会话是为与交易主体相同的 vendor_data 创建的,Didit 已保存该用户入驻时验证过的参考人脸:即已通过的活体自拍、护照芯片照片或证件人像。
用户只需拍摄一张实时自拍;Didit 会执行活体检测,并与存储的参考人脸进行 1:1 人脸比对。无需重新扫描证件,也无需重新入驻,只会在真正需要的交易上带来几秒钟的额外操作。4
根据结果完成结算
复核会话完成后,交易会自动重新评估:复核通过则为
APPROVED,失败则为 DECLINED,无论哪种结果都会发送 transaction.status.updated Webhook(交易时间线中的 reason: remediation_completed)。
您的应用可通过类似 onActionCompleted 的回调、GET /v1/transactions/{transaction_id}/ 或 Webhook 获取结果。Web SDK
submitTransaction 目前已在 Web SDK 单例上可用:
{ transactionId, status, travelRuleStatus?, actionRequired? }。
在 SDK 层的 payload 中,您需要填写 category、details 和 customProperties,SDK 会自动将它们映射为传输层别名(type、info、props)。
错误类型通过 DiditTransactionError 表示:
原生 SDK
原生
submitTransaction API 随 iOS 和 Android SDK 4.1.0 发布(以及锁定该版本的 React Native 和 Flutter 版本)。
与 Web SDK 不同,原生 payload 直接使用传输层字段名:type、info 和 props。getTransaction(transactionToken, transactionId) 方法,用同一个 token 重新读取交易。启动操作流程后,SDK 会自动为您调用该方法,您也可以随时自行调用。
在移动端,设备指纹传输已应用于所有 SDK 网络调用,因此交易提交会自动携带相同的设备信息。
不使用 SDK
直接使用 HTTPS 即可完成 SDK 的全部功能,无需依赖 SDK。 提供两种等效的集成方式: 仅服务端(无需任何 token)。 使用您的 API 密钥,从后端调用POST /v3/transactions/ 提交交易。响应以及之后每次 GET /v3/transactions/{transaction_id}/ 的返回都会包含相同的 action_required 块。由于没有 SDK 为您收集设备信息,您还可以为每个参与方显式传入 device 上下文。
客户端使用自有 HTTP 栈。 先通过 POST /v3/transactions/sdk-token/ 生成 token,然后在设备端使用 X-Transaction-Token 请求头调用 POST /v1/transactions/ 和 GET /v1/transactions/{transaction_id}/。主体绑定以及服务端的 IP 与 User-Agent 推导同样适用。
然后,请自行处理 action_required:
无需任何客户端回调即可检测完成状态:
- Webhook(推荐)。 待处理操作使交易得出结果时,会触发
transaction.status.updated;每次 Travel Rule 交换状态发生变化时,会触发travel_rule.status.updated。两者都包含足够的信息,可在无需轮询的情况下更新您的记录。 - 轮询。 重新读取交易(后端使用
/v3/,或在 token 有效期内使用/v1/配合 token)。操作完成后,action_required会恢复为null,status和travel_rule.status则会给出结果。
Webhook
您的后端无需依赖应用来回传状态。 提交的交易需要用户操作时,交易 webhook(transaction.created 或 transaction.status.updated)会包含相同的 action_required 块。因此,即使用户在流程完成前关闭了应用,您的系统也能获知待处理的操作。
verification_session 操作会使交易状态变为 AWAITING_USER,但 wallet_ownership 操作本身不会改变交易状态,因此请读取 action_required,而不要只看状态。
Travel Rule 状态变化还会触发 travel_rule.status.updated。
后续步骤
提交交易
服务端 Create Transaction API 及完整 payload 参考。
Travel Rule
托管式汇款人与收款人交换、设置及生命周期。
钱包所有权组件
wallet_ownership 操作背后的托管式控制权证明流程。
Webhook
transaction.created、transaction.status.updated 以及签名验证。
规则
规则操作,包括用于创建验证会话的操作。
状态
AWAITING_USER 在交易生命周期中的含义。