跳到主要内容

XMDT —— iOS

原生 SDK,不使用 WebView。Swift Package,模块名 AlogameKycKit(入口类是 AlogameKycSdk——参见初始化——但你 import 的是 AlogameKycKit)。与 SDK v2 相互独立——不要与 OegSdkV2 的 import 混用,也不要指望它们共享任何状态。

环境
环境Base URL
Devhttps://api-xmdt.dev.alogame.vn
Prodhttps://api-xmdt.alogame.vn

两者都已上线。你的 gameId 必须先由 Alogame 运营方开通 XMDT,两个环境才能使用——参见服务器集成。

请先阅读这部分

服务器集成介绍了你服务器唯一需要发起的调用,用于生成 sessionToken。你的客户端从不直接调用 Alogame 的身份认证 API,也从不会看到你的 HMAC secret。

部署目标: iOS 15+。Swift 和 Objective-C 都能用——Objective-C 侧的 API 见下方Objective-C一节。

1. 添加 Package​

Xcode → File → Add Package Dependencies…

https://github.com/alo-game/alogame-kyc-sdk-ios

在 Dependency Rule 中,选择 Up to Next Major,起始版本 0.2.0(或任何更新的已发布 tag——参见releases)。当弹窗询问要添加哪个 product 到你的 target 时,选择 AlogameKycKit。

如果你目前锁定在 0.1.x

SPM 的 "Up to Next Major" 会把 0.x 版本里第一个非零的部分当作 major——锁定在 0.1.1 的规则会被解析成 >=0.1.1, <0.2.0,永远不会自动升级到 0.2.0(带 Objective-C 支持的版本)。需要把版本规则的下限手动改成 0.2.0 才能拿到。

这是编译好的 xcframework,不是源码仓库

这是一个独立的公开发布仓库,为每个 tag 发布预编译好的 .xcframework——不是私有的 gitlab.oeg.vn 源码仓库(反正你的 Xcode 也没有访问权限)。每个 release 都由 dev 仓库中的 scripts/deploy_ios_spm.sh 构建。

该 package 声明零第三方依赖——只链接 Foundation、UIKit 和 SafariServices,不会与你 App 现有的依赖树产生冲突。

2. 初始化​

在 App 启动时执行一次:

Swift
import AlogameKycKit

AlogameKycSdk.shared.initialize(
AlogameKycConfig(env: .dev)
)
OEG 品牌游戏

AlogameKycConfig 增加了一个可选参数 brand——.alo(默认)或 .oeg。它只改变屏幕头部显示的 logo;env 仍然是选择网络端点的唯一开关。

Swift
AlogameKycConfig(env: .dev, brand: .oeg)

3. 设置游戏角色​

在你从服务器拿到 sessionToken 之后(参见服务器集成):

Swift
AlogameKycSdk.shared.setGameRole(
uid: "player_account_name", // 必须与你服务器 mint session 时使用的 `username` 一致
sessionToken: sessionToken,
serverId: "server_01", // 可选——不适用时可省略
roleId: "character_123" // 可选
)

uid 和 sessionToken 永远一起从同一个服务器响应中获得——绝不要只传其中一个而不传另一个。切换到不同的 uid 会清除上一个 uid 缓存的已验证状态。

4. 显示界面​

Swift
import AlogameKycKit

class YourViewController: UIViewController, AlogameKycListener {

func startXmdt() {
AlogameKycSdk.shared.show(from: self, listener: self)
}

func onResult(_ result: AlogameKycResult) {
switch result {
case .success:
break // 仅是界面信号——在解锁任何内容之前请先看下面的警告
case .failed(let reason, let message):
print("XMDT failed: \(reason)" + (message.map { " (\($0))" } ?? ""))
case .cancelled:
break // 玩家关闭了表单——只有在 xmdtRequired 为 false 时才可能出现
}
}

func onSessionTokenNeeded(uid: String) {
// 见下方"刷新过期 token"
}
}
onResult 是界面信号,不是证明

