Skip to content

离线/余额回收回调 ​

VCT 平台在玩家退出游戏、后台强制踢线或切换同币种游戏时通知包网(Agent)关闭会话并完成最终对账。

环境地址 ​

环境Base URL
生产环境https://hub-panel-api.v7stech.com
测试环境https://hub-panel-api-dev.v7stech.com

必须确认收到

包网必须返回 JSON 响应,且 status 必须为 success。缺少 status、返回其他状态或响应无法解析,都会被 VCT 记录为离线回调失败。

接口信息 ​

属性值
路径POST /api/v1/callback/offline
调用方VCT 平台 → 包网 (Agent)
Content-Typeapplication/json

鉴权与签名 ​

统一 Header Token 规则见认证机制。本接口的 Body sign 使用 HMAC-SHA256(username + "." + timestamp, agent_api_secret);Header 与 Body 的 timestamp 必须完全一致。

请求参数(VCT 发送) ​

参数名类型必填说明
usernamestring是玩家用户名
currencystring是货币代码
session_idstring是本次 VCT 游戏会话唯一 ID;必须用于幂等和对账
game_idstring否游戏 GUID
game_codestring否游戏代码
game_namestring否游戏名称
game_typestring否游戏类型
providerstring否游戏商代码
reclaimed_amountdecimal是Hub 在 cleared_at 清空本地钱包时保存的金额快照;仅用于本次 Offline 对账,不用于重复变更主钱包,也不代表所有迟到流水处理完成后的包网主钱包最终余额
wallet_cleared_totaldecimal普通下分是Hub 实际清零的同币种全部钱包物理合计
mirrored_delta_totaldecimal普通下分是清空时仍同时存在于来源钱包和当前钱包的增量合计;以来源钱包实际清空金额为上限,可包含同一次下分前发生的负数冲正
net_balance_to_reconciledecimal普通下分是wallet_cleared_total - mirrored_delta_total,用于与包网主钱包对账
wallet_breakdownarray普通下分是每个被清空钱包的金额以及 session、Game、Provider、Brand 快照
mirrored_adjustmentsarray普通下分是已成功镜像的调整事件;每项包含 event_id、原始 delta、镜像额 mirrored_amount、来源/目标 session 与 Brand。最终可扣重总额以 mirrored_delta_total 为准
cleared_atstring否*Hub 实际清空本地钱包的时间,RFC3339/RFC3339Nano;普通退出/踢出/切换游戏的新记录会提供,历史记录和 launch_failed 可能没有
actionstring是exit_game、api_kick_exit、switch_game 或启动失败补偿 launch_failed
signstring是请求签名
timestamplong是请求时间戳

示例请求 ​

json
{
  "username": "player001",
  "currency": "MYR",
  "session_id": "vct-session-uuid-001",
  "game_id": "game-guid-001",
  "game_code": "12345",
  "game_name": "Example Game",
  "game_type": "Slot",
  "provider": "gsc",
  "reclaimed_amount": 200.00,
  "wallet_cleared_total": 200.00,
  "mirrored_delta_total": 50.00,
  "net_balance_to_reconcile": 150.00,
  "wallet_breakdown": [
    {"brand_code":"UU","session_id":"uu-current-session","is_active_session":true,"cleared_amount":150.00},
    {"brand_code":"CMD","session_id":"cmd-old-session","is_active_session":false,"cleared_amount":50.00}
  ],
  "mirrored_adjustments": [
    {"event_id":"CMD_SETTLED_1","source_brand_code":"CMD","target_brand_code":"UU","delta":50.00,"mirrored_amount":50.00}
  ],
  "cleared_at": "2026-10-04T23:30:12.123Z",
  "action": "switch_game",
  "sign": "9a2f64d0bcfa829...",
  "timestamp": 1783324700
}

响应要求(Agent 必须返回) ​

成功确认 ​

json
{
  "status": "success",
  "message": "received",
  "username": "player001",
  "currency": "MYR",
  "reclaimed_amount": 125.50
}

status 必须严格为 success,表示 Agent 已收到并处理该离线/余额回收通知。钱包清空完成后 Hub 不会让 callback 成败阻止玩家再次启动;HTTP 2xx 且 Body 的 status 为 success 用于把 reclaim 标记完成并停止重试。

失败响应 ​

json
{
  "status": "error",
  "message": "player not found"
}

失败响应或 HTTP 非 2xx 会被 VCT 记录为回调失败;Agent 应使用 sign、timestamp 和 username 做验签及幂等处理。

如果 Agent 返回失败,VCT 会把该笔记录标记为 callback_failed,之后再次发送相同的 Offline 请求,或由管理员手动 Retry;Agent 必须按 session_id + action 幂等处理。失败记录不会阻止玩家再次启动游戏。

