Skip to content

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:

ActionDescription
GET_PROFILEGet the current user profile
GET_PROFILESBatch‑get user profiles (by uid array)
PURCHASEInitiate a purchase (e.g., in‑app purchase)
STORAGE_SETWrite to local key‑value storage (settings, progress, etc.)
STORAGE_GETRead from local key‑value storage (retrieve previously saved data)
ADD_SCOREReport a score (leaderboards, sharing, etc.)
GET_PERMISSION_MICRequest microphone permission (Android)
TOPUPTop up when in‑game currency is low; opens the client top‑up flow
ROUND_STARTRound start notification (call once per round start; tied to platform round/energy policies)
ROUND_ENDRound end notification (call when a round ends; round_id must match this round's ROUND_START)
GET_COUPON_TARGET_SCOREGet the target score for showing a coupon dialog (the client returns the specific score value)
SHOW_COUPON_DIALOGNotify the platform to show a coupon dialog (displayed by the client in the game container layer)
bookHQBook a highlight event (send a booking request; the client may prompt login first)
onBookHQListen for the bookHQ booking result callback
onAudioSuspendListen for audio suspend event (triggered when the platform requires the game to mute)
offAudioSuspendRemove audio suspend event listener
onAudioResumeListen for audio resume event (triggered when the platform allows the game to restore sound)
offAudioResumeRemove audio resume event listener
onAutoStartGameListen for client "start game" command (same effect as clicking the in-game start button)
offAutoStartGameRemove "start game" command listener
onExitAndResetGameListen for client "exit and reset game" command (end the game and return to initial state)
offExitAndResetGameRemove "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>

html
<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.

html
<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.

html
<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

javascript
// 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):

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:

FieldTypeDescription
uidnumberUser unique ID
avatarstringAvatar URL
userNamestringUsername / display name
userCoinsnumberUser's current coin balance
levelnumberUser growth level (account system)
gameLevelnumberRank (based on game matches / ranked play); 0 means no rank (e.g., new player, has not participated in ranked)
rankImgstringRank image URL; empty string when the user has no rank
gendernumberGender enum: 0 = unknown, 1 = male, 2 = female
testAccountbooleanWhether a test account / platform review account
guestbooleanWhether 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.

javascript
/**
 * 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:

FieldTypeRequiredDescription
uidsnumber[]YesList 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):

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)

javascript
/**
 * 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):

json
{
  "action": "PURCHASE",
  "error": false,
  "data": {
    "purchaseResultCode": 0,
    "testAccount": false,
    "userBalance": 22514
  }
}

purchaseResultCode values:

  • 0: Purchase succeeded
  • 11: Invalid product_id
  • 12: Insufficient user coins
  • 13: Duplicate or invalid reference_id
  • 14: User is a guest; guests cannot purchase
  • 16: Invalid product_id
  • 20: Other error

1.2.4 Store key‑value

javascript
/**
 * 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):

json
{
  "action": "STORAGE_SET",
  "error": false,
  "data": null
}

1.2.5 Read key‑value

javascript
/**
 * 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):

json
{
  "action": "STORAGE_GET",
  "error": false,
  "data": {
    "value": {
        "theme": "dark",
        "volume": 0.8
    }
  }
}

1.2.6 Report score

javascript
/**
 * 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):

json
{
  "action": "ADD_SCORE",
  "error": false,
  "data": null
}

1.2.7 Microphone permission

javascript
/**
 * 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.

javascript
/**
 * 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):

json
{
  "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.

javascript
/**
 * 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.

javascript
/**
 * 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.

javascript
/**
 * 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):

json
{
  "action": "GET_COUPON_TARGET_SCORE",
  "error": false,
  "data": {
    "targetScore": 5000
  }
}

Field reference:

FieldTypeDescription
targetScorenumberTarget 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.

javascript
/**
 * 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):

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.

javascript
// 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:

javascript
// 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.
javascript
// 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.

javascript
// 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:

javascript
// Pass the same function reference used when registering
GameTokSDK.offAutoStartGame(handleAutoStart);
GameTokSDK.offExitAndResetGame(handleExitReset);

Listen once:

javascript
GameTokSDK.onceAutoStartGame((payload) => {
  myGame.start();
});
GameTokSDK.onceExitAndResetGame((payload) => {
  myGame.exitAndReset();
});

Client push message format:

json
{ "action": "AUTO_START_GAME" }
json
{ "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.

javascript
/**
 * 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 registering onBookHQ. There are also two companion methods: offBookHQ(handler) (remove listener) and onceBookHQ(handler) (listen once). They are rarely needed — activity pages usually register once during initialization.

Sample payload (JSON):

json
{
  "action": "bookHQ",
  "data": {
    "pkId": 123,
    "scene": "kcSwiper"
  }
}

Client callback payload (what the onBookHQ handler receives, normalized on both ends):

json
{ "success": true }

Field reference:

FieldTypeDescription
successbooleanWhether 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 content as a JSON string, Android in data as an object). The SDK automatically normalizes this, so the onBookHQ callback always receives a structured object; the business side only needs to check payload.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; returns Profile[], same structure as getProfile.data in 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 the onBookHQ callback)
    • onBookHQ: (handler: Function) (handler receives { success: boolean, ... }; offBookHQ / onceBookHQ are companion methods, rarely needed)
  • Successful responses share this shape:
typescript
{
  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 in then via data.code; catch is 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):

javascript
// Dangerous — breaks iOS / Android bridge
window.GameTokSDK = {};
window.GameTokSDK = null;
delete window.GameTokSDK;

Full example

javascript
// 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' });

Swipe & Play Endless Game Together