跳转到内容

SDK

本 SDK 提供 游戏 与原生客户端(iOS、Android)之间的安全双向通信能力,帮助第三方开发者调用原生能力。

暴露的全局 API 名称:window.GameTokSDK

提供能力:

动作说明
GET_PROFILE获取当前用户资料
GET_PROFILES批量获取用户资料(按 uid 数组)
PURCHASE发起购买(如内购)
STORAGE_SET写入本地键值存储(用于保存设置、进度等)
STORAGE_GET读取本地键值存储(获取先前保存的数据)
ADD_SCORE上报分数(用于排行榜/分享等)
GET_PERMISSION_MIC获取麦克风权限(安卓)
TOPUP充值,游戏中金币不足,可以调起客户端充值
ROUND_START开局通知(每局开始时上报,与平台局次、能量等策略相关)
ROUND_END局末通知(每局结束时上报,round_id 须与本局 ROUND_START 一致)
GAME_START开局通知 · 干净通道(与 ROUND_START 同义、payload 相同,不带体力语义)
GAME_END局末通知 · 干净通道(与 ROUND_END 同义,round_id 须与本局 GAME_START 一致)
GET_COUPON_TARGET_SCORE获取弹出优惠券弹框的目标分数(由客户端返回具体分数值)
SHOW_COUPON_DIALOG通知平台展示优惠券弹框(由客户端在游戏容器层展示)
SHOW_LEADERBOARD通知平台展示排行榜(由客户端在游戏容器层展示,fire-and-forget)
SHOW_ENERGY_INSUFFICIENT_DIALOG体力(能量)不足,通知平台展示体力不足弹窗(fire-and-forget)
BRAND_ACTIVE_DETAIL_PAGE跳转品牌活动详情页(游戏侧发起,客户端跳转,fire-and-forget)
BRAND_DT_DETAIL_PAGE跳转品牌 DT 详情页(游戏侧发起,客户端跳转,fire-and-forget)
bookHQ预约高光赛事(发送预约请求,客户端可能先拉起登录)
onBookHQ监听 bookHQ 预约结果回调
onAudioSuspend监听音频暂停事件(平台要求游戏静音时触发)
offAudioSuspend移除音频暂停事件监听
onAudioResume监听音频恢复事件(平台允许游戏恢复声音时触发)
offAudioResume移除音频恢复事件监听
onAutoStartGame监听客户端“开始游戏”指令(效果等同点击游戏内 start 按钮)
offAutoStartGame移除“开始游戏”指令监听
onExitAndResetGame监听客户端“结束并重置游戏”指令(游戏结束并回到初始状态)
offExitAndResetGame移除“结束并重置游戏”指令监听
REQUEST_EXIT游戏自带「返回首页」按钮被点击,通知平台回首页(fire-and-forget,平台随后下发 EXIT_AND_RESET_GAME)
SHOW_MATCH_RESULTKC League 对局结果通知:把平台服务端返回的结算结果原样交给平台客户端展示结算弹窗(fire-and-forget)
SHOW_WPP_RESULTWPP 场景结果通知:把结果内容原样交给平台客户端展示结果弹窗(fire-and-forget,SDK 不校验不解析)

集成示例

以下示例展示如何在页面中引入 SDK 并调用各项能力。

1.1 引入代码

方式一:通过 <script> 直接引入打包产物

html
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
  console.log('GameTokSDK version loaded:', !!window.GameTokSDK);
</script>

本地开发(未嵌入 App、在浏览器里调试):在引入 SDK 后调用 **GameTokSDK.enableMock()** 即可,Promise 类接口会走内置模拟数据,无需原生客户端。正式发布或提审包中请勿调用,否则用户会看到虚假数据。

html
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
  GameTokSDK.enableMock();
</script>

真机测试(已嵌入 App 的 WebView):需要看日志时,在引入 SDK 后调用 **GameTokSDK.enableDebug()**(返回 Promise,会在页面加载 vConsole,并打印与原生通信相关日志)。不要与 enableMock 同时使用。

html
<script src="https://play.letskix.com/res/game/sdk-js/GameTokSDK.js"></script>
<script>
  GameTokSDK.enableDebug();
</script>

1.2 调用示例

以下示例统一采用 Promise 的 then/catch 调用方式。成功时返回统一响应对象,失败时通过 Promise.reject 抛出错误。

1.2.1 获取个人资料

javascript
// 调用示例
GameTokSDK.getProfile()
        .then(({ action, error, data }) => {
          console.log('获取个人资料成功:', data);
        })
        .catch((e) => {
          console.error('获取个人资料失败:', e);
        });

返回示例(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
  }
}

字段说明(重点):

字段类型说明
uidnumber用户唯一 ID
avatarstring头像 URL
userNamestring用户昵称
userCoinsnumber用户当前金币余额
levelnumber用户成长等级(账号体系)
gameLevelnumber段位(基于游戏对战 / 排位);0 表示没有段位(如新人、未参与排位)
rankImgstring段位图片地址;无段位时为空字符串
gendernumber性别枚举:0 = 未知,1 = 男,2 = 女
testAccountboolean是否测试账号 / 平台审核账号
guestboolean是否游客(未登录正式账号)

1.2.2 批量获取用户资料

适用于排行榜、好友列表、对局结算等需要一次性拉取 多名用户 基本信息的场景,避免对 getProfile 高频循环调用。

javascript
/**
 * 批量获取用户资料
 * @param uids  必填,待查询的 uid 数组(建议单次 1 ~ 50 个)
 */
GameTokSDK.getProfiles({ uids: [1234567, 1234568, 1234569] })
        .then(({ action, error, data }) => {
          console.log('批量获取成功:', data);
          // data 就是 Profile 数组,可直接遍历或按 uid 自行映射
          const map = new Map(data.map(p => [p.uid, p]));
          console.log('1234567 ->', map.get(1234567));
        })
        .catch((e) => {
          console.error('批量获取失败:', e);
        });

参数说明:

字段类型必填说明
uidsnumber[]待查询的 uid 列表,长度建议 1 ~ 50。为空数组、非数组或包含非法元素(如 "abc"null、负数、小数)时,SDK 直接 reject(SDKError),错误码 INVALID_PARAMS = 1003不会 向原生发送

