XMDT — Unity
A UPM package, com.alogame.kycsdk, that wraps the same native Android and iOS SDKs described on the Android and iOS pages. The native binaries are vendored inside the package, so there is no JitPack, CocoaPods or SPM resolution during your build, and nothing to edit in Gradle or Xcode.
Everything on this page describes the dev environment (api-xmdt.dev.alogame.vn). Production has not been provisioned yet — do not point a live build at it.
Server Integration covers the one call your server makes to mint a sessionToken. Your client never talks to Alogame's identity API directly and never sees your HMAC secret.
Requirements: Unity 2021.3+, Android minSdkVersion 24+, iOS 15+. Android and/or iOS Build Support must be installed through Unity Hub — without the platform module the package's native code paths are compiled out entirely and a clean console proves nothing.
1. Add the package
Package Manager → + → Add package from git URL:
https://github.com/alo-game/alogame-kyc-sdk-unity.git#0.1.3
Pin the tag. Newer tags are listed under releases.
It ships the compiled .aar and .xcframework per tag, plus this package's own C#/Java/Swift glue as readable source. It is not the private gitlab.oeg.vn repo, which your Unity has no access to anyway.
0.1.0 and 0.1.1 shipped an .xcframework that was missing its resource bundle. On iOS they crash the moment the screen is drawn: Fatal error: unable to find bundle named AlogameKycKit_AlogameKycKit.
2. Initialize
Once, at startup:
using Alogame.KycSdk;
AlogameKycSdk.Init(AlogameKycEnv.Dev);
There is no way to point the SDK at a custom URL — env is the only switch, and only Dev is usable today.
In the Editor and on desktop every call is a logged no-op, so your game stays playable in Play Mode without a device.
3. Set the game role
After you have a sessionToken from your server (see Server Integration):
AlogameKycSdk.SetGameRole(
uid: "player_account_name", // must match the `username` your server minted the session for
sessionToken: sessionToken,
serverId: "server_01", // optional
roleId: "character_123" // optional
);
uid and sessionToken always arrive together from the same server response — never call this with one and not the other. Switching to a different uid clears any cached verified state for the previous one.
4. Show the screen
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; // UI signal only — see the warning below
case AlogameKycResult.Failed f:
Debug.LogWarning("XMDT failed: " + f.Reason + " " + f.Message);
break;
case AlogameKycResult.Cancelled _:
break; // Player closed the form — only possible when xmdtRequired was false
}
}
public override void OnSessionTokenNeeded(string uid)
{
// see "Refreshing an expired token" below
}
}
}
OnResult is a UI signal, not proofSuccess means the player's device says the flow completed — a modified client can produce this callback without verifying anything. Before letting a player past anything that depends on XMDT, call POST /xmdt/session again from your server and trust the xmdtCompleted field on that response. See Server Integration.
OnResult fires exactly once per Show(), always on the Unity main thread, after the native screen has finished closing — you can touch Unity APIs and present your own UI in it directly. OnSessionTokenNeeded may fire zero or more times before it.
The SDK owns its screen entirely — full name, date of birth, phone entry, OTP and consent are handled internally, and nothing typed there is ever visible to your game.
Calling Show() again while a flow is already open is ignored (logged, not crashed).
If the session says the player already completed XMDT, Show() short-circuits straight to Success without presenting anything. That is intended — but when testing, it means each uid only exercises the flow once. Use a fresh uid per attempt.
Handling Failed
switch (f.Reason)
{
case AlogameKycFailReason.NotInitialized:
case AlogameKycFailReason.NoGameRole:
// Integration bug on your side — Init()/SetGameRole() wasn't called
// first. Never show this to a player; fix the call order.
break;
case AlogameKycFailReason.SessionUnavailable:
case AlogameKycFailReason.SessionInvalid:
// Could not get a usable session token. Mint a fresh one and retry.
break;
case AlogameKycFailReason.OtpAttemptsExceeded:
case AlogameKycFailReason.OtpUnavailable:
case AlogameKycFailReason.RateLimited:
// An abuse limit was hit. Show a generic "try again later" — do not
// expose which limit it was.
break;
case AlogameKycFailReason.ServiceUnavailable:
// Identity service was down after the player already retried inside
// the screen. Let them try again later.
break;
case AlogameKycFailReason.UnknownError:
// Forward-compatibility catch-all. Never a crash.
break;
}
Recoverable problems — a mistyped OTP, a validation error, a transient network blip — are handled inside the SDK's screen and never reach OnResult. Every reason above is one where the flow could not continue, not one where the player simply made a typo.
Refreshing an expired token
The closure-based tokenProvider available to native Android and iOS integrations is deliberately not exposed here: a C# delegate cannot survive as a live callback across the JNI / P-Invoke boundary. Unity uses the delegate path instead:
public override void OnSessionTokenNeeded(string uid)
{
StartCoroutine(MintThen(uid, token =>
{
if (string.IsNullOrEmpty(token)) AlogameKycSdk.AbortPendingToken();
else AlogameKycSdk.ProvideSessionToken(token);
}));
}
Respond with exactly one of the two. If you implement neither, a stale token ends the flow with SessionUnavailable rather than hanging.
What the package does to your build
Nothing you configure — but for the record, so nothing here surprises you:
Android. Unity merges the vendored .aar into the Gradle project it generates. An IPostGenerateGradleAndroidProject hook in the package appends org.jetbrains.kotlin:kotlin-stdlib to that project — the native SDK is Kotlin, and the vendored .aar carries no POM for Gradle to learn its transitive dependencies from. The hook only appends; it never rewrites your configuration.
iOS. An IPostprocessBuildWithReport hook sets three things Unity does not: SWIFT_VERSION (the bridge is Swift, and no Unity template target uses Swift), the embed step for AlogameKycKit.framework, and LD_RUNPATH_SEARCH_PATHS. Without the last one the app builds cleanly and then dies at launch with Library not loaded: @rpath/AlogameKycKit.framework/AlogameKycKit.
Check for this line in the Unity console after an iOS build — if it is missing, the hook did not run and the app will crash at launch:
[AlogameKycSdk] embedded the device slice (…) and configured rpath.
A physical device is the only configuration verified end to end. A Simulator build has not been made to work: an attempt was rejected with "This app needs to be updated by the developer" — an x86_64 binary against an arm64 Simulator runtime — and forcing ARCHS=arm64 then failed at link.
The cause was never established, and it is more likely build configuration than a hard limitation. Unity 6000.5 does ship arm64 Simulator variations, and this package's postprocess hook already selects the .xcframework's simulator slice when the target SDK is the Simulator. Treat the Simulator as untested, not as impossible.
Sample
Package Manager → this package → Samples → Reference Scene → Import. Drop AlogameKycSample on a GameObject, set a sessionToken in the Inspector, and build to a device.
Reference
| Member | Purpose |
|---|---|
AlogameKycSdk.Init(env) | Call once before anything else |
AlogameKycSdk.SetGameRole(uid, sessionToken, serverId?, roleId?) | Always both uid and sessionToken together |
AlogameKycSdk.SetPrefill(fullName) | Optional — prefills the name field |
AlogameKycSdk.IsVerified() | In-memory cache, UI state only — never a gate |
AlogameKycSdk.Show(listener) | Presents the screen |
AlogameKycSdk.ProvideSessionToken(token) / AbortPendingToken() | Answer OnSessionTokenNeeded |
AlogameKycEnv | Dev | Prod (Prod not yet available) |
AlogameKycResult | Success | Failed(Reason, Message) | Cancelled |
AlogameKycFailReason | 9 closed values — see above |
AlogameKycListener | Abstract class: OnResult (required), OnSessionTokenNeeded (optional) |
AlogameKycListener is an abstract class rather than an interface with a default member, so the package works on every Unity version from 2021.3 rather than requiring the C# 8 support that arrived in 2021.2+.
IsVerified() reads an in-memory cache from the most recent successful flow — not an authorization source, same caveat as OnResult. Use it for UI state only, never as the gate itself.