Android Payment
The Alogame SDK wraps Google Play Billing with server-side receipt verification and crash-safe pending purchase recovery.
Purchase — showPayment
Build a GameData object with the current player context, then call OegSdk.showPayment. The SDK opens the IAP UI, handles the Google Play billing flow, and verifies the receipt with the Alogame server automatically.
import vn.oeg.sdk.v2.legacy.domain.pojo.GameData
val gameData = GameData(
userId = 0L, // not used by SDK — pass 0
serverId = serverId, // Int
roleId = roleId, // Int
level = level, // Int
roleIdString = roleIdString, // String? — preferred character ID
extInfo = extInfo // String? — extra info passed through to game server
)
OegSdk.showPayment(
activity = this,
gameData = gameData,
callBack = iabCallback,
productId = "com.oeg.game.pack1"
)
import vn.oeg.sdk.v2.legacy.domain.pojo.GameData;
GameData gameData = new GameData(
0L, // userId — not used, pass 0
serverId, // Int
roleId, // Int
level, // Int
roleIdString, // String? — preferred character ID
extInfo // String? — extra info
);
OegSdk.INSTANCE.showPayment(
this,
gameData,
iabCallback,
"com.oeg.game.pack1"
);
Define the callback once (e.g. a field on your Activity):
private val iabCallback = object : IabCallBack {
override fun onIabResult(iabResult: WorkResult<PurchaseInfo>) {
if (iabResult.isSuccess) {
// Payment verified by OEG server. Item delivery is performed by the
// game server via the OEG entitlement webhook — DO NOT grant items
// locally. Refresh player inventory from your game server.
} else {
// Error — iabResult.message contains a human-readable reason.
// The SDK saves the transaction locally and retries on the next
// restorePurchases call — no action needed from the game.
}
}
}
private final IabCallBack iabCallback = iabResult -> {
if (iabResult.isSuccess()) {
// Payment verified by OEG server. Item delivery is performed by the
// game server via the OEG entitlement webhook — DO NOT grant items
// locally. Refresh inventory from your game server.
} else {
// Error — iabResult.getMessage() contains a human-readable reason.
}
};
isSuccess only confirms that the Alogame backend verified the receipt with Google Play. It is not a signal to grant items from the client. Item entitlement is performed by your game server, which receives a delivery callback from the Alogame backend after verification.
Your game server must implement the Mobile IAP server APIs. See Server Integration → Mobile IAP for the API contract.
Pending Purchase Recovery — restorePurchases
Call this after the player enters the game and GameData is known. It re-verifies any purchases that were interrupted by a crash or network failure.
OegSdk.restorePurchases(gameData, object : IabCallBack {
override fun onIabResult(iabResult: WorkResult<PurchaseInfo>) {
if (iabResult.isSuccess) {
// Pending purchase verified and delivered — refresh inventory
} else {
// No pending purchases, or error — safe to ignore
}
}
})
OegSdk.INSTANCE.restorePurchases(gameData, iabResult -> {
if (iabResult.isSuccess()) {
// Pending purchase verified and delivered — refresh inventory
} else {
// No pending purchases, or error — safe to ignore
}
});
How It Works
- Player taps Buy →
showPaymentopens the IAP UI - SDK queries Google Play for
ProductDetails, then launches the billing flow - On purchase confirmed: SDK saves transaction +
GameDatatoSharedPreferences— does NOT callconsumeAsyncyet - SDK sends
purchaseTokento Alogame server for S2S verification - On server success or HTTP 409: SDK calls
consumeAsync, removes the pending entry, firesonIabResult(success) - If the app crashes before step 4, the entry survives → recovered by
restorePurchases
Duplicate Transaction (HTTP 409)
If the app crashes after the server grants items but before consumeAsync is called, the next restorePurchases re-verifies and the server returns HTTP 409. The SDK handles this automatically — calls consumeAsync, removes the entry, and fires a success result.
Multi-Account Safety
Each pending entry is tagged with the purchasing user's UUID. restorePurchases filters by currentUUID — no cross-account item delivery.
TTL
Entries older than 3 days are pruned automatically. The 3-day threshold matches Google's auto-refund window — after this point consumeAsync would return ITEM_NOT_OWNED. A [CS-ALERT] warning is logged for any pruned entries.
Level Gating
Gate purchases by level requirements configured remotely:
OEGPayment.isIAPEnabled(level = currentLevel) { isEnabled ->
if (isEnabled) {
// Show payment button
} else {
// Show "Reach level X to unlock the shop"
}
}
OEGPayment.INSTANCE.isIAPEnabled(currentLevel, isEnabled -> {
if (isEnabled) {
// Show payment button
} else {
// Show "Reach level X to unlock the shop"
}
});