返回示例(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 游戏内购(例如购买道具)

javascript
/**
 * 游戏内购
 * @param productId   商品ID,需要在开发者后台(https://developer.lobah.net/)提前定义好
 */
GameTokSDK.purchase({ productId: 'HAB.WATER.10.COINS' })
        .then(({ data }) => {
          //需要注意,购买成功和失败需要根据这儿得返回值code码来确定  
          console.log('购买完成:', data);
        })
        .catch((e) => {
          console.error('错误捕获:', e);
        });

返回示例(JSON):

json
{
  "action": "PURCHASE",
  "error": false,
  "data": {
    "purchaseResultCode": 0, //结果码"
    "testAccount":false,     //是否测试账号,或平台审核人员账号
    "userBalance":22514      //用户的金币余额
  }
}


purchaseResultCode 的结果如下:

0: 购买成功
11: product_id 不正确
12: 用户金币不足
13: 重复或无效的 reference_id
14: 用户是游客,游客不能进行购买操作
16: product_id 不正确
20: 其他错误

1.2.4 存储键值

javascript
/**
 * 存储键值
 * @param key   需要存储的自定义键值
 * @param value 需要存储的值 可以是字符串,也可以是对象
 */
GameTokSDK.storageSet({ key: 'settings', value: { theme: 'dark', volume: 0.8 } })
        .then(() => {
          console.log('存储成功');
        })
        .catch((e) => {
          console.error('存储失败:', e);
        });

返回示例(JSON):

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

1.2.5 读取键值

javascript
/**
 * 读取键值
 * @param key   需要读取的定义好的键值
 */
GameTokSDK.storageGet({ key: 'settings' })
        .then((result) => {
          console.log('读取成功:', result.data.value); // 可能是对象或字符串
        })
        .catch((e) => {
          console.error('读取失败:', e);
        });

返回示例(JSON):

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

1.2.6 上报分数

javascript
/**
 * 对于不同的游戏,可能需要上报的是分数,也可能是关卡、等级
 * 如果是分数,就可以这样传{score:300,scoreType:'score',remark:'score'}
 * 如果是等级,就可以传 {score:1,scoreType:'level',remark:'level'}
 * @param score     需要上报的值 这个值需要为正整数
 * @param scoreType 数据类别(由业务自定义,作为上传数据的说明)
 * @param remark    自定义说明 (可以和scoreType一致)
 */

GameTokSDK.addScore({ score: 10, scoreType: 'score',remark:'score' })
        .then(() => {
          console.log('加分成功');
        })
        .catch((e) => {
          console.error('加分失败:', e);
        });

返回示例(JSON):

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

1.2.7 获取麦克风权限

javascript
/**
 * 获取麦克风权限(安卓)
 * 用于在游戏中请求用户授予麦克风权限,以便使用语音相关功能。
 * 无返回值,仅触发原生端弹出权限请求。
 */
GameTokSDK.getPermissionMic();

1.2.8 充值(TOPUP)

重要: 用户取消支付、支付失败等结果 仍走 then,请在 then 里根据 **data.code** 判断是否成功。只有 SDK不可用、超时等异常才会进入 **catch**。

javascript
/**
 * 充值
 * @param amount 充值金额(具体单位与客户端约定一致)
 */
GameTokSDK.topup({ amount: 100 })
        .then(({ action, error, data }) => {
          const code = data && data.code;
          if (code === 200) {
            console.log('充值成功:', data);
          } else {
            console.warn('充值未完成或失败, code:', code, data);
          }
        })
        .catch((e) => {
          console.error('充值请求异常(如SDK不可用):', e);
        });

返回示例(JSON,成功):

json
{
  "action": "TOPUP",
  "error": false,
  "data": {
    "code": 200,
    "message": "ok"
  }
}

code 的值定义:
200 成功,
401 充值失败(充值异常)
402 充值失败(主动取消)

1.2.9 开局通知(ROUND_START)

每局游戏 正式开始后 调用一次。round_id 可选,建议不传:缺省时 SDK 自动生成全局唯一的对局 ID(round_ 前缀)并在内部记住,roundEnd() 会自动带出同一个值。可选 timestamp(毫秒时间戳,不传则使用当前时间)。无 Promise 返回值,发后即忘。

javascript
// 推荐用法:什么都不传,round_id 由 SDK 生成并自动关联本局
GameTokSDK.roundStart();

// 兼容用法:自行传入 round_id(须保证每局全局唯一、绝不跨局复用,
// 切勿使用 1、2、3 这类会话内递增序号——刷新页面后会与历史局撞号)
GameTokSDK.roundStart({ round_id: 'round-' + crypto.randomUUID() });

需要读取当前局 ID 时(如传给 showEnergyInsufficientDialog 归因):

javascript
const roundId = GameTokSDK.getCurrentRoundId(); // roundStart 后有值,roundEnd 后为 null

上一局未 roundEnd 又再次 roundStart 时,视为开新局,SDK 直接覆盖当前局 ID(旧局按未正常结束处理,平台侧有容错)。

1.2.10 局末通知(ROUND_END)

每局 结束后(输/赢/超时都算结束)调用。round_id 可选:缺省自动带出与本局 roundStart 相同的值;自行传入时须与开局一致。可选 timestamp无 Promise 返回值

javascript
// 推荐用法:与 roundStart() 配对,全程不管 round_id
GameTokSDK.roundStart();
// ... 本局逻辑 ...
GameTokSDK.roundEnd();

// 兼容用法:显式传入(须与本局 roundStart 的 round_id 相同)
GameTokSDK.roundEnd({ round_id: myRoundId, timestamp: Date.now() });

若当前没有进行中的对局(从未 roundStart、或已经 roundEnd 过)且未显式传 round_id,SDK 会在控制台告警且 不会 向原生发送。

1.2.10a 开局通知 · 干净通道(GAME_START)

roundStart 同义、平级 —— 同样是「每局游戏正式开始后调用一次」,不是更高一层的会话。

为什么另开一条ROUND_START / ROUND_END 在平台侧已经绑定了体力扣除等业务逻辑,不便再改动;GAME_START / GAME_END语义相同但不携带任何体力 / 能量含义的干净通道,供后续业务挂载自己的逻辑。

payload 与 ROUND_START 逐字相同{ round_id, timestamp },ID 同为 round_ 前缀),只有 action 名不同 —— 原生侧已有的解析代码可直接复用。