这里的 .success 仅表示玩家设备报告流程已完成——被修改过的客户端可以在从未真正验证的情况下产生这个回调。在放行任何依赖 XMDT 的内容之前,请从你的服务器再次调用 POST /xmdt/session,并信任该响应中的 xmdtCompleted 字段。参见服务器集成。

AlogameKycListener 被 SDK 以 weak 方式持有——从单例对你的 view controller 持有强引用会造成一个你看不到也解不开的 retain cycle,所以请确保你传入的对象遵循该协议,并且在整个流程期间保持存活(一个正在 present 该界面的 view controller 自然就满足这一点)。

SDK 完全自己管理界面——姓名、出生日期、手机号输入、OTP 和同意书都在内部处理,玩家输入的任何内容都不会暴露给你的 App。onResult 在每次 show() 调用中只会触发一次,在主队列上,且是在界面已经 dismiss 之后——可以立即安全地 present 你自己的界面。

在一个流程已经打开时再次调用 show() 会被忽略(会记录日志,不会崩溃)。

处理 .failed​

Swift
switch reason {
case .notInitialized, .noGameRole:
// 你这边的集成错误——没有先调用 initialize()/setGameRole()。
// 绝不要把这个展示给玩家;请修正调用顺序。
break
case .sessionUnavailable, .sessionInvalid:
// 未能获取可用的 session token。请重新 mint 一个,
// 然后从头重试 show()。
break
case .otpAttemptsExceeded, .otpUnavailable, .rateLimited:
// 玩家(或其网络)触发了防滥用限制。显示一个通用的
// "请稍后再试"——不要暴露具体触发了哪个限制。
break
case .serviceUnavailable:
// Alogame 的身份认证服务在玩家已经在界面内重试一次之后
// 仍然不可用。让他们稍后再试一次 show()。
break
case .unknownError:
// 面向未来兼容性的兜底情况。绝不会崩溃。
break
}

可恢复的问题(OTP 输错、校验错误、短暂的网络抖动)都在 SDK 自己的界面内部处理,从不会传到 onResult——上面每一个 .failed 原因都是流程无法继续的情况,而不是玩家只是手误打错的情况。

刷新过期 token​

当 setGameRole 传入的 token 在流程中途过期时,有两种方式提供新的 sessionToken——选择其中一种:

基于闭包(如果你的 App 能跨 SDK 边界持有闭包):

Swift
AlogameKycSdk.shared.initialize(
AlogameKycConfig(env: .dev, tokenProvider: { uid, callback in
// 为这个 uid 再次调用你服务器的 mint 接口,然后:
callback(newSessionTokenOrNil)
})
)

基于 delegate(适用于无法跨语言边界传递闭包的 bridge/引擎层——不设置 tokenProvider,改为实现下面的方法):

Swift
func onSessionTokenNeeded(uid: String) {
// 为 `uid` 生成一个新 token,然后:
AlogameKycSdk.shared.provideSessionToken(newToken)
// 或者,如果无法获取:
AlogameKycSdk.shared.abortPendingToken()
}

如果你两种方式都没有实现,过期的 token 会以 .sessionUnavailable 结束流程,而不会一直挂起。

屏幕方向​

界面同时支持竖屏和横屏——你这边不需要做任何配置。

Objective-C​

同一个 package,同一个 AlogameKycSdk 单例——不需要额外添加任何东西。AlogameKycResult 本身无法跨越到 Objective-C 一侧(它是一个 Swift 枚举,每个 case 携带不同的载荷——.failed(reason:, message:)——而 Objective-C 没有与之对应的概念),所以面向 Objective-C 的这部分改用一个小的、并行的 listener 协议,再加两个以 Int 为底层类型的枚举,而不是硬把 Swift 的形态搬过去:

Objective-C
#import <AlogameKycKit/AlogameKycKit-Swift.h>