对于普通 Offline callback,VCT 会在失败后自动重试,最多 15 次,并使用递增退避间隔:1 分钟、5 分钟、15 分钟、30 分钟、1 小时、2 小时。达到上限后停止自动重试,但管理员仍可手动 Retry。每次实际 HTTP 调用都会单独记录,包网不能根据重复请求重复入账

重要说明 ​

  • 在 Seamless 模式下,玩家主钱包的扣款、加款和冲正已由 /bet-push 实时完成;reclaimed_amount 是 Hub 清空本地钱包时的金额快照,仅用于本次 Offline 对账,Agent 不得据此重复变更主钱包。
  • mirrored_adjustments 只列出成功应用的同步事件。每项的 mirrored_amount 是该事件已镜像到当前游戏钱包的金额;正数代表加款镜像,负数代表冲正抵销。来源钱包可能已经被其他流水改变,因此最终实际可扣重总额以 mirrored_delta_total 为准(Hub 已按来源钱包下分时的实际余额封顶)。wallet_cleared_total 可能同时包含来源钱包和这些已镜像金额。使用 event_id 核对本地事件账本,并以 net_balance_to_reconcile 作为最终总对账金额;不能把物理合计直接加回主钱包。
  • cleared_at 是 Hub 清空本地钱包的时间,不是 Offline 请求发送时间,也不是所有迟到日志处理完成的时间。清空之后才到达 Hub 的日志仍可能出现,Agent 应继续按 bet-push 的幂等规则处理,并使用 session_id 与 cleared_at 做审计。
  • VCT 会记录请求和响应,便于核对是否收到以及回收金额。
  • Agent 应优先按 session_id + action(并结合请求签名)实现幂等,避免重复入账

启动失败补偿 (launch_failed) ​

当 VCT 已经收到 /balance 成功响应、但游戏商启动失败时,VCT 会调用本接口并发送:

json
{
  "username": "player001",
  "currency": "MYR",
  "session_id": "vct-session-uuid-001",
  "reclaimed_amount": 100.00,
  "action": "launch_failed",
  "sign": "9a2f64d0bcfa829...",
  "timestamp": 1783324700
}

包网处理要求:

  • session_id 必须原样保存、匹配,不得使用玩家或币种推断替代。
  • launch_failed 只释放本次启动的临时会话、余额锁定或临时记录。
  • launch_failed 不得写入普通 enter_game,也不得进入普通退出游戏 offline 平账流程。
  • 同一个玩家、币种和 session_id 应建立唯一约束,重复启动记录必须被拒绝或幂等处理。
  • 重复收到同一个 launch_failed 时必须返回成功或幂等结果,不能重复加款、重复释放或重复创建记录。
  • 只有普通 exit_game、api_kick_exit、switch_game 等动作才进入普通 offline 平账逻辑。

AI 开发提示词(可直接复制) ​

text
请在当前包网项目中实现 VCT 离线/余额回收回调 POST /api/v1/callback/offline。

实现要求:
1. 校验 Authorization 三段式 Token,Header 与 Body timestamp 必须一致;Body sign=HMAC-SHA256(username + "." + timestamp, agent_api_secret),使用小写 hex 和常量时间比较。
2. 必须按 Agent + username + currency + session_id + action 幂等;VCT 会自动或人工重试相同请求。
3. 普通 exit_game、api_kick_exit、switch_game 用于关闭对应 session、释放临时状态并记录钱包快照;switch_game 汇总同币种全部 Provider 钱包。保存 wallet_breakdown 与 mirrored_adjustments,并按 event_id 对账。
4. Seamless 模式资金已由 bet-push 实时处理;普通 offline 不得再次给主钱包加款或扣款。wallet_cleared_total 是物理合计,net_balance_to_reconcile 才是扣除镜像后的对账值。
5. cleared_at 是 Hub 清空本地钱包的时间,不是回调发送时间;迟到 bet-push 仍要继续按幂等资金规则处理。
6. launch_failed 只撤销本次启动的 launch_pending、临时锁和记录,不得写 enter_game、不得进入普通退出流程、不得重复入账。
7. 只有数据库事务成功提交后才返回 HTTP 2xx 和 {"status":"success"};重复请求已处理也应幂等返回 success。
8. 暂时性故障返回非 2xx 或 status=error,让 VCT 重试;不能在处理失败时返回 success。
9. 日志保存 session_id、action、username、currency、reclaimed_amount、cleared_at 和处理结果,并脱敏 Secret、Token、sign。
10. 增加测试:三种普通退出、launch_failed、重复请求、乱序到达、签名错误、timestamp 过期、事务回滚以及 bet-push 后不重复改余额。

请复用现有会话表、钱包账本、事务、锁和认证组件,完成路由、Service、幂等约束及测试。

VCT Gaming Integration Platform