ROUND_START / ROUND_ENDGAME_START / GAME_END
语义一局开始 / 一局结束完全相同
平台侧已绑定的业务体力扣除、局间拦截等无(干净)
payload{ round_id, timestamp }逐字相同
状态与查询getCurrentRoundId()getCurrentGameId()
是否会下线永久保留新增

两条通道各自独立:ID 各自生成、状态互不影响。同一局里若两条都调用,会得到两个不同的 round_id(靠 action 名区分)。

用法与 roundStart 逐条对应。round_id 可选,建议不传:缺省时 SDK 自动生成全局唯一的对局 ID(round_ 前缀)并在内部记住,gameEnd() 会自动带出同一个值。可选 timestamp(毫秒时间戳,不传则使用当前时间)。无 Promise 返回值,发后即忘。

javascript
// 推荐用法:什么都不传,round_id 由 SDK 生成并自动关联本局
GameTokSDK.gameStart();

// 兼容用法:自行传入 round_id(须保证每局全局唯一、绝不跨局复用,
// 切勿使用 1、2、3 这类会话内递增序号——刷新页面后会与历史局撞号)
GameTokSDK.gameStart({ round_id: 'round-' + crypto.randomUUID() });

需要读取当前局 ID 时:

javascript
const gameRoundId = GameTokSDK.getCurrentGameId(); // gameStart 后有值,gameEnd 后为 null

上一局未 gameEnd 又再次 gameStart 时,视为开新局,SDK 直接覆盖当前局 ID(旧局按未正常结束处理,平台侧有容错)。

1.2.10b 局末通知 · 干净通道(GAME_END)

每局 结束后(输/赢/超时都算结束)调用,与 roundEnd 同义。round_id 可选:缺省自动带出与本局 gameStart 相同的值;自行传入时须与开局一致。可选 timestamp无 Promise 返回值

javascript
// 推荐用法:与 gameStart() 配对,全程不管 round_id
GameTokSDK.gameStart();
// ... 本局逻辑 ...
GameTokSDK.gameEnd();

// 兼容用法:显式传入(须与本局 gameStart 的 round_id 相同)
GameTokSDK.gameEnd({ round_id: myRoundId, timestamp: Date.now() });

若当前没有进行中的对局(从未 gameStart、或已经 gameEnd 过)且未显式传 round_id,SDK 会在控制台告警且 不会 向原生发送。

gameEnd()roundEnd() 互不影响:调用 gameEnd() 不会结束 roundStart() 开的那一局,反之亦然。

1.2.11 获取优惠券目标分数(GET_COUPON_TARGET_SCORE)

游戏开局或运行过程中,可向平台客户端查询「弹出优惠券弹框」所需达到的目标分数。客户端返回具体分数值后,游戏侧据此判断是否调用 showCouponDialog

javascript
/**
 * 获取优惠券目标分数
 * 通常无需额外参数
 */
GameTokSDK.getCouponTargetScore()
        .then(({ action, error, data }) => {
          console.log('优惠券目标分数:', data.targetScore);
          // 游戏内分数达到 data.targetScore 时,可调用 showCouponDialog
        })
        .catch((e) => {
          console.error('获取优惠券目标分数失败:', e);
        });

返回示例(JSON):

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

字段说明:

字段类型说明
targetScorenumber弹出优惠券弹框的目标分数阈值

1.2.12 展示优惠券弹框(SHOW_COUPON_DIALOG)

当游戏内需要引导用户领取或使用优惠券时,调用此接口通知平台客户端弹出优惠券弹框。弹框由平台在游戏容器层展示,游戏侧无需处理弹框 UI 逻辑。无 Promise 返回值,发后即忘。

javascript
/**
 * 展示优惠券弹框
 * @param coupon_id 可选,优惠券 ID;不传时由平台决定展示内容
 * @param scene     可选,触发场景(业务自定义,如 round_end、level_up)
 */
GameTokSDK.showCouponDialog({
  coupon_id: 'coupon-001',
  scene: 'round_end',
});

// 也可不传参数,由平台展示默认优惠券
GameTokSDK.showCouponDialog();

发送示例(JSON):

json
{
  "action": "SHOW_COUPON_DIALOG",
  "data": {
    "coupon_id": "coupon-001",
    "scene": "round_end"
  }
}

1.2.12a 展示排行榜(SHOW_LEADERBOARD)

当游戏内需要引导用户查看排行榜时(如局末结算、主界面入口),调用此接口通知平台客户端展示排行榜。排行榜由平台在游戏容器层展示,游戏侧无需处理 UI 逻辑。无 Promise 返回值,发后即忘。

javascript
/**
 * 展示排行榜
 * @param scene 可选,触发场景(业务自定义,如 round_end、home)
 */
GameTokSDK.showLeaderboard({ scene: 'round_end' });

// 也可不传参数
GameTokSDK.showLeaderboard();

发送示例(JSON):

json
{
  "action": "SHOW_LEADERBOARD",
  "data": {
    "scene": "round_end"
  }
}

1.2.12b 体力不足弹窗(SHOW_ENERGY_INSUFFICIENT_DIALOG)

游戏通过 getEnergy() / onEnergyUpdate()(见 1.2.12c)获知体力不足(如 balance < cost_per_round,无法开新局)时,调用此接口请求平台在游戏容器层展示体力不足弹窗(内含签到领体力、去获取体力等引导)。游戏侧不判断体力规则、不处理弹窗 UI,只负责触发。无 Promise 返回值,发后即忘。

注意:平台自身也会在开局扣费、局末检测等时机主动弹这个弹窗,游戏侧调用是补充触发(典型场景:玩家点「开始」按钮但游戏已知体力不够时,主动请求弹窗而不是静默无响应)。

javascript
/**
 * 体力不足弹窗
 * @param scene    可选,触发场景(业务自定义,如 round_start、continue)
 * @param round_id 可选,关联的对局 ID,便于平台归因(可用 getCurrentRoundId() 取)
 */
GameTokSDK.showEnergyInsufficientDialog({
  scene: 'round_start',
  round_id: GameTokSDK.getCurrentRoundId() || undefined,
});

// 也可不传参数
GameTokSDK.showEnergyInsufficientDialog();

KC League 流程:开局前由游戏服务端向平台服务端 entry-check 校验,返回 canStart: false 时同样调用本接口,并把校验返回的字段原样附上(fire-and-forget 通道透传任意 JSON 字段,无需 SDK 改版)。调用后游戏停在封面、START 按钮恢复可点;玩家充值/签到后若平台希望直接开局,会下发 AUTO_START_GAME,游戏无需自行重试。