@interface MyKycListener : NSObject <AlogameKycObjcListener>
@end

@implementation MyKycListener

- (void)onResultWithStatus:(AlogameKycResultStatus)status
reason:(AlogameKycFailReasonCode)reason
message:(NSString * _Nullable)message {
switch (status) {
case AlogameKycResultStatusSuccess:
break; // 仅是界面信号——解锁任何内容之前请先看上面的警告
case AlogameKycResultStatusFailed:
NSLog(@"XMDT failed, reason=%ld", (long)reason);
break;
case AlogameKycResultStatusCancelled:
break;
}
}

- (void)onSessionTokenNeededForUid:(NSString *)uid {
// 见上方"刷新过期 token"
}

@end
Objective-C
[[AlogameKycSdk shared] initializeWithEnv:AlogameKycEnvDev tokenProvider:nil];

// OEG 品牌游戏——独立的 selector,不影响已有的两参数调用:
[[AlogameKycSdk shared] initializeWithEnv:AlogameKycEnvDev tokenProvider:nil brand:AlogameKycBrandOeg];

[[AlogameKycSdk shared] setGameRoleWithUid:@"player_account_name"
sessionToken:sessionToken
serverId:nil
roleId:nil];

MyKycListener *listener = [MyKycListener new];
[[AlogameKycSdk shared] showFromViewController:self objcListener:listener];
这里到底验证过什么

AlogameKycSdk.shared、showFromViewController:objcListener:、initializeWithEnv:tokenProvider:、initializeWithEnv:tokenProvider:brand:,以及上面这些枚举/协议,都已经通过一次针对 iOS Simulator SDK 的真实 xcodebuild 构建加测试运行确认过,包括上面代码片段所引用的、真实生成出来的 AlogameKycKit-Swift.h 文件本身。没有验证过的是一个真实的 Objective-C 客户端 App 端到端调用它——如果你遇到任何和本页描述不一致的地方,请告诉我们。

参考​

类型用途
AlogameKycSdk.shared完整的公开 API —— initialize、setGameRole、setPrefill、isVerified、show、provideSessionToken、abortPendingToken
AlogameKycConfig(env:tokenProvider:brand:)传给 initialize 一次——brand 可选,默认 .alo
AlogameKycEnv.dev | .prod
AlogameKycBrand.alo | .oeg——仅影响外观,不影响 env/端点选择
AlogameKycResult.success | .failed(reason:message:) | .cancelled
AlogameKycFailReason9 个固定 case——见上文
AlogameKycListeneronResult(_:),可选的 onSessionTokenNeeded(uid:)
AlogameKycObjcListener面向 Objective-C 的 listener —— onResultWithStatus:reason:message:,可选的 onSessionTokenNeededForUid:
AlogameKycResultStatusAlogameKycResult 的 Int 底层类型镜像版本 —— Success | Failed | Cancelled
AlogameKycFailReasonCodeAlogameKycFailReason 的 Int 底层类型镜像版本,同样的 9 个 case

AlogameKycSdk.shared.setPrefill(_:) 是可选的——如果你的登录环节已经拿到了玩家姓名(例如来自 Sign in with Apple),可以在调用 show() 之前传进来,预填表单字段。它只保存在内存中,在玩家看到并能够编辑之前绝不会被发送到任何地方。

Sign in with Apple 的 fullName 只会出现一次

ASAuthorizationAppleIDCredential.fullName 只在同一个 Apple ID 第一次授权时才有值。请在那一刻捕获它并传给 setPrefill——在玩家于系统设置中撤销你的 App 授权之前,之后每次登录它都会是空的。

AlogameKycSdk.shared.isVerified 读取的是最近一次成功流程留下的内存缓存——不是授权依据,与上面 onResult 的提醒相同。只能用于界面状态(例如隐藏一个"立即验证"按钮),绝不能作为真正的门禁判断。