XMDT — Cocos Creator
一个 npm 包 @alogame/kyc-sdk,完全用 TypeScript 实现整个 XMDT 流程 —— 用 Cocos 自己的 UI 组件绘制界面,并通过 HTTPS 直接调用 Ekycx 的四个会话接口。与 Android、iOS、Unity SDK 不同,没有任何原生代码,没有 .aar,没有 .xcframework,构建时无需嵌入任何东西 —— 整个包就是 .ts 源码,你的项目按编译自身脚本的方式编译它即可。
本包已针对 Cocos Creator 3.8 的官方 API 声明做过类型检查,其业务逻辑有 119 个自动化测试,但从未在真实的 Cocos Creator 项目、真机或编辑器中运行过。请把本页的一切内容视为"纸面正确",尚未得到确认。如果你正在评估用于真实项目集成,请先询问再决定是否依赖它。
| 环境 | Base URL |
|---|---|
| Dev | https://api-xmdt.dev.alogame.vn |
| Prod | https://api-xmdt.alogame.vn |
两者都已上线。你的 gameId 必须先由 Alogame 运营方开通 XMDT,两个环境才能使用——参见服务器集成。
服务器集成 说明了你的服务器为生成 sessionToken 所需的唯一一次调用。你的客户端从不直接访问 Alogame 的身份 API,也永远看不到你的 HMAC 密钥。
环境要求: Cocos Creator 3.8+。
为什么在 Android/iOS/Unity SDK 之外还有这个包
原生 SDK(以及封装它们的 Unity 包)都需要真机才能运行 —— Cocos 的编辑器预览和模拟器无法加载原生二进制文件。对于 Cocos 游戏来说,这意味着每一个集成步骤——尝试新的 UI 布局、处理一个新的 Failed 原因,任何事情——都需要一次完整的真机构建。本包用一部分 UI 精细度(见下文"与原生 SDK 的区别")换取完全在 Cocos Creator 自己的预览环境中运行,就像你游戏的其余部分一样。
1. 添加包
npm install @alogame/kyc-sdk
Cocos Creator 像解析其他任何 npm 依赖一样解析它 —— 无需扩展,无需修改 Project Settings。
2. 初始化
启动时调用一次:
import { init } from "@alogame/kyc-sdk";
init("dev");
3. 设置 game role
拿到服务器返回的 sessionToken 之后(见服务器集成):
import { setGameRole } from "@alogame/kyc-sdk";
setGameRole(
"player_account_name", // uid —— 必须与服务器生成 session 时使用的 `username` 一致
sessionToken,
"server_01", // 可选
"character_123", // 可选
);
uid 与 sessionToken 总是来自同一次服务器响应 —— 绝不要只传其一。切换到不同的 uid 会清除前一个 uid 已缓存的验证状态。
4. 打开界面
import { show } from "@alogame/kyc-sdk";
import type { AlogameKycListener, AlogameKycResult } from "@alogame/kyc-sdk";
function startXmdt(): void {
show(listener);
}
const listener: AlogameKycListener = {
onResult(result: AlogameKycResult) {
switch (result.kind) {
case "success":
break; // 仅为 UI 信号 —— 请看下方警告
case "failed":
console.warn("XMDT failed:", result.reason, result.message);
break;
case "cancelled":
break; // 玩家关闭了表单 —— 仅在 xmdtRequired 为 false 时可能发生
}
},
onSessionTokenNeeded(uid: string) {
// 见下方"刷新过期 token"
},
};
onResult 只是 UI 信号,不是凭证success 表示玩家设备声称流程已完成 —— 被修改过的客户端可以在完全没有验证的情况下伪造这个回调。在放行任何依赖 XMDT 的内容之前,请从你的服务器再次调用 POST /xmdt/session,并以该响应中的 xmdtCompleted 字段为准。见服务器集成。
onResult 每次 show() 只会触发恰好一次,在界面已完全关闭之后 —— 你可以在其中直接展示自己的 UI。onSessionTokenNeeded 可能在此之前触发零次或多次。
SDK 完全拥有它自己的界面 —— 姓名、出生日期、手机号输入、OTP 和同意条款全部在内部处理,玩家输入的任何内容都不会暴露给你的游戏。
流程已打开时再次调用 show() 会被忽略(记录日志,不会崩溃)。
如果 session 表明玩家已完成 XMDT,show() 会直接短路返回 success,不展示任何界面。这是预期行为 —— 但在测试时意味着每个 uid 只能走一次完整流程。请每次测试都换用新的 uid。
处理 failed
switch (result.reason) {
case "notInitialized":
case "noGameRole":
// 你这边的集成错误 —— 没有先调用 init()/setGameRole()。
// 绝不要展示给玩家;请修正调用顺序。
break;
case "sessionUnavailable":
case "sessionInvalid":
// 无法获得可用的 session token。重新生成一个再重试。
break;
case "otpAttemptsExceeded":
case "otpUnavailable":
case "rateLimited":
// 触发了防滥用限制。展示笼统的"请稍后再试" —— 不要暴露具体是哪一种限制。
break;
case "serviceUnavailable":
// 玩家已在界面内重试过之后,身份服务仍不可用。让他们稍后再试。
break;
case "unknownError":
// 向前兼容的兜底分支。绝不会崩溃。
break;
}
可恢复的问题 —— OTP 输错、校验失败、网络短暂抖动 —— 都在 SDK 界面内部处理,永远不会到达 onResult。上面每一个 reason 都代表流程无法继续,而不是玩家打错了字。
刷新过期 token
与 Unity 包不同,JS 闭包没有需要跨越的边界,因此 tokenProvider 直接暴露出来:
init("dev", (uid, callback) => {
mintSessionToken(uid).then((token) => callback(token)).catch(() => callback(null));
});
如果你不传 tokenProvider,则改为响应 onSessionTokenNeeded:
import { provideSessionToken, abortPendingToken } from "@alogame/kyc-sdk";
onSessionTokenNeeded(uid: string) {
mintSessionToken(uid)
.then((token) => (token ? provideSessionToken(token) : abortPendingToken()))
.catch(() => abortPendingToken());
}
必须且只能响应其中之一。若两者都不实现,过期 token 会让流程以 sessionUnavailable 结束,而不是一直挂起。
与原生 SDK 的区别
这里没有任何隐藏或意外之处 —— 每一条都是为了不依赖原生代码而做出的、有意披露的取舍:
- 没有防截屏机制。 Android/iOS SDK 会启用
FLAG_SECURE或等效机制。本包没有:没有任何法规要求这么做,而且界面上只显示玩家自己正在输入的数据。 - 没有 SMS 自动填充。 OTP 验证码必须手动输入(或粘贴 —— 见下文),绝不会从收到的短信中自动识别。
- "粘贴验证码"仅在 Cocos Creator 基于浏览器的编辑器预览中有效,在原生构建中无效。Cocos 的引擎在浏览器之外完全没有剪贴板读取 API;在原生构建上,玩家需要手动输入验证码。
- 6 位 OTP 验证码是单个输入框,不像原生 SDK 那样是六个可自动跳转的独立输入框。Cocos 的文本输入组件尚未确认有可以用代码在多个输入框之间切换焦点的方式。
- 仅支持越南语 —— 与原生 SDK 保持一致,此前的一项产品决策已经去掉了英语。
示例
尚未发布 —— 本包从未在真实的 Cocos Creator 项目中运行过(见本页顶部的警告)。
参考
| 成员 | 用途 |
|---|---|
init(env, tokenProvider?) | 在其他一切之前调用一次 |
setGameRole(uid, sessionToken, serverId?, roleId?) | 始终同时传入 uid 与 sessionToken |
setPrefill(fullName) | 可选 —— 预填姓名字段 |
isVerified() | 内存缓存,仅用于 UI 状态 —— 绝不能当作门禁 |
show(listener) | 打开界面 |
provideSessionToken(token) / abortPendingToken() | 响应 onSessionTokenNeeded |
AlogameKycEnv | "dev" | "prod" |
AlogameKycResult | {kind: "success"} | {kind: "failed", reason, message?} | {kind: "cancelled"} |
AlogameKycFailReason | 9 个封闭取值 —— 见上文 |
AlogameKycListener | onResult(必须实现)、onSessionTokenNeeded(可选) |
isVerified() 读取的是最近一次成功流程留下的内存缓存 —— 不是授权依据,与 onResult 的注意事项相同。仅用于 UI 状态,绝不能当作门禁本身。