javascript
GameTokSDK.showEnergyInsufficientDialog({
  scene: 'kc_entry_check',
  energyBalance: 0,          // entry-check 返回的余额,原样带上
  blockReason: 'NO_ENERGY'   // entry-check 返回的枚举,原样带上
});

发送示例(JSON):

json
{
  "action": "SHOW_ENERGY_INSUFFICIENT_DIALOG",
  "data": {
    "scene": "round_start",
    "round_id": "round-1732000000-abc123"
  }
}

1.2.12c 体力查询与推送(getEnergy / onEnergyUpdate)

体力(能量)的扣除、弹窗、充值全部由平台完成,游戏只需要两件事:展示体力 HUD、体力不足时把「开始」按钮置灰。数据来源两条:

  • getEnergy()(请求-应答):初始化时主动查一次;
  • onEnergyUpdate(handler)(平台推送):每次体力变化(开局扣费、签到、充值、局末检测等)后平台推最新值,游戏据此刷新 HUD。

两者返回/推送同一数据结构

字段类型说明
balancenumber | null体力余额;体力功能未启用时为 null,游戏应隐藏体力 UI、按不限体力运行
charge_enabledboolean该游戏是否开启每局扣除
cost_per_roundnumber每局消耗体力数
reasonstring变化原因(仅推送带):initial / round_charge / round_end_check / checkin / recharge_check
seqnumber单调递增序号,消费方须按 seq 丢弃乱序旧值(推送不保证按序到达)
javascript
// 初始化:查一次 + 注册推送
const { data } = await GameTokSDK.getEnergy();
if (data.balance === null) {
  hideEnergyHud();           // 体力功能未启用,降级为不限体力
} else {
  renderEnergyHud(data.balance, data.cost_per_round);
}

let lastSeq = -Infinity;
GameTokSDK.onEnergyUpdate((payload) => {
  if (payload.seq <= lastSeq) return; // 丢弃乱序旧值
  lastSeq = payload.seq;
  renderEnergyHud(payload.balance, payload.cost_per_round);
  setStartButtonEnabled(payload.balance === null || payload.balance >= payload.cost_per_round);
});

最后值同步补偿:与音频事件相同,onEnergyUpdateoptions.sync 默认 true——注册时若 SDK 已缓存过体力值(来自更早的推送或 getEnergy() 应答),会异步补发一次,晚注册也不会错过当前状态;传 { sync: false } 关闭。另提供 offEnergyUpdate / onceEnergyUpdate(签名同音频事件),以及只读缓存:

javascript
const { last, seq } = GameTokSDK.getEnergyState(); // last 为最新体力数据(按 seq 单调),无数据时为 null

本地调试(Mock)enableMock()getEnergy() 返回内置模拟值 { balance: 100, charge_enabled: true, cost_per_round: 8 },可用自定义 handler 覆盖:

javascript
GameTokSDK.enableMock({ GET_ENERGY: () => ({ balance: 3, charge_enabled: true, cost_per_round: 8, seq: Date.now() }) });

ENERGY_UPDATE 推送来自宿主,Mock 模式没有;调试时可在控制台手动派发:

javascript
window.dispatchEvent(new MessageEvent('message', { data: JSON.stringify({
  action: 'ENERGY_UPDATE',
  data: { balance: 0, charge_enabled: true, cost_per_round: 8, reason: 'round_charge', seq: Date.now() }
})}));

1.2.12d 转盘 / 盲盒抽奖通道

仅适用于转盘、盲盒等「服务端先定结果,游戏只做演出」的模版。冲分游戏继续使用 roundStart / addScore / roundEnd

完整链路:

  1. 游戏用 getGameConfig()onGameConfig() 获得盘面;
  2. 玩家点击后,游戏先进入空转/蓄力,再调用 requestDraw()
  3. 平台扣体力并开奖,随后推送 DRAW_SETTLE
  4. 游戏调用自身的 settle(prize_id),动画落定后调用 notifyDrawResult()
  5. 平台弹奖励窗,关闭后通过 EXIT_AND_RESET_GAME 要求游戏 reset。
javascript
// 盘面配置:请求一次,并监听后续配置更新
const { data: config } = await GameTokSDK.getGameConfig();
renderPrizes(config.prizes);

GameTokSDK.onGameConfig((nextConfig) => {
  renderPrizes(nextConfig.prizes);
});

// 平台下发落点:这是瞬时事件,不补发旧结果
GameTokSDK.onDrawSettle(({ prize_id, config_version, draw_id }) => {
  if (config_version !== getCurrentConfigVersion()) {
    // 不要硬演旧配置;重新拉配置并由业务决定是否重试
    GameTokSDK.getGameConfig().then(({ data }) => renderPrizes(data.prizes));
    return;
  }
  game.settle(prize_id, () => {
    GameTokSDK.notifyDrawResult({ prize_id, draw_id });
  });
});

// 玩家点击:先空转,再通知平台扣体力+开奖
function spin() {
  game.spin(); // 无落点,可持续等待
  const roundId = GameTokSDK.requestDraw();
  console.log('draw round:', roundId);
}

// 扣费失败、超时或平台奖励窗关闭后,平台要求回待机
GameTokSDK.onExitAndResetGame(() => game.reset());

// 游戏自带「返回首页」按钮:通知平台即可,reset 仍等上面的 EXIT_AND_RESET_GAME(见 1.2.14a)
function onHomeButton() {
  GameTokSDK.requestExit({ state: game.state });
}

API:

API方向说明
getGameConfig(params?)游戏→平台(请求-应答)获取 { config_version, game_type, activity_id, prizes[] }
onGameConfig(handler, options?)平台→游戏配置推送;sync 默认 true,晚注册补当前配置
offGameConfig / onceGameConfig取消 / 一次性监听
getGameConfigState()SDK 本地读取最后配置 { last }
requestDraw({ round_id?, timestamp? })游戏→平台点击时请求扣费+开奖;返回实际 round_id
onDrawSettle(handler)平台→游戏收到 { prize_id, config_version, draw_id?, round_id? } 后落点
offDrawSettle / onceDrawSettle取消 / 一次性监听;不补发旧结果
notifyDrawResult({ prize_id, draw_id?, round_id? })游戏→平台动画完全落定,平台可弹奖励窗

