SDK 事件与前端接入

本页面是 集成方案方案二:API + SDK v2 集成 的前端接入补充说明。

方案二告诉您如何获取 SDK URL 并让用户完成 KYC;本页告诉您在接入方前端页面上:

  • 如何嵌入 SDK URL
  • 如何监听 SDK 发出的关闭请求,正确销毁 SDK 页面
  • 联调阶段可用的 URL 参数

集成方式

POST /kyc/individual/sdk/url 获取到 SDK URL(形如 https://otc.legendtrading.com/v2/kyc/s/{code})后,推荐通过以下两种方式在您的前端页面呈现:

方式一:iframe 嵌入

在您的页面用 <iframe> 承载 SDK URL,SDK 作为您页面的一部分展示(适合放在弹窗、抽屉、侧边栏里)。

<iframe
  src="https://otc.legendtrading.com/v2/kyc/s/{code}"
  allow="camera; geolocation"
  style="width: 100%; height: 100%; border: 0;"
></iframe>

方式二:打开链接

window.open() 打开 SDK URL,把 KYC 流程独立到新窗口完成(适合弹出窗口 / 新标签页场景)。

window.open("https://otc.legendtrading.com/v2/kyc/s/{code}")
💡

SDK 内部在流程结束、或用户主动退出时,需要关闭承载它的 iframe / 弹窗。但浏览器安全策略不允许 SDK 页面直接关闭您的宿主窗口,所以 SDK 会通过消息通知您、由您来完成关闭动作。详见下文 close-requested 事件章节

URL 参数

在 SDK URL 后可以追加以下 query 参数,用于联调、布局适配、品牌定制、消息安全加固:

参数取值是否必填说明
modedev切换到联调环境。不传 = 生产环境
remotesandboxapi联调环境的后端子域,必须与 mode=dev 同时传
layoutmobile | desktopSDK 布局形态。不传或其它值 = desktop
primary-color十六进制颜色 #RGB#RRGGBB(URL 中 # 需转义为 %23,例:%23FF6600自定义 SDK 主题色(按钮、链接、进度等主色)。仅接受严格的十六进制格式,非法值会被忽略并回退到默认主题色
parentOrigin您页面的完整 origin(如 https://a.com否(强烈建议填收窄 SDK 向宿主 postMessage 时的 targetOrigin。不传时 SDK 会用 *,意味着任意 origin 都能收到消息

联调环境完整示例

https://otc.legendtrading.com/v2/kyc/s/{code}?mode=dev&remote=sandboxapi&layout=mobile&primary-color=%23FF6600&parentOrigin=https://a.com
  • mode=dev&remote=sandboxapi — 前后端都指向联调环境,不影响生产数据
  • layout=mobile — 使用移动端布局(适合嵌入手机 App WebView 或较窄的容器)
  • primary-color=%23FF6600 — 自定义 SDK 主题色为橙色 #FF6600# 必须转义为 %23
  • parentOrigin=https://a.com — 只允许 https://a.com 收到 SDK 发出的关闭请求消息

生产环境示例

生产环境不需要传 mode / remote,直接使用原始 SDK URL 即可。仍然强烈建议追加 parentOrigin 参数以提高安全性,并按需追加 layout / primary-color 完成布局与品牌色适配:

https://otc.legendtrading.com/v2/kyc/s/{code}?layout=mobile&primary-color=%23FF6600&parentOrigin=https://your-domain.com
  • layout=mobile — 使用移动端布局(不传或其它值 = 桌面布局)
  • primary-color=%23FF6600 — 自定义 SDK 主题色,与您的品牌色保持一致(# 必须转义为 %23
  • parentOrigin=https://your-domain.com — 收窄 SDK postMessagetargetOrigin

监听 close-requested 事件

SDK 页面在流程结束或用户主动退出时,需要关闭承载它的 iframe / 弹窗。由于浏览器安全策略不允许 SDK 页面直接关闭您的宿主窗口,SDK 会通过 postMessage 向您的页面发一条 close-requested 消息,请您帮忙完成关闭动作。

您在宿主页面上监听这条消息,收到后销毁承载 SDK 的容器(并可以在这里衔接您自己的后续业务逻辑)。

消息格式

SDK 向宿主页面发送的 postMessage 消息统一如下:

{
  source: "legend-kyc-relay",   // 固定值,识别消息来源
  type: "close-requested",      // 固定值,当前只有这一种消息类型
  detail: {                     // 附加信息,可能为 null
    flow: "kyc" | "additional-kyc" | null
  }
}

字段说明:

  • source — 固定 "legend-kyc-relay",用于将 SDK 消息与页面其它 postMessage 区分
  • type — 目前只有 "close-requested" 一种
  • detail.flow — 触发关闭时用户所处的 KYC 流程;kyc = 普通 KYC,additional-kyc = 续填 KYC,null = 用户还未进入 KYC 主流程(例如在初始拦截页触发关闭)

安全边界

监听器必须校验 event.origin === "https://otc.legendtrading.com",否则任意第三方页面都能通过 postMessage 伪造消息、诱发意外关闭行为。

同时建议在 SDK URL 上追加 parentOrigin 参数收窄 SDK 端的 targetOrigin,双向加固。

场景一:iframe 嵌入

假设您用 modal 承载 iframe,用户在 SDK 内触发关闭时需要销毁 iframe / 关闭 modal:

<div id="kyc-modal" class="modal hidden">
  <iframe
    id="kyc-iframe"
    src="https://otc.legendtrading.com/v2/kyc/s/{code}?parentOrigin=https://a.com"
    allow="camera; geolocation"
  ></iframe>
</div>
window.addEventListener("message", (event) => {
  // 1. 校验消息来源
  if (event.origin !== "https://otc.legendtrading.com") return

  // 2. 校验消息格式
  const { source, type, detail } = event.data || {}
  if (source !== "legend-kyc-relay") return
  if (type !== "close-requested") return

  // 3. 销毁承载 SDK 的 iframe / 关闭 modal
  document.getElementById("kyc-iframe")?.remove()
  document.getElementById("kyc-modal")?.classList.add("hidden")

  // 4. 在这里衔接您自己的业务逻辑,例如:
  //    - 调用您的接口,重新拉取用户 KYC 状态
  //    - 跳转到"KYC 已提交"提示页
  //    - 更新前端 UI(按钮 disable / 显示 loading 等)
  //    - detail.flow 可以帮您区分是普通 KYC 还是续填 KYC
})

场景二:popup 弹窗

您用 window.open() 打开 SDK。popup 端 SDK 收到关闭请求时会自己调用 window.close(),宿主端只做善后:

// 打开 SDK popup
const popup = window.open(
  "https://otc.legendtrading.com/v2/kyc/s/{code}?parentOrigin=https://a.com"
)

// 监听 popup 发来的关闭请求
window.addEventListener("message", (event) => {
  if (event.origin !== "https://otc.legendtrading.com") return
  const { source, type, detail } = event.data || {}
  if (source !== "legend-kyc-relay" || type !== "close-requested") return

  // popup 端 SDK 自己会 window.close(),宿主端在这里做善后:
  //    - 调用您的接口,重新拉取用户 KYC 状态
  //    - 更新前端 UI / 跳转页面
  //    - detail.flow 可以帮您区分是普通 KYC 还是续填 KYC

  // 兜底:如果 popup 因浏览器差异没能自关,宿主端主动关一次
  if (popup && !popup.closed) popup.close()
})

常见问题

收不到 close-requested 消息怎么排查?

  1. 确认 window.addEventListener("message", ...) 在 iframe / popup 打开之前就已挂上,避免消息比监听器早到
  2. 检查 event.origin 校验值 —— 生产、联调都是 https://otc.legendtrading.commode / remote 只切换后端 API,不影响 SDK 页面 origin)
  3. 检查 parentOrigin 参数拼接是否正确 —— 必须是完整 origin(如 https://a.com),不带路径、不带尾斜杠
  4. 打开浏览器 DevTools Console 查是否有 postMessage 相关报错

primary-color 传了没生效怎么排查?

  1. 确认 # 已在 URL 中转义为 %23 —— 直接写 # 会被浏览器当作 fragment 截断,SDK 收不到
  2. 确认取值符合 #RGB#RRGGBB 格式(3 位或 6 位十六进制)—— 其它写法(rgb()、颜色名、8 位带透明度等)都会被安全校验拦截、回退到默认色
  3. 确认参数名是 primary-color(中划线),不是 primaryColorprimary_color

SDK URL 有效期是多久?

单个 SDK URL 有效期为 24 小时。过期后需要重新调用 POST /kyc/individual/sdk/url 获取新 URL。

用户中途关闭后再次打开同一个 SDK URL,进度还在吗?

。同一个 URL 在 24 小时内可以重复打开,SDK 会自动恢复到用户上次的进度,无需您额外处理。

移动端 App 内嵌 WebView 需要注意什么?

  • WebView 需要开启 JavaScript 与 postMessage 支持(iOS WKWebView / Android WebView 默认已开启)
  • WebView 需要授予 摄像头地理位置 权限,否则 IDV / GPS PoA 环节会失败
  • 建议使用 ?layout=mobile URL 参数切换到移动端布局

如何知道用户 KYC 最终审核结果?

close-requested 只表示用户从 SDK 侧完成或退出,不代表 KYC 已经通过。KYC 审核结果需要通过 kyc-status WebhookPOST /app/kyc-status 轮询获取,详见 集成方案 → 实时获取 KYC 审核状态