SDK
This SDK provides secure bidirectional communication between the game and the native clients (iOS and Android), enabling third‑party developers to invoke native capabilities.
Exposed global API name: window.GameTokSDK
Capabilities:
| Action | Description |
|---|---|
| GET_PROFILE | Get the current user profile |
| GET_PROFILES | Batch‑get user profiles (by uid array) |
| PURCHASE | Initiate a purchase (e.g., in‑app purchase) |
| STORAGE_SET | Write to local key‑value storage (settings, progress, etc.) |
| STORAGE_GET | Read from local key‑value storage (retrieve previously saved data) |
| ADD_SCORE | Report a score (leaderboards, sharing, etc.) |
| GET_PERMISSION_MIC | Request microphone permission (Android) |
| TOPUP | Top up when in‑game currency is low; opens the client top‑up flow |
| ROUND_START | Round start notification (call once per round start; tied to platform round/energy policies) |
| ROUND_END | Round end notification (call when a round ends; round_id must match this round's ROUND_START) |
| GET_COUPON_TARGET_SCORE | Get the target score for showing a coupon dialog (the client returns the specific score value) |
| SHOW_COUPON_DIALOG | Notify the platform to show a coupon dialog (displayed by the client in the game container layer) |
| bookHQ | Book a highlight event (send a booking request; the client may prompt login first) |
| onBookHQ | Listen for the bookHQ booking result callback |
| onAudioSuspend | Listen for audio suspend event (triggered when the platform requires the game to mute) |
| offAudioSuspend | Remove audio suspend event listener |
| onAudioResume | Listen for audio resume event (triggered when the platform allows the game to restore sound) |
| offAudioResume | Remove audio resume event listener |
| onAutoStartGame | Listen for client "start game" command (same effect as clicking the in-game start button) |
| offAutoStartGame | Remove "start game" command listener |
| onExitAndResetGame | Listen for client "exit and reset game" command (end the game and return to initial state) |
| offExitAndResetGame | Remove "exit and reset game" command listener |
Integration examples
The following shows how to include the SDK on a page and call each capability.
1.1 Including the script
Option 1: Load the bundle via <script>
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
console.log('GameTokSDK version loaded:', !!window.GameTokSDK);
</script>Local development (not embedded in the app, debugging in a browser): After loading the SDK, call GameTokSDK.enableMock(). Promise‑based APIs will use built‑in mock data; no native client is required. Do not call this in production or store submission builds, or users will see fake data.
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
GameTokSDK.enableMock();
</script>Device testing (WebView inside the app): When you need logs, after loading the SDK call GameTokSDK.enableDebug() (returns a Promise; loads vConsole on the page and logs native bridge traffic). Do not use together with enableMock.
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
GameTokSDK.enableDebug();
</script>1.2 Usage examples
All examples use then / catch with Promises. On success, a unified response object is returned; on failure, the Promise rejects.
1.2.1 Get profile
// Example call
GameTokSDK.getProfile()
.then(({ action, error, data }) => {
console.log('Profile loaded:', data);
})
.catch((e) => {
console.error('Failed to get profile:', e);
});Sample response (JSON):
{
"action": "GET_PROFILE",
"error": false,
"data": {
"uid": 1024780,
"avatar": "https://game-load-sa.lobah.net/avatar/1.jpg",
"userName": "Guest",
"userCoins": 0,
"level": 0,
"gameLevel": 0,
"rankImg": "",
"gender": 0,
"testAccount": false,
"guest": true
}
}Key fields:
| Field | Type | Description |
|---|---|---|
| uid | number | User unique ID |
| avatar | string | Avatar URL |
| userName | string | Username / display name |
| userCoins | number | User's current coin balance |
| level | number | User growth level (account system) |
| gameLevel | number | Rank (based on game matches / ranked play); 0 means no rank (e.g., new player, has not participated in ranked) |
| rankImg | string | Rank image URL; empty string when the user has no rank |
| gender | number | Gender enum: 0 = unknown, 1 = male, 2 = female |
| testAccount | boolean | Whether a test account / platform review account |
| guest | boolean | Whether a guest (not logged in to a real account) |
1.2.2 Batch get user profiles
Use this for leaderboards, friend lists, match settlement, or any scenario that needs to fetch basic info for multiple users at once, avoiding high‑frequency looping calls to getProfile.
/**
* Batch get user profiles
* @param uids Required; array of uids to query (recommended: 1–50 per call)
*/
GameTokSDK.getProfiles({ uids: [1234567, 1234568, 1234569] })
.then(({ action, error, data }) => {
console.log('Batch fetch succeeded:', data);
// data is a Profile array; iterate or map by uid directly
const map = new Map(data.map(p => [p.uid, p]));
console.log('1234567 ->', map.get(1234567));
})
.catch((e) => {
console.error('Batch fetch failed:', e);
});Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
| uids | number[] | Yes | List of uids to query; recommended length 1–50. If empty array, non-array, or contains invalid elements (e.g. "abc", null, negative numbers, decimals), SDK directly reject(SDKError), error code INVALID_PARAMS = 1003, will not send to native |
Sample response (JSON):
{
"action": "GET_PROFILES",
"error": false,
"data": [
{
"uid": 1024780,
"avatar": "https://game-load-sa.lobah.net/avatar/1.jpg",
"userName": "Guest",
"userCoins": 0,
"level": 0,
"gameLevel": 0,
"rankImg": "",
"gender": 0,
"testAccount": false,
"guest": true
},
{
"uid": 1024781,
"avatar": "https://game-load-sa.lobah.net/avatar/2.jpg",
"userName": "Alice",
"userCoins": 200,
"level": 3,
"gameLevel": 5,
"rankImg": "https://game-load-sa.lobah.net/rank/5.png",
"gender": 2,
"testAccount": false,
"guest": false
}
]
}1.2.3 In‑app purchase (e.g., buy an item)
/**
* In‑app purchase
* @param productId Product ID; must be defined beforehand in the developer console (https://developer.lobah.net/)
*/
GameTokSDK.purchase({ productId: 'HAB.WATER.10.COINS' })
.then(({ data }) => {
// Success vs failure must be determined from the returned code here
console.log('Purchase finished:', data);
})
.catch((e) => {
console.error('Error:', e);
});Sample response (JSON):
{
"action": "PURCHASE",
"error": false,
"data": {
"purchaseResultCode": 0,
"testAccount": false,
"userBalance": 22514
}
}purchaseResultCode values:
0: Purchase succeeded11: Invalidproduct_id12: Insufficient user coins13: Duplicate or invalidreference_id14: User is a guest; guests cannot purchase16: Invalidproduct_id20: Other error
1.2.4 Store key‑value
/**
* Store a key‑value pair
* @param key Custom key
* @param value Value; may be a string or an object
*/
GameTokSDK.storageSet({ key: 'settings', value: { theme: 'dark', volume: 0.8 } })
.then(() => {
console.log('Stored successfully');
})
.catch((e) => {
console.error('Store failed:', e);
});Sample response (JSON):
{
"action": "STORAGE_SET",
"error": false,
"data": null
}1.2.5 Read key‑value
/**
* Read a key‑value pair
* @param key Predefined key to read
*/
GameTokSDK.storageGet({ key: 'settings' })
.then((result) => {
console.log('Read OK:', result.data.value); // object or string
})
.catch((e) => {
console.error('Read failed:', e);
});Sample response (JSON):
{
"action": "STORAGE_GET",
"error": false,
"data": {
"value": {
"theme": "dark",
"volume": 0.8
}
}
}1.2.6 Report score
/**
* Depending on the game, you may report score, stage, or level.
* For score: { score: 300, scoreType: 'score', remark: 'score' }
* For level: { score: 1, scoreType: 'level', remark: 'level' }
* @param score Value to report; must be a positive integer
* @param scoreType Category (business‑defined label for the uploaded data)
* @param remark Optional note (may match scoreType)
*/
GameTokSDK.addScore({ score: 10, scoreType: 'score', remark: 'score' })
.then(() => {
console.log('Score reported');
})
.catch((e) => {
console.error('Report failed:', e);
});Sample response (JSON):
{
"action": "ADD_SCORE",
"error": false,
"data": null
}1.2.7 Microphone permission
/**
* Request microphone permission (Android)
* Use in‑game to prompt the user for mic access (e.g., voice features).
* No return value; only triggers the native permission UI.
*/
GameTokSDK.getPermissionMic();1.2.8 Top up (TOPUP)
Important: User cancel, payment failure, etc. still resolve via then. In then, check data.code for success. catch is only for SDK unavailability, timeouts, and similar errors.
/**
* Top up
* @param amount Amount (units as agreed with the client)
*/
GameTokSDK.topup({ amount: 100 })
.then(({ action, error, data }) => {
const code = data && data.code;
if (code === 200) {
console.log('Top‑up succeeded:', data);
} else {
console.warn('Top‑up incomplete or failed, code:', code, data);
}
})
.catch((e) => {
console.error('Top‑up request error (e.g., SDK unavailable):', e);
});Sample response (JSON, success):
{
"action": "TOPUP",
"error": false,
"data": {
"code": 200,
"message": "ok"
}
}
code values:
200 Success
401 Top-up failed (error)
402 Top-up failed (user cancelled)1.2.9 Round start (ROUND_START)
Call once after a round actually starts. round_id is required (unique ID for this round, generated by the game). Optional timestamp (ms; defaults to now if omitted). No Promise return — fire‑and‑forget.
/**
* Round start notification
* @param round_id Unique ID for this round
* @param timestamp Optional start time (ms)
*/
GameTokSDK.roundStart({
round_id: 'round-' + Date.now(),
timestamp: Date.now(),
});If round_id is missing, the SDK logs a warning and does not send to native.
1.2.10 Round end (ROUND_END)
Call when the round ends. round_id must match this round's roundStart. Optional timestamp. No Promise return.
/**
* Round end notification
* @param round_id Same as this round's roundStart
* @param timestamp Optional end time (ms)
*/
const roundId = 'round-' + Date.now();
GameTokSDK.roundStart({ round_id: roundId });
// ... round logic ...
GameTokSDK.roundEnd({ round_id: roundId, timestamp: Date.now() });1.2.11 Get coupon target score (GET_COUPON_TARGET_SCORE)
During game start or runtime, the game can query the platform client for the target score required to show the coupon dialog. After the client returns the score, the game uses it to decide whether to call showCouponDialog.
/**
* Get coupon target score
* Usually no extra parameters required
*/
GameTokSDK.getCouponTargetScore()
.then(({ action, error, data }) => {
console.log('Coupon target score:', data.targetScore);
// When in-game score reaches data.targetScore, call showCouponDialog
})
.catch((e) => {
console.error('Failed to get coupon target score:', e);
});Sample response (JSON):
{
"action": "GET_COUPON_TARGET_SCORE",
"error": false,
"data": {
"targetScore": 5000
}
}Field reference:
| Field | Type | Description |
|---|---|---|
| targetScore | number | Target score threshold for showing the coupon dialog |
1.2.12 Show coupon dialog (SHOW_COUPON_DIALOG)
When the game needs to guide users to claim or use a coupon, call this API to notify the platform client to show a coupon dialog. The dialog is displayed by the platform in the game container layer; the game does not need to handle dialog UI logic. No Promise return — fire‑and‑forget.
/**
* Show coupon dialog
* @param coupon_id Optional coupon ID; if omitted, the platform decides what to show
* @param scene Optional trigger scene (business-defined, e.g. round_end, level_up)
*/
GameTokSDK.showCouponDialog({
coupon_id: 'coupon-001',
scene: 'round_end',
});
// Or call without parameters to show the platform default coupon
GameTokSDK.showCouponDialog();Sample payload (JSON):
{
"action": "SHOW_COUPON_DIALOG",
"data": {
"coupon_id": "coupon-001",
"scene": "round_end"
}
}1.2.13 Audio event listeners (onAudioSuspend / offAudioSuspend / onAudioResume / offAudioResume)
When the platform requires the game to mute (e.g., the user enters a live room, a system call comes in, etc.), an audio suspend event is pushed to the game; when sound is allowed to resume, an audio resume event is pushed. The game should promptly mute/unmute all sound effects and background music upon receiving these events.
This is an event listener interface, not a Promise interface, and has no return value.
// Listen for audio suspend event
function handleAudioSuspend(payload) {
console.log('Mute notification received:', payload);
// Mute all sound effects and background music in the game
myGame.muteAll();
}
GameTokSDK.onAudioSuspend(handleAudioSuspend);
// Listen for audio resume event
function handleAudioResume(payload) {
console.log('Resume sound notification received:', payload);
// Restore all sound effects and background music in the game
myGame.unmuteAll();
}
GameTokSDK.onAudioResume(handleAudioResume);Remove listeners:
// Remove audio suspend listener (pass the same function reference used when registering)
GameTokSDK.offAudioSuspend(handleAudioSuspend);
// Remove audio resume listener
GameTokSDK.offAudioResume(handleAudioResume);options.sync parameter (state synchronization):
onAudioSuspend and onAudioResume support a second optional options parameter, where sync (default true) handles the case where the listener is registered after the event has already fired:
sync: true(default) — If audio is already in a suspended/resumed state when the listener is registered, the callback will be called once immediately in the next microtask, ensuring the game does not miss previous state changes.sync: false— Only listens for subsequent new event pushes; no state sync on registration.
// Default sync: true — if audio is already suspended, callback fires immediately once
GameTokSDK.onAudioSuspend((payload) => {
myGame.muteAll();
});
// Disable state sync, only listen for subsequent events
GameTokSDK.onAudioSuspend((payload) => {
myGame.muteAll();
}, { sync: false });Recommended usage: Register listeners as early as possible during game initialization and keep sync: true (default), so the game can correctly sync state even if the platform has already sent a mute instruction before registration.
1.2.14 Game control event listeners (onAutoStartGame / offAutoStartGame / onExitAndResetGame / offExitAndResetGame)
The client can push two types of control commands to the game (client => H5):
- AUTO_START_GAME: The client notifies the game to start automatically, with the same effect as clicking the in-game start button.
- EXIT_AND_RESET_GAME: The client notifies the game to end and return to the initial state.
This is an event listener interface, not a Promise interface, and has no return value. Unlike audio events, these are instantaneous commands (not persistent state), so options.sync state compensation is not provided — register listeners as early as possible during game initialization to avoid missing commands.
// Listen for "start game" command
function handleAutoStart(payload) {
console.log('Start game command received:', payload);
myGame.start(); // same as clicking the in-game start button
}
GameTokSDK.onAutoStartGame(handleAutoStart);
// Listen for "exit and reset game" command
function handleExitReset(payload) {
console.log('Exit and reset command received:', payload);
myGame.exitAndReset(); // end current round and return to initial state
}
GameTokSDK.onExitAndResetGame(handleExitReset);Remove listeners:
// Pass the same function reference used when registering
GameTokSDK.offAutoStartGame(handleAutoStart);
GameTokSDK.offExitAndResetGame(handleExitReset);Listen once:
GameTokSDK.onceAutoStartGame((payload) => {
myGame.start();
});
GameTokSDK.onceExitAndResetGame((payload) => {
myGame.exitAndReset();
});Client push message format:
{ "action": "AUTO_START_GAME" }{ "action": "EXIT_AND_RESET_GAME" }1.2.15 Book a highlight event (bookHQ / onBookHQ)
Used to book a highlight event (HQ). Calling bookHQ() sends the booking request to the client. Upon receiving it, the client may prompt login first (SMS verification code / third-party authorization, etc.); the whole flow can be long and unpredictable in duration. Once done, the result is sent back via the onBookHQ callback.
Uses "send + event listener" instead of a Promise: Because the login flow is long and its duration is uncertain, bookHQ() is designed as fire-and-forget (no timeout, no waiting for the callback), and the result is delivered asynchronously through onBookHQ. This way, even a very long login flow will not be interrupted by a timeout.
There are just two steps: register the onBookHQ listener first, then call bookHQ to send.
/**
* 1. Register the callback first (the client login can take as long as it needs; no timeout)
* payload is a normalized object; success check: payload.success === true
*/
GameTokSDK.onBookHQ(function (payload) {
if (payload && payload.success === true) {
console.log('Booking succeeded');
// Update UI to "Booked"
} else {
console.log('Booking failed / user cancelled; can retry');
// Restore UI to clickable
}
});
/**
* 2. Send when the user taps "Book" (non-blocking, no waiting)
* @param pkId Required, event id (number or numeric string)
* @param scene Optional, scene identifier; defaults to "kcSwiper"
* @returns boolean — whether the bridge is available (whether the message was sent / enqueued)
*/
GameTokSDK.bookHQ({ pkId: 123, scene: 'kcSwiper' });If you just want to "send a booking and don't care about the result", you can call
bookHQ()alone without registeringonBookHQ. There are also two companion methods:offBookHQ(handler)(remove listener) andonceBookHQ(handler)(listen once). They are rarely needed — activity pages usually register once during initialization.
Sample payload (JSON):
{
"action": "bookHQ",
"data": {
"pkId": 123,
"scene": "kcSwiper"
}
}Client callback payload (what the onBookHQ handler receives, normalized on both ends):
{ "success": true }Field reference:
| Field | Type | Description |
|---|---|---|
| success | boolean | Whether the booking succeeded; true means success, false / missing means failure or cancellation |
Note: The envelopes returned by the client differ slightly between platforms (iOS carries the payload in
contentas a JSON string, Android indataas an object). The SDK automatically normalizes this, so theonBookHQcallback always receives a structured object; the business side only needs to checkpayload.success.
Parameters and response shape
- Typical call parameters (reference; see each API for exact fields):
- getProfile: `` (usually no extra parameters)
- getProfiles:
{ uids: number[] }(required, non-empty array; returnsProfile[], same structure asgetProfile.datain array form) - purchase:
{ productId: string } - storageSet:
{ key: string, value: any } - storageGet:
{ key: string } - addScore:
{ score: number, scoreType: string } - getPermissionMic: `` (usually no extra parameters)
- topup:
{ amount: number, ... }(other fields per client protocol) - roundStart / roundEnd:
{ round_id: string, timestamp?: number } - getCouponTargetScore: `` (usually no extra parameters)
- showCouponDialog:
{ coupon_id?: string, scene?: string } - onAudioSuspend / onAudioResume:
(handler: Function, options?: { sync?: boolean }) - offAudioSuspend / offAudioResume:
(handler: Function) - onAutoStartGame / onExitAndResetGame:
(handler: Function) - offAutoStartGame / offExitAndResetGame:
(handler: Function) - bookHQ:
{ pkId: number|string, scene?: string }(fire-and-forget, returns boolean; result delivered via theonBookHQcallback) - onBookHQ:
(handler: Function)(handler receives{ success: boolean, ... };offBookHQ/onceBookHQare companion methods, rarely needed)
- Successful responses share this shape:
{
action: string; // Action name for this call (e.g. 'GET_PROFILE')
error: false; // false on success (on failure the Promise rejects)
data: any; // Native payload; fields depend on the action
}- Failure behavior:
topup: Most business outcomes (success/failure/cancel) resolve inthenviadata.code;catchis for SDK errors and timeouts.- Other Promise interfaces: On failure the Promise rejects with an
Error; handle in.catch(log, retry, or show UI).
Important notes
Do not overwrite or remove these globals, or the SDK will break:
window.GameTokSDK(main SDK entry)
Example (do not do this):
// Dangerous — breaks iOS / Android bridge
window.GameTokSDK = {};
window.GameTokSDK = null;
delete window.GameTokSDK;Full example
// Get user profile
GameTokSDK.getProfile({})
.then((resp) => {
console.log('Profile:', resp.data);
})
.catch((e) => {
console.error('Failed to get profile:', e);
});
// Batch get user profiles (for leaderboards, friend lists, etc.)
GameTokSDK.getProfiles({ uids: [10001, 10002, 10003] })
.then(({ data }) => {
const map = new Map(data.map(p => [p.uid, p]));
console.log('10001 profile:', map.get(10001));
})
.catch((e) => {
console.error('Batch fetch failed:', e);
});
// Store
GameTokSDK.storageSet({ key: 'config', value: { lang: 'en' } })
.then(() => {
console.log('Stored successfully');
})
.catch((e) => {
console.error('Store failed:', e);
});
// Read
GameTokSDK.storageGet({ key: 'config' })
.then((resp) => {
console.log('Config:', resp.data.value);
})
.catch((e) => {
console.error('Read failed:', e);
});
// Purchase
GameTokSDK.purchase({ productId: 'HAB.WATER.10.COINS' })
.then((resp) => {
console.log('Purchase:', resp.data);
})
.catch((e) => {
console.error('Purchase failed:', e);
});
// Score
GameTokSDK.addScore({ score: 5, scoreType: 'score' })
.then(() => {
console.log('Score added');
})
.catch((e) => {
console.error('Score report failed:', e);
});
// Microphone (Android, no return value)
GameTokSDK.getPermissionMic();
// Top up: check data.code in then (200 = success)
GameTokSDK.topup({ amount: 100 })
.then(({ data }) => {
if (data && data.code === 200) {
console.log('Top-up succeeded', data);
} else {
console.log('Top-up not successful', data);
}
})
.catch((e) => console.error('Top-up error', e));
// Round notifications (same round_id)
const rid = 'r-' + Date.now();
GameTokSDK.roundStart({ round_id: rid });
GameTokSDK.roundEnd({ round_id: rid });
// Get coupon target score, then show dialog when reached
GameTokSDK.getCouponTargetScore()
.then(({ data }) => {
console.log('Coupon target score:', data.targetScore);
if (myGame.score >= data.targetScore) {
GameTokSDK.showCouponDialog({ scene: 'score_reached' });
}
})
.catch((e) => console.error('Failed to get coupon target score', e));
// Audio event listeners (register during game initialization)
GameTokSDK.onAudioSuspend((payload) => {
console.log('Mute', payload);
myGame.muteAll();
});
GameTokSDK.onAudioResume((payload) => {
console.log('Resume sound', payload);
myGame.unmuteAll();
});
// Book a highlight event: register the callback first, then send (fire-and-forget, no timeout)
GameTokSDK.onBookHQ((payload) => {
if (payload && payload.success === true) {
console.log('Booking succeeded');
} else {
console.log('Booking failed / cancelled; can retry');
}
});
GameTokSDK.bookHQ({ pkId: 123, scene: 'kcSwiper' });