XMDT — Unity
Một package UPM, com.alogame.kycsdk, bọc đúng hai SDK native Android và iOS được mô tả ở trang Android và iOS. Binary native được nhúng sẵn trong package, nên không có bước resolve JitPack, CocoaPods hay SPM lúc build, và không phải sửa gì trong Gradle hay Xcode.
| Môi trường | Base URL |
|---|---|
| Dev | https://api-xmdt.dev.alogame.vn |
| Prod | https://api-xmdt.alogame.vn |
Cả hai đều đang hoạt động. gameId của bạn phải được Alogame Operations bật XMDT trước khi dùng được ở cả hai — xem Tích hợp phía Server.
Tích hợp Server mô tả lệnh gọi duy nhất phía server của bạn để mint sessionToken. Client của bạn không bao giờ gọi trực tiếp API danh tính của Alogame và không bao giờ thấy secret HMAC của bạn.
Yêu cầu: Unity 2021.3+, Android minSdkVersion 24+, iOS 15+. Phải cài Build Support cho Android và/hoặc iOS qua Unity Hub — thiếu module nền tảng thì phần code native của package bị compile-out hoàn toàn, và console sạch không chứng minh được gì.
1. Thêm package
Package Manager → + → Add package from git URL:
https://github.com/alo-game/alogame-kyc-sdk-unity.git#0.1.4
Hãy ghim tag. Các tag mới hơn xem ở releases.
Nó phát hành .aar và .xcframework đã compile cho mỗi tag, kèm phần code C#/Java/Swift của chính package này ở dạng source đọc được. Đây không phải repo riêng tư gitlab.oeg.vn (mà Unity của bạn cũng không có quyền truy cập).
0.1.0 và 0.1.1 phát hành .xcframework thiếu resource bundle. Trên iOS chúng crash ngay khi màn hình được vẽ: Fatal error: unable to find bundle named AlogameKycKit_AlogameKycKit.
2. Khởi tạo
Gọi một lần, lúc khởi động:
using Alogame.KycSdk;
AlogameKycSdk.Init(AlogameKycEnv.Dev);
Trong Editor và trên desktop, mọi lệnh gọi đều là no-op có ghi log, nên game của bạn vẫn chạy được ở Play Mode mà không cần thiết bị.
Init có thêm tham số thứ hai tùy chọn, brand — AlogameKycBrand.Alo (mặc định) hoặc AlogameKycBrand.Oeg. Nó chỉ đổi logo hiển thị ở phần header của màn hình; env vẫn là thứ duy nhất quyết định endpoint mạng.
AlogameKycSdk.Init(AlogameKycEnv.Dev, AlogameKycBrand.Oeg);
3. Set game role
Sau khi đã có sessionToken từ server của bạn (xem Tích hợp Server):
AlogameKycSdk.SetGameRole(
uid: "player_account_name", // phải khớp `username` mà server bạn đã mint session cho
sessionToken: sessionToken,
serverId: "server_01", // tuỳ chọn
roleId: "character_123" // tuỳ chọn
);
uid và sessionToken luôn về cùng một response từ server — không bao giờ gọi với cái này mà thiếu cái kia. Đổi sang uid khác sẽ xoá trạng thái đã xác thực cache của uid cũ.
4. Mở màn hình
using UnityEngine;
using Alogame.KycSdk;
public class XmdtFlow : MonoBehaviour
{
public void StartXmdt()
{
AlogameKycSdk.Show(new Listener(this));
}
private sealed class Listener : AlogameKycListener
{
private readonly XmdtFlow _owner;
public Listener(XmdtFlow owner) { _owner = owner; }
public override void OnResult(AlogameKycResult result)
{
switch (result)
{
case AlogameKycResult.Success _:
break; // chỉ là tín hiệu UI — đọc cảnh báo bên dưới
case AlogameKycResult.Failed f:
Debug.LogWarning("XMDT failed: " + f.Reason + " " + f.Message);
break;
case AlogameKycResult.Cancelled _:
break; // Người chơi đóng form — chỉ xảy ra khi xmdtRequired = false
}
}
public override void OnSessionTokenNeeded(string uid)
{
// xem mục "Làm mới token hết hạn" bên dưới
}
}
}
OnResult là tín hiệu UI, không phải bằng chứngSuccess nghĩa là thiết bị của người chơi báo luồng đã hoàn tất — một client bị chỉnh sửa có thể tạo ra callback này mà không xác thực gì cả. Trước khi cho người chơi qua bất kỳ cổng nào phụ thuộc XMDT, hãy gọi lại POST /xmdt/session từ server của bạn và tin vào field xmdtCompleted trong response đó. Xem Tích hợp Server.
OnResult bắn đúng một lần cho mỗi Show(), luôn trên Unity main thread, sau khi màn hình native đã đóng xong — bạn có thể gọi API Unity và hiện UI của mình ngay trong đó. OnSessionTokenNeeded có thể bắn không lần nào hoặc nhiều lần trước đó.
SDK sở hữu toàn bộ màn hình của nó — họ tên, ngày sinh, nhập số điện thoại, OTP và đồng ý điều khoản đều xử lý bên trong, và không gì người chơi gõ vào lọt ra ngoài game của bạn.
Gọi Show() lần nữa khi một luồng đang mở sẽ bị bỏ qua (có ghi log, không crash).
Nếu session cho biết người chơi đã hoàn tất XMDT, Show() sẽ trả về Success ngay mà không hiện gì. Đó là hành vi đúng — nhưng khi test, nó có nghĩa mỗi uid chỉ chạy được luồng một lần. Hãy dùng uid mới cho mỗi lần thử.
Xử lý Failed
switch (f.Reason)
{
case AlogameKycFailReason.NotInitialized:
case AlogameKycFailReason.NoGameRole:
// Lỗi tích hợp phía bạn — chưa gọi Init()/SetGameRole() trước.
// Đừng bao giờ hiện cho người chơi; sửa thứ tự gọi.
break;
case AlogameKycFailReason.SessionUnavailable:
case AlogameKycFailReason.SessionInvalid:
// Không lấy được session token dùng được. Mint token mới rồi thử lại.
break;
case AlogameKycFailReason.OtpAttemptsExceeded:
case AlogameKycFailReason.OtpUnavailable:
case AlogameKycFailReason.RateLimited:
// Chạm giới hạn chống lạm dụng. Hiện thông báo chung "thử lại sau" —
// đừng lộ ra là chạm giới hạn nào.
break;
case AlogameKycFailReason.ServiceUnavailable:
// Dịch vụ danh tính lỗi sau khi người chơi đã thử lại ngay trong màn
// hình. Để họ gọi lại sau.
break;
case AlogameKycFailReason.UnknownError:
// Catch-all cho tương thích về sau. Không bao giờ crash.
break;
}
Các vấn đề còn cứu được — gõ nhầm OTP, lỗi validate, mạng chập chờn — đều được xử lý bên trong màn hình của SDK và không bao giờ tới OnResult. Mọi reason ở trên đều là trường hợp luồng không thể tiếp tục, không phải người chơi gõ nhầm.
Làm mới token hết hạn
Cơ chế closure tokenProvider mà tích hợp native Android/iOS có được cố tình không expose ở đây: một delegate C# không sống được như callback thật qua biên JNI / P-Invoke. Unity dùng đường delegate thay thế:
public override void OnSessionTokenNeeded(string uid)
{
StartCoroutine(MintThen(uid, token =>
{
if (string.IsNullOrEmpty(token)) AlogameKycSdk.AbortPendingToken();
else AlogameKycSdk.ProvideSessionToken(token);
}));
}
Phải đáp lại bằng đúng một trong hai. Nếu không làm gì cả, token hết hạn sẽ kết thúc luồng bằng SessionUnavailable thay vì treo.
Package tác động gì tới bản build của bạn
Không có gì bạn phải cấu hình — nhưng ghi ra đây để không có gì bất ngờ:
Android. Unity tự merge .aar đã nhúng vào Gradle project nó sinh ra. Một hook IPostGenerateGradleAndroidProject trong package sẽ thêm org.jetbrains.kotlin:kotlin-stdlib vào project đó — SDK native viết bằng Kotlin, còn .aar nhúng dạng file trần không có POM để Gradle biết dependency transitive của nó. Hook chỉ thêm vào, không ghi đè cấu hình của bạn.
iOS. Một hook IPostprocessBuildWithReport set ba thứ Unity không tự set: SWIFT_VERSION (bridge viết bằng Swift, mà không template nào của Unity dùng Swift), bước embed cho AlogameKycKit.framework, và LD_RUNPATH_SEARCH_PATHS. Thiếu cái cuối thì app build sạch rồi chết lúc khởi động với Library not loaded: @rpath/AlogameKycKit.framework/AlogameKycKit.
Sau khi build iOS, tìm dòng này trong console Unity — nếu không có nghĩa là hook không chạy và app sẽ crash lúc mở:
[AlogameKycSdk] embedded the device slice (…) and configured rpath.
Máy thật là cấu hình duy nhất đã được kiểm chứng đầy đủ. Bản build cho Simulator thì chưa chạy được: một lần thử bị từ chối với thông báo "This app needs to be updated by the developer" — binary x86_64 trên runtime Simulator arm64 — và ép ARCHS=arm64 thì lỗi lúc link.
Nguyên nhân chưa từng được xác định, và nhiều khả năng là do cấu hình build chứ không phải một giới hạn cứng. Unity 6000.5 có phát hành các variation arm64 cho Simulator, còn hook postprocess của package này vốn đã chọn đúng slice simulator trong .xcframework khi target SDK là Simulator. Hãy coi Simulator là chưa được kiểm chứng, không phải là không thể.
Sample
Package Manager → package này → Samples → Reference Scene → Import. Gắn AlogameKycSample lên một GameObject, điền sessionToken trong Inspector, rồi build ra thiết bị.
Tham chiếu
| Thành phần | Mục đích |
|---|---|
AlogameKycSdk.Init(env, brand?) | Gọi một lần trước mọi thứ khác — brand tùy chọn, mặc định AlogameKycBrand.Alo |
AlogameKycSdk.SetGameRole(uid, sessionToken, serverId?, roleId?) | Luôn truyền uid và sessionToken cùng nhau |
AlogameKycSdk.SetPrefill(fullName) | Tuỳ chọn — điền sẵn ô họ tên |
AlogameKycSdk.IsVerified() | Cache trong RAM, chỉ dùng cho trạng thái UI — không bao giờ là cổng chặn |
AlogameKycSdk.Show(listener) | Mở màn hình |
AlogameKycSdk.ProvideSessionToken(token) / AbortPendingToken() | Đáp lại OnSessionTokenNeeded |
AlogameKycEnv | Dev | Prod |
AlogameKycBrand | Alo | Oeg — chỉ thay đổi giao diện, không ảnh hưởng tới việc chọn env/endpoint |
AlogameKycResult | Success | Failed(Reason, Message) | Cancelled |
AlogameKycFailReason | 9 giá trị đóng — xem ở trên |
AlogameKycListener | Abstract class: OnResult (bắt buộc), OnSessionTokenNeeded (tuỳ chọn) |
AlogameKycListener là abstract class chứ không phải interface có default member, nhờ vậy package chạy được trên mọi bản Unity từ 2021.3 thay vì đòi hỏi hỗ trợ C# 8 (chỉ có từ 2021.2+).
IsVerified() đọc cache trong RAM từ lần hoàn tất gần nhất — không phải nguồn phân quyền, cùng lưu ý như OnResult. Chỉ dùng cho trạng thái UI, không dùng làm cổng chặn.