getEnergy() 的响应若内嵌 game_config,SDK 会自动写入同一份配置缓存;晚注册的 onGameConfig 也能同步拿到。

正式开奖禁止使用客户端 weightprize_id 必须来自平台,且应存在于当前 prizes[].id。转盘/盲盒主链路不使用 addScoregetCouponTargetScore 或达标 showCouponDialog

Mock:

javascript
GameTokSDK.enableMock(); // getGameConfig() 内置返回 6 格转盘配置

1.2.13 音频事件监听(onAudioSuspend / offAudioSuspend / onAudioResume / offAudioResume)

当平台需要游戏静音(如用户进入直播间、系统来电等场景),会向游戏推送音频暂停事件;当允许恢复声音时,会推送音频恢复事件。游戏应在收到事件后及时暂停/恢复所有音效和背景音乐。

这是事件监听接口,不是 Promise 接口,无返回值。

javascript
// 监听音频暂停事件
function handleAudioSuspend(payload) {
  console.log('收到静音通知:', payload);
  // 暂停游戏中所有音效、背景音乐
  myGame.muteAll();
}

GameTokSDK.onAudioSuspend(handleAudioSuspend);

// 监听音频恢复事件
function handleAudioResume(payload) {
  console.log('收到恢复声音通知:', payload);
  // 恢复游戏中所有音效、背景音乐
  myGame.unmuteAll();
}

GameTokSDK.onAudioResume(handleAudioResume);

移除监听:

javascript
// 移除音频暂停监听(传入注册时的同一函数引用)
GameTokSDK.offAudioSuspend(handleAudioSuspend);

// 移除音频恢复监听
GameTokSDK.offAudioResume(handleAudioResume);

**options.sync 参数(状态同步):**

onAudioSuspendonAudioResume 支持第二个可选参数 options,其中 sync(默认 true)用于处理"注册时机晚于事件触发"的场景:

  • sync: true(默认)—— 若注册监听时音频已经处于暂停/恢复状态,回调会在下一个微任务中被立即同步调用一次,确保游戏不会错过之前的状态变化。
  • sync: false —— 只监听后续新的事件推送,不做注册时的状态同步。
javascript
// 默认 sync: true,若此时音频已被暂停,回调会立即触发一次
GameTokSDK.onAudioSuspend((payload) => {
  myGame.muteAll();
});

// 关闭状态同步,仅监听后续事件
GameTokSDK.onAudioSuspend((payload) => {
  myGame.muteAll();
}, { sync: false });

推荐用法: 在游戏初始化阶段尽早注册监听,并保持 sync: true(默认),这样即使注册时平台已经发送过静音指令,游戏也能正确同步状态。

1.2.14 游戏控制事件监听(onAutoStartGame / offAutoStartGame / onExitAndResetGame / offExitAndResetGame)

客户端/壳子可向游戏推送两类控制指令(客户端 => H5):

  • AUTO_START_GAME:通知游戏自动开始,效果等同于点击游戏里的 start 按钮。触发场景有两个:①玩家在平台排行榜弹层点"再玩一次"、平台核过体力后发出;②进场自动开局——玩家从小程序海报页点"立即开始"进入时,平台在游戏就绪后直接发出,可能在游戏刚加载完成、玩家没有任何游戏内操作时到达
  • EXIT_AND_RESET_GAME:客户端通知游戏结束,并回到初始状态(立即终止本局、清理倒计时/动画,回到初始或待开始画面)。

⚠️ 接入体力/排行榜链路的游戏必须实现这两个监听:平台的体力竞态兜底(开局后才发现体力不足)依赖 EXIT_AND_RESET_GAME 收回对局,签到/充值后的续玩与进场自动开局依赖 AUTO_START_GAME 开局;未实现会导致体力不足时游戏无法被平台拦截。

这是事件监听接口,不是 Promise 接口,无返回值。 AUTO_START_GAME 支持迟注册一次性补发:指令到达时若尚无监听(如进场自动开局早于游戏初始化完成),SDK 会暂存最近一条,待首个 onAutoStartGame 注册时异步补发一次并清除——因此正常在初始化阶段注册监听即可,不会错过进场指令。EXIT_AND_RESET_GAME 为纯瞬时指令、无补偿(它只在局中发出,游戏必然已完成初始化),请在游戏初始化阶段尽早注册监听。

javascript
// 监听“开始游戏”指令
function handleAutoStart(payload) {
  console.log('收到开始游戏指令:', payload);
  myGame.start(); // 等同点击游戏内 start 按钮
}
GameTokSDK.onAutoStartGame(handleAutoStart);

// 监听“结束并重置游戏”指令
function handleExitReset(payload) {
  console.log('收到结束并重置指令:', payload);
  myGame.exitAndReset(); // 结束当前局并回到初始状态
}
GameTokSDK.onExitAndResetGame(handleExitReset);

移除监听:

javascript
// 传入注册时的同一函数引用
GameTokSDK.offAutoStartGame(handleAutoStart);
GameTokSDK.offExitAndResetGame(handleExitReset);

仅监听一次:

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

客户端推送的消息格式:

json
{ "action": "AUTO_START_GAME" }
json
{ "action": "EXIT_AND_RESET_GAME" }

1.2.14a 游戏自带返回按钮(requestExit / REQUEST_EXIT)

若游戏界面自带「返回首页 / 退出」按钮,玩家点击时调用 requestExit() 通知平台「我要回首页」。平台收到后会下发 EXIT_AND_RESET_GAME,游戏在既有的 onExitAndResetGame 监听里收尾(终止本局、清理动画、回到待开始画面)即可——requestExit() 本身不改变任何对局状态,也不会清空当前 round_id

这是 fire-and-forget 接口:无 Promise、无返回值、不做去重(重复点击由平台侧去重)。参数全部可省,仅供排障关联:

javascript
/**
 * 玩家点击游戏内「返回首页」按钮
 * @param reason    可选,触发原因,缺省 'player_button'
 * @param state     可选,点击时游戏所处状态(如 'IDLE' / 'PLAY' / 'RESULT'),仅供排障
 * @param timestamp 可选,毫秒时间戳,不传则使用当前时间
 */
GameTokSDK.requestExit();

