Skip to main content

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.

Dev environment only

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.

Read this first

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

settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
app/build.gradle.kts
dependencies {
implementation("com.github.alo-game:alogame-kyc-sdk-android:0.1.1")
}
Coordinate is the GitHub repo, not vn.alogame:kyc-sdk

Use 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:

Kotlin
import vn.alogame.kycsdk.AlogameKycSdk
import vn.alogame.kycsdk.AlogameKycConfig
import vn.alogame.kycsdk.AlogameKycEnv

AlogameKycSdk.init(
context,
AlogameKycConfig(env = AlogameKycEnv.DEV)
)
Java
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):

Kotlin
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
)
Java
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

Kotlin
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
}
}
}
})
Java
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 proof

Success 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

Kotlin
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):

Kotlin
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):

Kotlin
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

TypePurpose
AlogameKycSdkThe entire public API — init, setGameRole, setPrefill, isVerified, show, provideSessionToken, abortPendingToken
AlogameKycConfig(env, tokenProvider)Passed once to init
AlogameKycEnvDEV | PROD (PROD not yet available)
AlogameKycResultSuccess | Failed(reason, message) | Cancelled
AlogameKycFailReason9 closed values — see above
AlogameKycListeneronResult(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.