XMDT — Android
Native SDK, no WebView. Written in Kotlin, callable from Java. Independent of SDK v2 — do not mix vn.alogame.kycsdk with vn.oeg.sdk.v2 imports or expect them to share any state.
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.
1. Add the dependency
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
dependencies {
implementation("com.github.alo-game:alogame-kyc-sdk-android:0.1.1")
}
vn.alogame:kyc-sdkUse com.github.alo-game:alogame-kyc-sdk-android:<version> — JitPack only ever serves a build under the coordinate it was first requested with, which is always this GitHub org/repo form. A custom vn.alogame:kyc-sdk groupId is not resolvable even though it appears in the release repo's own build.gradle; confirmed by curling jitpack.io directly (404 under vn/alogame/kyc-sdk/, 200 under com/github/alo-game/alogame-kyc-sdk-android/).
Browse available versions at jitpack.io/#alo-game/alogame-kyc-sdk-android.
The published artifact has zero third-party runtime dependencies — nothing to conflict with your app's own dependency tree.
2. Initialize
Once, at app start:
import vn.alogame.kycsdk.AlogameKycSdk
import vn.alogame.kycsdk.AlogameKycConfig
import vn.alogame.kycsdk.AlogameKycEnv
AlogameKycSdk.init(
context,
AlogameKycConfig(env = AlogameKycEnv.DEV)
)
import vn.alogame.kycsdk.AlogameKycSdk;
import vn.alogame.kycsdk.AlogameKycConfig;
import vn.alogame.kycsdk.AlogameKycEnv;
AlogameKycSdk.INSTANCE.init(
context,
new AlogameKycConfig(AlogameKycEnv.DEV, null)
);
There is no way to point the SDK at a custom URL — env is the only switch, and only DEV is usable today (see Overview).
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 — omit if not applicable
roleId = "character_123" // optional
)
AlogameKycSdk.INSTANCE.setGameRole("player_account_name", sessionToken, "server_01", "character_123");
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
import vn.alogame.kycsdk.AlogameKycListener
import vn.alogame.kycsdk.AlogameKycResult
AlogameKycSdk.show(activity, object : AlogameKycListener {
override fun onResult(result: AlogameKycResult) {
when (result) {
is AlogameKycResult.Success -> {
// UI signal only — see the warning below before unlocking anything
}
is AlogameKycResult.Failed -> {
println("XMDT failed: ${result.reason}${result.message?.let { " ($it)" } ?: ""}")
}
is AlogameKycResult.Cancelled -> {
// Player closed the form — only possible when xmdtRequired was false
}
}
}
})
AlogameKycSdk.INSTANCE.show(activity, new AlogameKycListener() {
@Override
public void onResult(AlogameKycResult result) {
if (result instanceof AlogameKycResult.Success) {
// UI signal only — see the warning below
} else if (result instanceof AlogameKycResult.Failed) {
AlogameKycFailReason reason = ((AlogameKycResult.Failed) result).getReason();
String message = ((AlogameKycResult.Failed) result).getMessage();
} else if (result instanceof AlogameKycResult.Cancelled) {
// Player closed the form
}
}
@Override
public void onSessionTokenNeeded(String uid) { /* see "Refreshing an expired token" below */ }
});
onResult is a UI signal, not proofSuccess here means the player's device says the flow completed — a modified client can produce this callback without ever 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 instead. See Server Integration.
The SDK owns its screen entirely — full name, date of birth, phone entry, OTP, and consent are all handled internally, and nothing typed there is ever visible to your app. onResult fires exactly once per show() call, on the main thread, after the screen has already closed — safe to show your own UI immediately.
Calling show() again while a flow is already open is ignored (logged, not crashed).
Handling Failed
import vn.alogame.kycsdk.AlogameKycFailReason
when (result.reason) {
AlogameKycFailReason.notInitialized,
AlogameKycFailReason.noGameRole -> {
// Integration bug on your side — init()/setGameRole() wasn't called
// first. Never show this to a player; fix the call order.
}
AlogameKycFailReason.sessionUnavailable,
AlogameKycFailReason.sessionInvalid -> {
// Could not get a usable session token. Mint a fresh one and retry
// show() from scratch.
}
AlogameKycFailReason.otpAttemptsExceeded,
AlogameKycFailReason.otpUnavailable,
AlogameKycFailReason.rateLimited -> {
// The player (or their network) hit an abuse limit. Show a generic
// "try again later" — do not expose which limit was hit.
}
AlogameKycFailReason.serviceUnavailable -> {
// Alogame's identity service was down after the player already
// retried once inside the screen. Let them try show() again later.
}
AlogameKycFailReason.unknownError -> {
// Forward-compatibility catch-all. Never a crash.
}
}
Recoverable problems (bad OTP entry, a validation error, a transient network blip) are handled inside the SDK's own screen and never reach onResult at all — every Failed reason above is one where the flow could not continue, not one where the player simply made a typo.
Refreshing an expired token
Two ways to supply a fresh sessionToken when the one from setGameRole goes stale mid-flow — pick one:
Closure-based (works if your app can hold a closure across the SDK boundary):
AlogameKycSdk.init(
context,
AlogameKycConfig(
env = AlogameKycEnv.DEV,
tokenProvider = AlogameKycTokenProvider { uid, callback ->
// Call your server's mint endpoint again for this uid, then:
callback.onToken(newSessionTokenOrNull)
}
)
)
Callback-based (for a bridge/engine layer that can't pass a closure across a language boundary — leave tokenProvider unset and implement instead):
override fun onSessionTokenNeeded(uid: String) {
// Mint a new token for `uid`, then:
AlogameKycSdk.provideSessionToken(newToken)
// or, if you can't get one:
AlogameKycSdk.abortPendingToken()
}
If you don't implement either, a stale token ends the flow with sessionUnavailable rather than hanging.
Orientation
The screen supports both portrait and landscape — nothing to configure on your side.
Reference
| Type | Purpose |
|---|---|
AlogameKycSdk | The entire public API — init, setGameRole, setPrefill, isVerified, show, provideSessionToken, abortPendingToken |
AlogameKycConfig(env, tokenProvider) | Passed once to init |
AlogameKycEnv | DEV | PROD (PROD not yet available) |
AlogameKycResult | Success | Failed(reason, message) | Cancelled |
AlogameKycFailReason | 9 closed values — see above |
AlogameKycListener | onResult(result), optional onSessionTokenNeeded(uid) |
AlogameKycSdk.setPrefill(fullName) is optional — if your login layer already has the player's name (e.g. from a social login), pass it here before show() to prefill the form field. It's held in memory only and never sent anywhere until the player has seen and can edit it.
AlogameKycSdk.isVerified() reads an in-memory cache from the most recent successful flow — not an authorization source, same caveat as onResult above. Use it for UI state only (e.g. hiding a "Verify now" button), never as the gate itself.