// 带排障信息
GameTokSDK.requestExit({ reason: 'player_button', state: 'PLAY' });

// 真正的收尾仍在这里完成(接入前须已注册,见 1.2.14)
GameTokSDK.onExitAndResetGame(() => myGame.exitAndReset());

若当前有进行中的对局(roundStart 之后、roundEnd 之前),SDK 会自动把 round_id 一并带出,便于平台关联本局;无需手动传入。

发送示例(JSON):

json
{
  "action": "REQUEST_EXIT",
  "data": {
    "reason": "player_button",
    "state": "PLAY",
    "round_id": "round_xxx",
    "timestamp": 1717000000000
  }
}

字段说明:

字段类型说明
reasonstring触发原因,缺省 player_button
statestring可选,点击时的游戏状态,仅供排障
round_idstring可选,局中点击时由 SDK 自动带出
timestampnumber毫秒时间戳,缺省为当前时间

⚠️ 不要在 requestExit() 之后自行 reset 游戏——统一等 EXIT_AND_RESET_GAME 到达再收尾,避免平台侧弹窗/结算与游戏画面不同步。

1.2.14b 对局结果通知(showMatchResult / SHOW_MATCH_RESULT)

适用于 KC League 等「对局结束后游戏不显示自己的结算屏、结算弹窗由平台展示」的流程。普通冲分流程无需调用。

对局结束后,游戏服务端向平台服务端 POST /kc-league/match-result 上报,平台服务端在响应里返回本局结算结果 result;游戏服务端把它推给游戏客户端,游戏客户端再调用 showMatchResult()result 原样交给平台客户端展示结算弹窗。三跳全程逐字透传:游戏不解析、SDK 不解析、不做任何字段约定,最终由平台客户端读取。这样以后弹窗要加字段,游戏侧和 SDK 都不用改版。

这是 fire-and-forget 接口:无 Promise、无返回值。 弹窗的渲染、关闭、后续去向全部由平台客户端决定;玩家在弹窗上点「再来一局」→ 平台下发 AUTO_START_GAME,点「退出」→ 下发 EXIT_AND_RESET_GAME,游戏在既有监听里响应即可(见 1.2.14)。

javascript
/**
 * 对局结果通知
 * @param matchId   必填,本局 id(游戏服务端生成,与 match-result 请求中一致)
 * @param result    必填,match-result 响应中的 result 对象,原样透传;上报失败时传 null
 * @param reason    可选,result 为 null 时的原因:'REPORT_FAILED'(平台返回非 2xx)| 'REPORT_TIMEOUT'(超时)
 * @param round_id  可选,缺省由 SDK 自动带出(当前局或最近一局),一般不传
 * @param timestamp 可选,毫秒时间戳,不传则使用当前时间
 */

// 推荐时序:先局末通知,再结算通知
GameTokSDK.roundEnd();
GameTokSDK.showMatchResult({
  matchId: msg.matchId,
  result: msg.platformResult,   // 服务端推送里原样携带的平台结果对象
});

// 上报失败(游戏服务端拿不到 result)时
GameTokSDK.showMatchResult({ matchId: msg.matchId, result: null, reason: 'REPORT_FAILED' });

round_idmatchId 的关系round_idroundStart()进匹配之前生成,matchId 是匹配成功后游戏服务端才生成的,两者不相等。SDK 会自动把本局 round_id 一并带出(roundEnd() 之后调用也能带出——SDK 记住了最近一局),平台用 round_id + matchId 这一对关联本局的体力扣减与结算记录,游戏侧无需处理。

发送示例(JSON):

json
{
  "action": "SHOW_MATCH_RESULT",
  "data": {
    "matchId": "sa-2041-Ckh2qj92Z",
    "result": { "league": "L20260901", "self": { "rank": 1, "pointsDelta": 12 }, "participants": [] },
    "round_id": "round-1788250600-abc123",
    "timestamp": 1788250693456
  }
}

字段说明:

字段类型说明
matchIdstring必填,本局 id
resultobject | null必填,平台服务端返回的结算对象,原样透传;null 表示上报失败
reasonstring可选,resultnull 时的原因枚举
round_idstring可选,缺省由 SDK 自动带出当前局或最近一局
timestampnumber毫秒时间戳,缺省为当前时间

约束:

  • result 必须是纯 JSON 可序列化对象——不能含函数、undefined、循环引用、Date / Map 等,否则 SDK 会拒发并 console.warn
  • 体量无限制(十人房实测约 2 KB,通道上限 MB 级)。
  • matchId、或参数里没有 result 键(上报失败请显式传 null)时,SDK 告警且不发送。
  • 调用后不要自行开局或 reset,统一等 AUTO_START_GAME / EXIT_AND_RESET_GAME 到达再动作,避免与平台弹窗不同步。

1.2.14c WPP 结果通知(showWppResult / SHOW_WPP_RESULT)

适用于 WPP 场景「游戏不显示自己的结果屏、结果弹窗由平台展示」的流程。

把结果内容原样交给平台客户端展示结果弹窗。SDK 不校验、不解析、不补任何字段:没有必填项,也不会自动带 round_id / timestamp,需要这类字段请自行放进去。传哪些字段由游戏与平台客户端双方商定,以后加减字段两边都不用改版。

这是 fire-and-forget 接口:无 Promise、无返回值。 弹窗的渲染、关闭、后续去向全部由平台客户端决定;玩家在弹窗上点「再来一局」→ 平台下发 AUTO_START_GAME,点「退出」→ 下发 EXIT_AND_RESET_GAME,游戏在既有监听里响应即可(见 1.2.14)。

javascript
/**
 * WPP 结果通知
 * @param payload 可选,原样透传给平台客户端的结果内容,字段自定
 */

GameTokSDK.showWppResult(msg.platformResult);

发送示例(JSON):

json
{
  "action": "SHOW_WPP_RESULT",
  "data": { "self": { "rank": 1, "score": 3200 }, "participants": [] }
}

字段说明: 无约定字段。data 就是调用时传入的那个对象本身,客户端直接读 data

约束:

  • payload 必须是纯 JSON 可序列化对象——不能含函数、undefined、循环引用、Date / Map 等,否则 SDK 会拒发并 console.warn
  • 调用后不要自行开局或 reset,统一等 AUTO_START_GAME / EXIT_AND_RESET_GAME 到达再动作,避免与平台弹窗不同步。

