iOS Payment
The Alogame SDK supports StoreKit 2 (iOS 15+) with automatic fallback to StoreKit 1. It handles receipt validation, server-side verification, and crash-safe pending transaction recovery.
Initialize
Call initialize once after login, passing a TokenProvider that returns the current auth token and user UUID.
OEGPayment.shared.initialize(
tokenProvider: myTokenProvider,
gameId: YOUR_GAME_ID
)
Purchase and Verify
Build a GameData object with the current player context, then call purchaseAndVerify.
import OegSdkV2
let gameData = GameData(
serverId: "server_01",
characterId: "role_123",
characterName: "Hero",
level: 50,
extraData: nil
)
OEGPayment.shared.purchaseAndVerify(
productId: "com.oeg.game.pack1",
gameData: gameData
) { purchaseResult, serverResult in
switch purchaseResult {
case .success:
if serverResult?.success == true {
// 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 {
// Purchase recorded locally, server verify pending — retries automatically
}
case .cancelled:
break
case .failed(let error):
print("Purchase failed: \(error.localizedDescription)")
default:
break
}
}
A successful serverResult only confirms that the Alogame backend verified the StoreKit receipt with Apple. It is not a signal to grant items from the client. Item entitlement must be performed by your game server, which receives a delivery callback from the Alogame backend after verification. Granting items client-side is not authoritative and can lead to double-grants or fraud.
Your game server must implement the Mobile IAP server APIs. See Server Integration → Mobile IAP for the API contract.
The SDK saves the transaction to a persistent store before calling the server. If the app crashes or the network fails, the pending entry survives and is re-verified automatically on next login.
Pending Purchase Recovery
The SDK automatically re-verifies pending purchases after every successful login. No extra code needed for the common case.
For sessions where the user was already logged in when the app launched (token still valid, no login flow triggered), call this once after your game finishes loading:
OEGPayment.shared.checkAndProcessPendingPurchases()
Manual Restore
Expose a "Restore Purchases" button for players who report paying but not receiving items. This re-verifies all pending entries for the current user against the server.
OEGPayment.shared.restorePurchases(gameData: gameData) { result in
switch result {
case .success:
// Item verified and delivered — refresh inventory
case .alreadyGranted:
// Server returned 409 — item was already granted previously
case .noItems:
// Nothing pending for this user
case .authError:
// User not logged in
case .alreadyProcessing:
// Another restore is already running — ignore
case .networkError(let error):
// Network failed — entry retained, will retry
case .serverError(let code, let message):
// Server error — entry retained, will retry
}
}
restorePurchases only recovers transactions still in the device's persistent store (within 30-day TTL). It does not query Apple's purchase history.
To avoid an unnecessary network call, check first:
if OEGPayment.shared.hasPendingPurchasesForCurrentUser() {
OEGPayment.shared.restorePurchases(gameData: gameData) { ... }
}
How It Works
- Player taps Buy → StoreKit confirms payment
- SDK saves transaction +
GameDatatoUserDefaults— does NOT callfinishTransactionyet - SDK sends receipt to Alogame server for S2S verification
- On server success or HTTP 409: SDK calls
finishTransaction, then removes the pending entry - If the app crashes before step 3, the entry survives in
UserDefaults - On next login,
checkAndProcessPendingPurchasesruns automatically and re-verifies
finishTransaction is always called before the store entry is removed. If the app crashes between those two steps, the entry is still present → next session re-verifies → server returns 409 → clean removal.
Duplicate Transaction (HTTP 409)
If the app crashes after the server grants items but before finishTransaction is called, StoreKit re-delivers the transaction on the next launch. The server returns HTTP 409. The SDK handles this automatically — calls finishTransaction, removes the pending entry, and returns .alreadyGranted. You do not need to handle 409 in your game code.
Multi-Account Safety
Each pending entry is tagged with the purchasing user's UUID. If a different user is logged in when restore runs, their entries are filtered out — no cross-account item delivery.
TTL
Entries older than 30 days are pruned automatically when the store is accessed. A [CS-ALERT] warning is logged for any pruned entries.
Objective-C — Legacy IAP API
Objective-C games (Egret, Cocos Creator) use inAppPurchaseWithServerID: on OEGManager. This API handles StoreKit internally and calls back with a status code.
[[OEGManager sharedManager] inAppPurchaseWithServerID:@"server_01"
roleID:@"role_123"
levels:@"50"
accountID:@"account_abc"
productID:@"com.oeg.game.pack1"
extInfo:@""
callback:^(OEGIAPStatus status, NSString *msg, NSError *err) {
if (status == OEGIAPStatusSuccess) {
// Purchase verified. Refresh player inventory from your game server.
// Do NOT grant items client-side.
} else if (status == OEGIAPStatusCancel) {
// User cancelled
} else {
NSLog(@"IAP failed: %@ / %@", msg, err.localizedDescription);
}
}];
Parameters:
| Parameter | Description |
|---|---|
serverID | Current game server ID |
roleID | Player's character/role ID |
levels | Player's current level (as string) |
accountID | Player's Alogame account ID |
productID | StoreKit product identifier |
extInfo | Extra data passed through to the game server (can be empty string) |
OEGIAPStatus values: OEGIAPStatusSuccess, OEGIAPStatusCancel, OEGIAPStatusFailed
The same server-authoritative delivery rule applies — item grants must come from your game server via the Alogame delivery webhook, not from the client callback.