1.2.15 预约高光赛事(bookHQ / onBookHQ)

用于预约高光赛事(HQ)。调用 bookHQ() 把预约请求发送给客户端,客户端收到后可能先拉起登录(短信验证码 / 三方授权等),整条链路较长且耗时不可控;完成后通过 onBookHQ 回调把结果回传。

采用「发送 + 事件监听」而非 Promise: 因为登录链路长且耗时不确定,bookHQ() 设计为 fire-and-forget(发后即忘,不设超时、不等回调),结果通过 onBookHQ 异步回调。这样即使登录流程很长也不会被超时中断。

用法就两步:先注册 onBookHQ 监听,再调用 bookHQ 发送

javascript
/**
 * 1. 先注册回调(客户端登录多久都不影响,不会超时)
 *    payload 为归一化后的对象,成功判定:payload.success === true
 */
GameTokSDK.onBookHQ(function (payload) {
  if (payload && payload.success === true) {
    console.log('预约成功');
    // 更新 UI 为「已预约」
  } else {
    console.log('预约失败 / 用户取消,可重试');
    // 恢复 UI 为可点击
  }
});

/**
 * 2. 点击预约时发送(不阻塞、不等待)
 * @param pkId  必填,赛事 id(number 或纯数字字符串)
 * @param scene 可选,场景标识,默认 "kcSwiper"
 * @returns boolean —— bridge 是否可用(消息是否已发出 / 入队)
 */
GameTokSDK.bookHQ({ pkId: 123, scene: 'kcSwiper' });

若只是"发个预约、不关心结果",可只调用 bookHQ(),无需注册 onBookHQ。 另有 offBookHQ(handler)(移除监听)、onceBookHQ(handler)(只监听一次)两个配套方法,一般用不到——活动页通常初始化时注册一次即可。

发送示例(JSON):

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

客户端回调 payload(onBookHQ handler 收到的内容,两端已归一化):

json
{ "success": true }

字段说明:

字段类型说明
successboolean是否预约成功;true 为成功,false / 缺失为失败或取消

说明:客户端两端回传的信封略有差异(iOS 载荷在 content 且为 JSON 字符串,Android 在 data 且为对象),SDK 已自动抹平,onBookHQ 回调收到的始终是结构化对象,业务侧只需判断 payload.success

1.2.16 跳转品牌详情页(BRAND_ACTIVE_DETAIL_PAGE / BRAND_DT_DETAIL_PAGE)

游戏内点击按钮后,可调用以下接口通知客户端跳转到对应品牌详情页。客户端收到指令后自行完成页面跳转,游戏侧无需等待回包

方法action说明
GameTokSDK.brandActiveDetailPage(params)BRAND_ACTIVE_DETAIL_PAGE跳转品牌活动详情页
GameTokSDK.brandDtDetailPage(params)BRAND_DT_DETAIL_PAGE跳转品牌 DT 详情页

二者均为 fire-and-forget(发后即忘):无 Promise 返回值、不设超时、不等回调。

brandActiveDetailPage(BRAND_ACTIVE_DETAIL_PAGE)
javascript
/**
 * 跳转品牌活动详情页
 * @param gameId 必填,游戏 id
 */
GameTokSDK.brandActiveDetailPage({
  gameId: 'game-001',
});

发送示例(JSON):

json
{
  "action": "BRAND_ACTIVE_DETAIL_PAGE",
  "data": {
    "gameId": "game-001"
  }
}

字段说明:

字段类型必填说明
gameIdstring | number游戏 id
brandDtDetailPage(BRAND_DT_DETAIL_PAGE)

跳转品牌 DT 详情页时,需传入游戏色板五色,供客户端按设计稿角色渲染页面。色值建议传 CSS 色值字符串(如 #RRGGBB)。

javascript
/**
 * 跳转品牌 DT 详情页
 * @param background 必填,游戏主背景色(色系锚点,三色中最深)— 设计稿 a
 * @param surface    必填,卡片色(背景上的面板 / placeholder,三色中最浅)— 设计稿 b
 * @param text       必填,文字色1(与背景完全不同色系的醒目强调色)— 设计稿 c
 * @param accent     必填,辅助色(高亮票券 / 选中态,明度居中)— 设计稿 d
 * @param text2      必填,文字色2(通用正文,黑白二选一)— 设计稿 e
 */
GameTokSDK.brandDtDetailPage({
  background: '#2D6A4F', // 薄荷绿(主背景,最深)
  surface: '#D8F3DC',    // 更浅的绿(卡片)
  text: '#E85D04',       // 橙红(强调文字,异色系)
  accent: '#74C69D',     // 中间绿(辅助高亮)
  text2: '#000000',      // 通用正文
});

发送示例(JSON):

json
{
  "action": "BRAND_DT_DETAIL_PAGE",
  "data": {
    "background": "#2D6A4F",
    "surface": "#D8F3DC",
    "text": "#E85D04",
    "accent": "#74C69D",
    "text2": "#000000"
  }
}

字段说明:

字段设计稿标注类型必填说明示例(绿系游戏)
backgroundastring游戏主背景色(色系锚点,三色中最深)薄荷绿
surfacebstring卡片色(背景上的面板 / placeholder,三色中最浅)更浅的绿
textcstring文字色1(与背景完全不同色系的醒目强调色,如绿底的橙红、深梅底的金)橙红 / 金
accentdstring辅助色(高亮票券 / 选中态,明度居中)介于两者的绿
text2estring文字色2(通用正文,黑白二选一)#000000

参数与返回结构

  • 通用调用参数示例(参考,具体参数以各能力为准):
    • getProfile:``(通常无需额外参数)
    • getProfiles:{ uids: number[] }(必填,非空数组;返回 Profile[],结构等同 getProfile.data 的数组形式)
    • purchase:{ productId: string }
    • storageSet:{ key: string, value: any }
    • storageGet:{ key: string }
    • addScore:{ score: number, scoreType: string }
    • getPermissionMic:``(通常无需额外参数)
    • topup:{ amount: number, ... }(其它字段以客户端协议为准)
    • roundStart / roundEnd:{ round_id: string, timestamp?: number }
    • gameStart / gameEnd:{ round_id: string, timestamp?: number }(与 roundStart / roundEnd 的 payload 逐字相同,同义的干净通道;全部可选,fire-and-forget,无返回值)
    • getCouponTargetScore:``(通常无需额外参数)
    • showCouponDialog:{ coupon_id?: string, scene?: string }
    • showLeaderboard:{ scene?: string }(可选;fire-and-forget,无返回值)
    • showEnergyInsufficientDialog:{ scene?: string, round_id?: string }(可选;fire-and-forget,无返回值)
    • brandActiveDetailPage:{ gameId: string|number }(必填;fire-and-forget,无返回值)
    • brandDtDetailPage:{ background: string, surface: string, text: string, accent: string, text2: string }(必填色板五色;fire-and-forget,无返回值)
    • onAudioSuspend / onAudioResume:(handler: Function, options?: { sync?: boolean })
    • offAudioSuspend / offAudioResume:(handler: Function)
    • onAutoStartGame / onExitAndResetGame:(handler: Function)
    • offAutoStartGame / offExitAndResetGame:(handler: Function)
    • requestExit:{ reason?: string, state?: string, timestamp?: number }(全部可选;fire-and-forget,无返回值;局中自动带出 round_id
    • showMatchResult:{ matchId: string, result: object|null, reason?: string, round_id?: string, timestamp?: number }matchIdresult 必填;fire-and-forget,无返回值;自动带出当前局或最近一局的 round_id
    • showWppResult:Record<string, any>(可选,无约定字段;fire-and-forget,无返回值;SDK 不校验、不解析、不补 round_id / timestamp
    • bookHQ:{ pkId: number|string, scene?: string }(fire-and-forget,返回 boolean;结果通过 onBookHQ 回调)
    • onBookHQ:(handler: Function)(handler 收到 { success: boolean, ... }offBookHQ / onceBookHQ 为配套方法,一般用不到)
  • 成功返回统一结构:
typescript
{
  action: string;      // 本次调用的动作名(如 'GET_PROFILE')
  error: false;        // 成功时为 false(失败时 promise 会直接 reject)
  data: any;           // 原生回包内容,字段由具体动作定义
}
  • 失败行为:
    • **topup:** 业务成功/失败/取消等 多数在 then 通过 **data.code** 区分;catch 多为SDK异常、超时等。
    • 其它 Promise 接口: 失败时 Promise 将直接 reject 一个 Error 对象;建议在 .catch 中记录错误并进行重试或提示。

重要说明

为确保 SDK 正常工作,请勿覆盖或删除以下关键全局对象:

  • window.GameTokSDK(SDK 主接口)

示例(错误用法,请勿复制):

javascript
// 危险!将导致 iOS 或 Android 通信失效
window.GameTokSDK = {};
window.GameTokSDK = null;
delete window.GameTokSDK;

完整示例

javascript
// 获取用户资料
GameTokSDK.getProfile({})
        .then((resp) => {
          console.log('Profile:', resp.data);
        })
        .catch((e) => {
          console.error('获取个人资料失败:', e);
        });

// 批量获取用户资料(用于排行榜、好友列表等场景)
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('批量获取失败:', e);
        });

// 存储数据
GameTokSDK.storageSet({ key: 'config', value: { lang: 'zh-CN' } })
        .then(() => {
          console.log('存储成功');
        })
        .catch((e) => {
          console.error('存储失败:', e);
        });

// 读取数据
GameTokSDK.storageGet({ key: 'config' })
        .then((resp) => {
          console.log('Config:', resp.data.value);
        })
        .catch((e) => {
          console.error('读取失败:', e);
        });

// 购买示例
GameTokSDK.purchase({ productId: 'HAB.WATER.10.COINS' })
        .then((resp) => {
          console.log('Purchase:', resp.data);
        })
        .catch((e) => {
          console.error('购买失败:', e);
        });

// 加分示例
GameTokSDK.addScore({ score: 5, scoreType: 'score' })
        .then(() => {
          console.log('Score added');
        })
        .catch((e) => {
          console.error('加分失败:', e);
        });

// 获取麦克风权限(安卓,无返回值)
GameTokSDK.getPermissionMic();

// 充值:在 then 里用 data.code 判断(如 200 表示成功)
GameTokSDK.topup({ amount: 100 })
        .then(({ data }) => {
          if (data && data.code === 200) {
            console.log('充值成功', data);
          } else {
            console.log('充值未成功', data);
          }
        })
        .catch((e) => console.error('充值异常', e));

// 局次通知(round_id 前后一致)
const rid = 'r-' + Date.now();
GameTokSDK.roundStart({ round_id: rid });
GameTokSDK.roundEnd({ round_id: rid });

// 开局 / 局末的干净通道(与上面两行同义,不带体力语义;round_id 交给 SDK 兜底)
GameTokSDK.gameStart();
GameTokSDK.gameEnd();

// 获取优惠券目标分数,达到后再弹框
GameTokSDK.getCouponTargetScore()
        .then(({ data }) => {
          console.log('优惠券目标分数:', data.targetScore);
          if (myGame.score >= data.targetScore) {
            GameTokSDK.showCouponDialog({ scene: 'score_reached' });
          }
        })
        .catch((e) => console.error('获取优惠券目标分数失败', e));

// 音频事件监听(在游戏初始化阶段注册)
GameTokSDK.onAudioSuspend((payload) => {
  console.log('静音', payload);
  myGame.muteAll();
});

GameTokSDK.onAudioResume((payload) => {
  console.log('恢复声音', payload);
  myGame.unmuteAll();
});

// 预约高光赛事:先注册回调,再发送(fire-and-forget,不会超时)
GameTokSDK.onBookHQ((payload) => {
  if (payload && payload.success === true) {
    console.log('预约成功');
  } else {
    console.log('预约失败 / 取消,可重试');
  }
});
GameTokSDK.bookHQ({ pkId: 123, scene: 'kcSwiper' });

// 游戏自带「返回首页」按钮:只通知平台,收尾等 EXIT_AND_RESET_GAME
document.getElementById('btn-home').onclick = () => GameTokSDK.requestExit({ state: 'PLAY' });

// 跳转品牌详情页(fire-and-forget;参数字段待定,按最终协议传入)
GameTokSDK.brandActiveDetailPage({ gameId: 'game-001' });
GameTokSDK.brandDtDetailPage({
  background: '#2D6A4F',
  surface: '#D8F3DC',
  text: '#E85D04',
  accent: '#74C69D',
  text2: '#000000',
});

Swipe & Play Endless Game Together