Seamless Wallet 运营商对接文档
PlayRouter Seamless Wallet(单一钱包)· 方向 A(你调我们的 15 个端点)+ 方向 B(我们调你的 5 个钱包回调)· 本页所有端点均含可交互 Playground · 接转账钱包?见 Transfer Wallet 文档站 ↗
这份文档面向运营商的后端工程师。你不需要读完整规范就能完成对接:本页从零开始讲每个字段是什么、你该传什么/返回什么、以及所有会出错的地方。所有示例都可以在页内 Playground 直接玩(纯前端模拟,不发网络请求)。
① 上线必须只有 4 + 5(调 4 个端点、实现 5 个回调,其余用到再学)。② 双向共用同一组密钥、同一个签名算法。③ 方向 B 每个回调都回 HTTP 200 + JSON,同一个 transaction_id 只记一次账,credit 和 rollback 永远不许拒——唯二例外 player_not_found / currency_mismatch(见附录铁律注)。④ 方向 A 是真 HTTP 码——2xx 用 body、429 等着重试、5xx 同幂等键重试、其余 4xx 修请求。
接入之旅:全貌一张图
整套对接只有两个方向。方向 A:你调用归属服 {eu|sea|latam}.api.playrouter.io——启动游戏、拉目录、对账、管活动、彩池 feed;方向 B:我们调用你注册的钱包回调——查余额、扣款、入账、结算、冲正。端点按「洋葱」分层:接通核心层就可以上线——4 个端点 + 5 个回调;其余端点全部可接可不接(对账三件推荐接,上线后再补也行)。下面每个端点的标题上都标了必须 / 可选。
| 层 | 端点 | 什么时候需要 |
|---|---|---|
| 核心 · 上线必须 | sessions/create · games/list · providers/list · currencies(+ 5 个回调) | 第一天。认证套件核心卷只考这些 |
| 运营 | transactions/list · transactions/get · records/list · records/get | 想做每日对账、查投递日志时(强烈建议,可后补);想给玩家看游戏记录、按局做统计时接 records(可选) |
| 活动模块 | campaigns/create · cancel · options · list · get | 要发免费旋转/免费筹码/白送功能购买时。不发活动永远不用碰 |
| 展示 / 参考 | jackpots/feed | 想拿彩池当前金额去展示时(跑马灯等是典型用途,不设限) |
| 会话辅助 | sessions/terminate | 风控/自我排除要把人请出游戏时(best-effort) |
钱包模式与拓扑
单一钱包(Seamless)模式下,玩家资金始终且只在你的系统里——没有厂商侧游戏钱包、没有划拨。玩家的每一笔投注/派彩,由 PlayRouter 实时回调你的钱包记账:钱不动,账单飞过来。代价与收益是同一件事:每一笔钱都过你的手(实时、可拒绝、账本天然一致),所以你要运营一个 7×24 可被调用的回调服务。
三条你要提前知道的规则
| 规则 | 含义 |
|---|---|
| 唯一账本 | 玩家余额只有你这一份。我们的每个资金回调都带你答复的最新 balance——但记账的真相永远在你的库里。 |
| 7×24 回调服务 | 你要暴露一个回调 base URL(沙箱与生产各一),我们向 {base}/wallet/* 五个固定路径发 POST。回调 base URL 须是最终地址:我们不跟随 HTTP 跳转(3xx),收到 3xx 按配置故障处理(见「错误与重试」)。宕机也不烧钱:我们按 HTTP 类别解读你的故障并按时间表重试(见「我们对你的承诺」)。 |
| 归属区 | 生产三服 eu / sea / latam 全开放,固定用离你钱包后端最近的一个——方向 A 你调它,方向 B 回调从它发出,双向一个家,回调出口 IP 白名单只有一段。 |
/v1/seamless/…,转账面 /v1/transfer/…)——将来若增开转账,这 5 个端点切换只改前缀,密钥也是同一把(密钥账号级通用,2026-07-20 起):一把 pk_/sk_ 对两条产品线同时有效,增开转账无需换钥。你能调用哪些端点取决于账号的产品开通状态,与密钥无关——体现在路径前缀上:未开通的面返回 403 product_not_enabled。五分钟快速开始
pk_test_…(Key-Id,出现在请求头里)和 sk_test_…(secret,签名/验签用,绝不出现在网络上)。双向共用这一组——方向 A 你签名我们验,方向 B 我们签名你验。
沙箱与生产各注册一个,例如 https://wallet.yourcasino.com。请填最终地址——不要填会返回 301 / 302 / 307 / 308 的地址(常见于 http 跳 https、换域名、路径末尾自动补斜杠)。我们向 {base}/wallet/* 五个固定路径发 POST;五个端点共用同一段验签逻辑(下节,三种语言现成代码)。
一张 processed_transactions 幂等表 + 直线记账代码,没有状态机(见方向 B 区,建表 SQL 现成)。
providers/list + games/list 建 lobby(厂商分区 + 游戏目录)、currencies 拿金额精度表(方向 B 记账要用)→ sessions/create 启动第一局。启动时我们会先回调你的 wallet/balance 做预检——第一条回调就这么来(见全景时序)。
沙箱后台一键运行,核心卷只考必须面(启用活动模块才加考活动卷)。全绿 = 自动获得生产密钥申请资格。
第一个请求(复制即跑)
配合下方「签名」一节的 signedHeaders,对沙箱调一次 providers/list——从密钥到 200 的最短路径:
三个核心概念(先建立心智模型)
整套回调只依赖三个概念。用你熟悉的电商类比一次讲清,后面所有字段表都会变得好读。
transaction_id = 支付流水号
每个资金回调是一笔流水,这是它的全局唯一编号(txn_ 前缀),也是幂等键:同一编号再来,答 duplicate:true、别记账。你的幂等表存的就是它。
round = 订单号(一局游戏)
slots 转一次轮、二十一点打一手=一局。一局产生多笔流水(扣注、派彩、冲正),全挂同一个 round.id(rnd_)。sequence=本局第几个事件;finished=这局完了没(每局恰好一次 true)。用途:游戏历史按局分组、未完成局管理、客服对坐标。不用它账也是对的——纯分组标签。注意:收口后仍可能到达冲正(rollback)——冲正不改变收口状态、不携带 finished:true,照常按 rollback 处理。
session = 一次游玩
玩家点开游戏到离开算一次游玩。session_id(ses_)是我们的编号——排障时报给支持,双方对同一坐标;session_payload 是你的字条——启动时你塞什么,每个回调原样带回什么(内部会话号/平台/品牌),零查表认路。两个都零处理义务,且都不是拒绝资金的理由。
附赠:campaign_id = 活动编号
钱源于你创建的活动(免费旋转、免费筹码、白送的功能购买)时出现(cmp_)——多活动并行时,这笔派彩算哪个活动的营销成本,到账即知。
全景时序:一笔钱怎么流动
四个角色:玩家在你的站点玩、游戏由厂商提供、PlayRouter 居中翻译、你只面对统一的 5 个回调。下图一次讲完启动、主流两段式厂商的一局、少数合笔厂商的一局——你会发现无论厂商怎么发,落到你这里只有 debit / credit / settle 三种直线调用。
签名(必读 · 双向同一套)
两个方向用同一组密钥、同一个算法:HMAC-SHA256 对「时间戳 + 原始请求体」签名,装在两个固定请求头里。方向 A 你给请求签名、我们验(失败 = 401,语义正确且唯一);方向 B 我们签名、你验(失败 = 200 + {"status":"error"})。验签是你唯一的安全义务——不需要校验会话、不需要校验 IP、不需要维护 token。
A给你的请求签名(你 → 我们 · 三种语言)
B验签(我们 → 你 · 三种语言)
验签失败你返回 200 + status:"error";就算你错手返回了 401 或 403——我们按类别解读:任何 4xx(429 除外——429 按 5xx 进重试时间表)都当作你的配置故障(告警 + 冻结投递,绝不重试轰炸),任何 5xx / 超时进重试时间表。没有会烧钱的错误答案。
幂等与重试(总纲)
一套规则、两种钥匙——按方向分工,两边都是「拿不到结果就同键重来,恒安全(在同一区内)」。这里给总图,细节在各自分区展开。
方向 A · 你是调用方
幂等键 = body 里的 idempotency_key(仅变更类端点,如 campaigns/create)。write-ahead 先落库再派生(推荐 fs-{内部ID}),超时/5xx 同 key 原样重发;命中重放返回原响应 + Idempotency-Replayed: true;同 key 不同 body → 409 idempotency_key_reused;同 key 同 body 但首请求仍在处理中 → 409 request_in_progress + Retry-After(按头值重试同 key 即命中重放);保留 7 天。严禁每次重试换新 key。详见方向 A 总览。
declined,不是错误)——所以你那侧永远不会触发我们的错误重试,宕机时基建吐的 5xx 恰好就是正确信号。方向 A:你调用我们的 15 个端点 DIRECTION A
Base URL:https://{eu|sea|latam}.api.playrouter.io——三个归属服全开放,固定用离你钱包后端最近的一个,用哪个归属就在哪个(沙箱 https://api.sandbox.playrouter.io,单区)。改用别的区不需要新账号或新密钥,直接调用新区即可;数据按区存放、区与区不互通——旧区的会话与交易记录留在旧区,照常可查。拿不到响应要重试时,请留在同一个区:同一个 idempotency_key 只在同一区内去重,换到别的区重发会被当成一笔新请求再执行一次。一律 POST + JSON,带上一节的两个签名头。路径第二段 = 钱包模式面:本产品全部端点都在 /v1/seamless/<域>/<动作> 下(如 /v1/seamless/sessions/create、/v1/seamless/currencies);转账产品的同名端点在 /v1/transfer/… 下。账号开通了哪一面才能调哪个前缀——未开通的面一律返回 403 product_not_enabled,与端点是否"公共"无关。两面共有的 5 个目录/会话端点字段逐字相同,切换只改前缀。角色反转提醒:这一区你是调用方——幂等键是 body 里的 idempotency_key(仅变更类端点),错误是真 HTTP 码 + 语义字符串错误体,别把方向 B 的「恒 200」搬过来。15 个端点里必须的只有 4 个(sessions/create · games/list · providers/list · currencies),其余全部可选——每个端点标题都有标注。字段表读法(与方向 B 镜像):请求表的「必填 / 可选」说的是你要不要传;响应表的「总是有 / 不一定有」说的是我们给不给——「不一定有」判空再用即可。
错误处理:你只需要 4 个分支
每个 4xx/5xx 都是固定形状的错误体——code 是语义字符串(不用数字),只进日志不要求逐个 catch;没见过的 code 按其 HTTP 码的动作处理即可(code 增补不是破坏性变更):
完整错误码目录(30 个逐码表)收在 附录 · 速查。
幂等(变更类端点:campaigns/create · cancel)
body 必填 idempotency_key(1–128 字符 [A-Za-z0-9_.-])。推荐确定性派生(如 fs-issue-{你的内部记录ID}),严禁每次重试新生成。命中重放返回原响应 + 响应头 Idempotency-Replayed: true;同 key 不同 body → 409 idempotency_key_reused(绝不执行,修 bug);保留 7 天。5xx / 超时后放心用同 key 重试——绝不会发出两份活动。
连击(在途撞键):同 key 同 body、首请求尚在处理中(原响应还没产生)→ 409 request_in_progress + Retry-After 头(秒)——按头值用同一个 key 重试,届时命中重放取回原响应。判定顺序:先比 body(不同 → idempotency_key_reused,永久错误)、再看状态(终态 → 重放;在途 → request_in_progress,瞬态)。本语义适用于所有带 idempotency_key 的变更端点;transfers/create(转账钱包产品)例外——它有 pending 单据形态,撞键在途直接 200 + pending + Idempotency-Replayed 头。
标准响应头 RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset;超限 429 + Retry-After。配额按 API Key 计(轮换期两把 key 各有独立额度),以下限保证公布——每个 API Key 每分钟至少:transactions/list 60 次、records/list 150 次(本面与转账钱包面的 records/list 两面合计——两面同开的账号共用这一档)、其余端点 1000 次;实际放行量可能更高,下限只会放宽不会收紧。RateLimit-* 头的值按端点给出(重查询端点配额更严),勿跨端点缓存;建议(非强制)按 RateLimit-Remaining 自适应节流。首次接入回补历史可联系临时提额。
POST/v1/seamless/sessions/create 上线必须
启动游戏,真钱/试玩合一。启动时我们统一先回调你的 wallet/balance 做预检(所有厂商一致,你无需感知)。不托管玩家资料:全 API 的玩家身份面只有一个不透明的 player_id——姓名/邮箱/生日/国籍一律不收、PII 不过境。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| player_id | string | real 必填 | ≤ 64 字符;你侧稳定玩家标识,方向 B 回调原样带回;demo 可省略(传了可做统一日志归因) | "wang-123" |
| game_code | string | 必填 | <provider>:<游戏段>,来自 games/list——全 API 同一把游戏键。不透明键,原样回传:多产品线厂商(目前只有 db,DB Gaming)的游戏段形如 card-101(产品线-厂商游戏码),单产品线厂商的游戏段就是厂商游戏码原文;不要自行拆游戏段,产品维度用 provider_product 字段 | "pg:fortune-tiger" / "db:card-101" |
| currency | string | 必填 | real = 钱包币种,精确匹配永不降级;同一厂商(多产品线厂商按产品线)内,一个 player_id 只能使用一个币种,以首次真钱开局为准,沙箱与正式共用这一规则——之后用别的币种在同一厂商开局 → 422 currency_mismatch(message 点名该玩家在这个厂商的币种);要在同一厂商用多个币种,请按币种拆 player_id(如 wang-123-cny / wang-123-usd),不同厂商之间可以用不同币种(厂商 / 实例不收该币种 → 422 currency_not_supported,在建户 / 打厂商之前拒——如 saba 实例只登记 USD 时的 CNY;该币种平台侧暂时不可用时同码、fields.currency = temporarily unavailable,不要换币种、稍后重试);demo = 期望展示币种——厂商不支持则以厂商默认币种(通常 EUR)启动,静默降级不报错、不校验 | "BRL" |
| mode | string | 必填 | real | demo——真钱/试玩合一端点,模式进字段 | "real" |
| locale | string | 可选 | ISO 639-1 两字母小写;不支持的值静默降级 en | "pt" |
| device | string | 可选 | desktop | mobile;厂商需要时我们给默认值 | "mobile" |
| lobby_url | string | 可选 | 退出游戏的返回地址 | "https://op.com/lobby" |
| deposit_url | string | 可选 | 游戏内余额不足时厂商「去充值」按钮的跳转地址;不给则降级用 lobby_url 补位 | "https://op.com/cashier" |
| launch_mode | string | 可选 | "url"(默认)| "html"——HTML 直出(抗封锁,见下图);游戏是否支持看 games/list 的 launch_modes;不支持时返 url 不报错 | "url" |
| ip | string | real 必填 | 玩家 IP(IPv4/IPv6);demo 可省略 | "203.0.113.7" |
| session_payload | string | 可选 | ≤1024 字符透传串,方向 B 每个回调原样奉还(零查表认路) | "wsess-8f2|ios" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| session_id | 总是有 | 我们的会话 ID(ses_)——排障 / terminate / 回调关联的锚点 |
| url | 总是有 | 恒在——HTML 直出型厂商由我们托管 launcher 包装成 URL,统一契约零分支。拿到后立即打开、只用一次:链接有效期由游戏厂商决定,短的只有约 1 分钟,部分厂商的链接只能打开一次;玩家每次进游戏(含刷新页面、重新打开)都重新调用 sessions/create 取新链接,不要缓存复用 |
| html | 不一定有 | launch_mode:"html" 且厂商支持 HTML 直出时给——HTML 启动文档,从你的自有域名伺服。伺服禁缓存:每次启动重新取、响应带 Cache-Control: no-cache, no-store, must-revalidate(厂商启动令牌一次性,缓存复用会启动失败) |
| expires_at | 总是有 | 启动 URL 最晚失效时刻;实际有效期由游戏厂商决定、可能早得多(见 url 行),不要据此缓存复用 url |
locale 不支持 → en;demo 币种不支持 → 厂商默认币种;launch_mode:"html" 厂商没有 → 只返 url。全部静默、不报错——凡是拒绝调用方毫无收益的地方,一律降级。你不用维护任何厂商白名单。
- 422
game_not_available:游戏当前不对这个玩家开放(下架、地域限制、未到 released_at,或厂商拒绝该玩家进入)——重试同一请求不会成功,提示玩家换一款游戏。例外:fields.game_code=temporarily unavailable表示平台侧暂时不可用、游戏本身没有下架——稍后重试同一请求即可,你不需要做任何配置,也不要把游戏从大厅撤下。 - 422
demo_not_available:这款游戏不支持试玩。 - 502
provider_error:临时故障,稍后用同一请求重试。
你 → PLAYROUTER
(点启动)
PLAYROUTER → 你
POST/v1/seamless/games/list 上线必须
游戏目录:cursor 分页 + updated_after 增量同步。图、语言、币种、投注面值、features、RTP、启动形态、状态一个对象拿全——没有独立面值端点、没有索引往返(campaigns/create 直接传金额字符串)。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| cursor | string | 可选 | 上页响应的 next_cursor;首页传 null 或省略。黑盒游标:内容不作承诺,原样传回即可 | "eyJ…" |
| limit | int | 可选 | 每页条数(≤2000) | 500 |
| updated_after | string | 可选 | 增量同步:只拉此时刻后有变更的(厂商下架也是一次变更,status 变 recalled;从目录消失的游戏不在增量里,见响应 status 说明) | "2026-07-01T00:00:00.000Z" |
| provider | string | 可选 | 过滤:按厂商(厂商族码,值来自本面 providers/list 的 code) | "pg" |
| category | string | 可选 | 过滤:按品类(live 是一等品类) | "slots" |
| currency | string | 可选 | 过滤:按币种可玩性 | "BRL" |
带过滤条件做增量时,只返回当前仍匹配的游戏;游戏因品类或币种登记变化不再匹配时,不会出现在结果里。要跟踪这类变化,增量同步请不带过滤条件,按返回的 category / currencies 字段在本地筛(这些字段变化时 updated_at 会刷新)。
响应信封
| 字段 | 出现 | 说明 |
|---|---|---|
| data | 总是有 | 游戏对象数组(逐字段见下表) |
| next_cursor | 总是有 | 下一页游标;末页为 null |
| has_more | 总是有 | 还有没有下一页 |
响应的游戏对象(逐字段)
| 字段 | 出现 | 说明 |
|---|---|---|
| game_code | 总是有 | <provider>:<游戏段>——启动、活动、对账全 API 同一把键(不透明、原样回传;多产品线厂商如 db:card-101) |
| name | 总是有 | 游戏展示名 |
| provider | 总是有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) |
| provider_product | 不一定有 | 该游戏所属的厂商产品维度键,多产品线厂商才出现(db 的产品线:live 真人 / hash 哈希 / card DB 棋牌 / boyacard 博雅棋牌 / fishing DB 捕鱼 / boyafishing 博雅捕鱼 / slots 电子 / scratch 刮刮乐 / lottery 彩票 / esports 电竞 / sports 体育;其它多产品厂商沿用既有子钱包语义)。lobby 按产品线分区、对账按产品线归类用它;不要自行拆 game_code 的游戏段来反推 |
| category | 总是有 | 品类(live 是一等品类)。取值 = 游戏类型字典 17 码:sports, live, slots, lottery, card, poker, fishing, esports, virtual, originals, racing, cockfighting, hash, arcade, table, prediction, scratch;字典只增不改,未知值按「其它」兜底 |
| images | 不一定有 | {square, landscape} 图片 URL——只在厂商提供时出现;缺省 = 厂商无图源,不编图(2026-09-19 起由「总是有」改为「不一定有」:7 家已接厂商中仅个别厂商的游戏列表带图) |
| locales | 总是有 | 支持语言(ISO 639-1 两字母,真数组)。厂商实例级:由平台运营按厂商实例登记,同一厂商实例下所有游戏同值,不保证每款游戏各异;未登记时缺省 ["en"]。含义 = 该厂商启动接口可传的语言;不在表内的 locale 启动时静默降级 en |
| currencies | 总是有 | 支持币种(ISO 4217 / 加密货币码,真数组)。厂商实例级:由平台运营按厂商实例登记,同一厂商实例下所有游戏同值,不保证每款游戏各异;未登记时缺省 ["CNY"]。以此判断 sessions/create 该传什么币种;实例真正接受的币种以 422 currency_not_supported 为准(fields.currency = temporarily unavailable 的除外:那是平台侧暂时不可用,不代表实例不收)(目录里出现该实例不接受的币种属运营登记错误,我方告警修正;目录只列部分接受币种是正常经营选择) |
| devices | 总是有 | 支持设备(desktop / mobile) |
| launch_modes | 总是有 | ["url"] 或 ["url","html"]——含 "html" 即支持 HTML 直出(配合 sessions/create 的 launch_mode) |
| features | 不一定有 | {demo, freespins, bonus_buy, jackpot} 能力布尔——只在厂商目录给出能力信息时出现;缺省 = 未知,不编假 false(2026-09-19 起由「总是有」改为「不一定有」) |
| bet_levels | 不一定有 | 币种 → 合法面值数组(金额字符串),只在厂商提供时出现;缺省 = 厂商不给面值表,campaigns/create 的 bet_amount 按厂商文档自定——无索引、无独立端点、无工单 |
| rtp | 不一定有 | RTP 百分数字符串("96.81"),只在厂商提供时出现;缺省 = 厂商不公开 RTP,不编数(2026-09-19 起由「总是有」改为「不一定有」:已接 7 家厂商的目录接口均不含 RTP) |
| volatility | 不一定有 | 厂商波动率评级原值透传(各家口径不一,不强行归一) |
| released_at | 不一定有 | 发布时刻——此前启动返回 game_not_available(「预告上架」排期数据源) |
| forbid_bonus_play | 不一定有 | true = 禁止用奖金资金玩(你自建奖金系统的合规输入) |
| status | 总是有 | active | recalled——目录只含运营已上架、且所属厂商(多产品线厂商按产品线)当前由平台开放的游戏;active = 厂商在售,recalled = 厂商已下架(下架即一次 update,增量同步天然捕获)。运营关闭上架的游戏、平台停用的厂商(产品线)的游戏会直接从目录消失,这类消失不经 updated_after 增量下发,由我方商务另行通知;也可自行核对:厂商(产品线)停用时它会从 providers/list 中消失,单款游戏关闭上架可不带 updated_after 全量拉取比对。平台恢复该厂商后,其游戏重新出现、updated_at 随之刷新 |
| updated_at | 总是有 | 最近变更时刻——增量同步(updated_after)的锚 |
首次:读响应头 X-Full-Dump-Url 拉每日全量快照(CDN,万级目录不用翻页);链接下载失败时,改用不带 updated_after 的分页全量。日常:updated_after 增量 + cursor 翻页。合规不用自算——不可用的游戏启动时返回 game_not_available,没有黑白名单矩阵要维护;平台停用的厂商(产品线)的游戏启动时返回 403 provider_not_enabled(游戏本身已下架或未上架的仍返回 game_not_available)。
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/providers/list 上线必须
零参数目录端点——请求就是空对象 {}(租户由签名头的 Key-Id 解析,任何端点都不用带租户字段),每个厂商族一行(pg、db、pp、saba……)。什么时候用:后台厂商开关页一次拿全;lobby 厂商分区要的展示名(games/list 只给 "pg" 这种机器码,"PG Soft" 在这里);厂商是否在开放(停用的厂商族直接从列表消失,整区下架以此为判断依据);多产品线厂商的产品线清单 products(lobby 二级分区、provider_product 取值发现)。lobby 厂商列表页是行业标配、这三样只有它给——所以它属必须(也是全 API 最便宜的端点:{} 进、对象数组出、日缓存足矣)。
请求字段
空对象 {}——零字段(租户由签名头的 Key-Id 解析)。
响应字段(data[] 的厂商对象)
| 字段 | 出现 | 说明 | 示例 |
|---|---|---|---|
| code | 总是有 | 厂商族码——与 games/list.provider、campaigns/create.provider 同一把值,全 API 的 provider 值都来自这里 | "pg" |
| name | 总是有 | 人读展示名(lobby 厂商分区用的就是它) | "PG Soft" |
| categories | 总是有 | 该厂商覆盖的品类 = 本面该族目录中(运营已上架)游戏 category 的去重集合;一个类目下最后一款下架即从此消失 | ["slots"] |
| currencies | 总是有 | 厂商级支持币种 = 本面该族各实例登记的 currencies 的并集(运营按实例登记,未登记缺省 CNY,与 games/list 同源) | ["BRL","USD"] |
| status | 总是有 | active | disabled——目前恒为 active:列表只含本面当前开放的厂商族;厂商族整体停用时直接从列表消失、某条产品线停用时从 products 中消失,lobby 整区下架以此为准(disabled 暂不下发) | "active" |
| products | 不一定有 | 多产品线厂商才出现:[{code, name}],供 lobby 分区与 provider_product 取值发现 | [{"code":"card","name":"DB Card"}] |
示例
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/currencies 上线必须
同样是零参数端点(请求 {})。方向 B 的地基:全 API 金额字符串的小数位权威表——回调金额解析与余额存储精度都依赖它(USDT 这类加密货币不在 ISO 4217 里,精度只能问它)。
请求字段
空对象 {}。
响应字段(data[] 的币种对象)
| 字段 | 出现 | 说明 | 示例 |
|---|---|---|---|
| code | 总是有 | 币种码:法币 = ISO 4217 三字母;加密货币 = 3–4 位字母数字 | "BRL" / "USDT" |
| decimals | 总是有 | 金额字符串的小数位数——全 API(含方向 B 回调)的解析与存储精度按此 | 2 / 6 |
| type | 总是有 | fiat | crypto | "fiat" |
示例
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/transactions/list 可选 · 推荐
对账流:cursor 分页 + 时间窗过滤(from/to 必填),数据源是我们自己的账本(非厂商透传)。响应是 Transaction 对象分页(对象全貌见下一节 transactions/get)。
方向 B 的三件事都在这里可见:①每日对账兜底(投递机制失手时,差异在这里捕获、事后 adjustment 修正);②delivery_status / delivery_attempts——每笔回调投没投到你、重试了几次,像 Stripe 的 webhook attempts 一样可查;③钱包回调里刻意不带的元数据(彩池贡献明细 jackpot、厂商活动明细 event)在 Transaction 对象上承接,数据不丢。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| from | string | 条件必填 | 时间窗起点(RFC 3339,按 occurred_at 过滤);不带 round_id 时必填,带 round_id 时可省略(回合天然有界) | "2026-07-02T00:00:00.000Z" |
| to | string | 条件必填 | 时间窗终点(口径同 from) | "2026-07-03T00:00:00.000Z" |
| round_id | string | 可选 | 按回合过滤(rnd_ 前缀)——回合全貌在此取:回合内多笔按 sequence 排序即可(2026-07-15 修订,原 transactions/get 的回合查询挪到此处) | "rnd_01H…" |
| cursor | string | 可选 | 上页响应的 next_cursor;首页省略或传 null。黑盒游标:内容不作承诺,原样传回即可 | "eyJ…" |
| limit | int | 可选 | 每页条数(≤5000) | 2000 |
| player_id | string | 可选 | ≤ 64 字符;按玩家过滤 | "wang-123" |
| provider | string | 可选 | 按厂商过滤(厂商族码,来自本面 providers/list 的 code) | "pg" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data | 总是有 | Transaction 对象数组(逐字段见下一节 transactions/get) |
| next_cursor | 总是有 | 下一页游标;末页为 null |
| has_more | 总是有 | 还有没有下一页 |
示例
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/transactions/get 可选 · 推荐
按 transaction_id 查单笔详情(点查),最近 1 个月在线可查(更早归档调取,与列表查询窗口同口径)、带完整投递日志与 jackpot / event / campaign 元数据。回合维度查询走 transactions/list 的 round_id 过滤(2026-07-15 修订:单对象响应装不下回合多笔,get 语义回归点查)。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| transaction_id | string | 必填 | 查单笔(txn_ 前缀) | "txn_01H…" |
响应字段(data 的 Transaction 对象逐字段)
| 字段 | 出现 | 说明 |
|---|---|---|
| transaction_id | 总是有 | 交易 ID(txn_),与方向 B 回调里的同值 |
| type | 总是有 | debit | credit | settle | rollback——对应四种资金回调 |
| source | 总是有 | 资金语义标签,与方向 B 的 source 同一枚举族 |
| player_id | 总是有 | 你侧玩家标识(与回调内同值) |
| currency | 总是有 | 交易币种 |
| bet | 不一定有 | 投注金额(金额字符串;type = debit / settle 时出现) |
| win | 不一定有 | 派彩金额(金额字符串;type = credit / settle 时出现) |
| round_id | 不一定有 | 回合 ID(rnd_;无回合事件如小费不带) |
| sequence | 不一定有 | 回合内单调递增序号 |
| round_finished | 不一定有 | 该回合是否已收口 |
| game_code | 不一定有 | 游戏标识(<provider>:<游戏段>,不透明键,原样回传) |
| provider | 不一定有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) |
| provider_product | 不一定有 | 厂商产品维度键回显(多产品线厂商才出现,如 db 的 card)——按产品线对账用 |
| session_id | 不一定有 | 会话 ID(ses_) |
| occurred_at | 总是有 | 事件发生时刻(RFC 3339) |
| jackpot | 不一定有 | 彩池明细 {contribution, win, breakdown[{pool_id, contribution, win}]}——厂商给了才出现;钱包回调刻意不带,数据去处在此 |
| campaign_id | 不一定有 | 源于你的活动时出现(与回调内同值) |
| event | 不一定有 | 厂商侧活动明细 {id, type, details}(锦标赛/奖品雨——非你发起,明细走对账不走回调) |
| delivery_status | 总是有 | pending | delivered | retrying | frozen | failed——这笔回调投没投到你。frozen = 你的端点返回了 3xx 跳转或非 429 的 4xx(配置故障),我们已暂停自动重试并告警,修复确认后恢复(v1.x 加法新增值,未识别请按未知值兜底) |
| delivery_attempts | 不一定有 | 投递日志数组 {attempted_at, http_status, outcome}(像 Stripe 的 webhook attempts) |
| operator_transaction_id | 不一定有 | 你在响应里回执过的账目 ID(对账互查) |
示例
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/records/list 可选 · 推荐
游戏记录(注单)流:每一局结束后,我们把你这位玩家在这一局的输赢汇成一条记录(部分游戏按手 / 按注给出),供玩家游戏记录、客服查询与按局统计用。请求字段、分页、时间窗与转账钱包面的 records/list 逐字相同;按 record_id 去重入库。可查窗口统一最近 1 个月(from 越界 422)。
资金以钱包回调(方向 B)与 transactions/list 为准。注单只汇总已入账的资金流水(你拒掉的扣款不计);活动 / 奖励类入账(不挂在某一局上的奖金、返现等)不进注单。注单与资金流水的对应关系见下方 round_id 一行。
注单在这一局结束后出现,通常几分钟内;部分游戏的注单可能在局结束后数小时才出现(这类游戏的「局结束」要等一段时间没有新的下注 / 派彩才能确认),这类游戏的注单自 2026-10-25 起提供。之后若同一局又有迟到的派彩或冲正,会按下方「改判」规则追加记录。部分彩票、体育类游戏目前只给出有派彩的注单,未中奖的注单暂不出现;这类游戏的下注额以 transactions/list 为准。
from / to 比较的是 recorded_at,不是 started_at——迟到的记录、改判追加的冲销 / 更正记录都出现在它们进入时刻所在的窗里,按时间窗向前增量拉取不会漏行。建议时间窗终点比当前时刻至少早 1 分钟(或相邻窗重叠 1 分钟,按 record_id 去重)。
多数游戏一局一条(同一局多人同桌时,每位玩家各一条);进桌带入、离桌带出的桌游(如棋牌)每手一条;每注独立结算的游戏(如电竞)每注一条,未结算时先给一条 unsettled 快照、结算后另追加一条 settled。
原记录永不修改;改判时追加①冲销记录(source=adjustment、金额为被冲销记录的负值、ref_record_id 指向它)与②更正记录(改判后的全量值、ref_record_id 同指原记录)。金额求和只算 status=settled 的记录,净额恒正确。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| from | string | 必填 | 开始时间(含),RFC 3339;比较记录的 recorded_at | "2026-07-05T00:00:00.000Z" |
| to | string | 必填 | 结束时间(不含):from ≤ recorded_at < to | "2026-07-06T00:00:00.000Z" |
| cursor | string|null | 可选 | 黑盒游标,原样传回 | null |
| limit | integer | 可选 | 1–2000,缺省 500 | 2000 |
| player_id | string | 可选 | ≤ 64 字符;按玩家过滤 | "wang-123" |
| provider | string | 可选 | 按厂商过滤(厂商族码,来自本面 providers/list 的 code) | "pg" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data | 总是有 | Record 对象数组(逐字段见下表) |
| next_cursor | 总是有 | 下一页游标;末页为 null |
| has_more | 总是有 | 还有没有下一页 |
Record 对象(records/list 与 records/get 同形)
字段名与转账钱包面的 Record 相同;round_id、valid_bet 两个字段在本面的含义与转账面不同,同时接两面的请特别留意。
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| record_id | string | 总是有 | 唯一 ID(rec_)——去重主键、records/get 查询键 | "rec_01H8XGJWBW" |
| ref_record_id | string | 不一定有 | 冲销 / 更正记录指向被冲销原记录的 record_id | "rec_01H8XGJWBW" |
| player_id | string | 总是有 | 玩家标识 | "wang-123" |
| provider | string | 总是有 | 厂商族码(来自本面 providers/list 的 code) | "pg" |
| provider_product | string | 不一定有 | 厂商产品维度键(多产品线厂商才出现,如 db 的 card) | "card" |
| game_code | string | 总是有 | 游戏键,与目录一致(不透明键,原样回传) | "pg:fortune-tiger" |
| category | string | 总是有 | 品类(值域同 games/list);查不到时为空串 "" | "slots" |
| currency | string | 总是有 | 币种 | "BRL" |
| round_id | string | 不一定有 | 回合 ID(rnd_)——与钱包回调里的 round.id、transactions/list 里的 round_id 同值。用它加上 player_id 调 transactions/list,即得这一行涉及的资金流水(同一局多人同桌时各玩家共用一个回合 ID,不带 player_id 会把同桌其他玩家的流水一起查出);本行的 bet / win 只计其中已入账的。不按局结算的游戏(进桌带入、离桌带出的桌游;每注独立结算的电竞)不带 round_id——这类游戏按整场(或整段)汇总核对 | "rnd_01H8XGJWBW" |
| bet | string | 总是有 | 投注额;仅冲销记录(source=adjustment)可为负 | "10.00" |
| valid_bet | string | 不一定有 | 有效投注额——只有游戏本身给出了有效投注时才出现;拿不到就不给(我们不拿投注额冒充)。不出现时请自定反水口径(通常按 bet) | "9.50" |
| win | string | 总是有 | 派彩额;玩家输赢 = win − bet | "15.50" |
| source | string | 总是有 | game(常规局)/ adjustment(冲销 / 更正);可增补 | "game" |
| status | string | 总是有 | unsettled | settled | canceled | refunded——口径同转账钱包面:金额求和只算 settled;canceled / refunded 金额保留原注额、不参与求和 | "settled" |
| provider_record_id | string | 不一定有 | 厂商侧单号——客诉排障时报给我们 | "8865512900" |
| started_at | string | 总是有 | 本局开始时间 | "2026-07-05T10:11:02.000Z" |
| settled_at | string | 不一定有 | 结束 / 结算时间;unsettled 时缺省 | "2026-07-05T10:11:05.000Z" |
| recorded_at | string | 总是有 | 记录进入 PlayRouter 的时刻——时间窗按它切 | "2026-07-05T10:12:30.000Z" |
示例
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/records/get 可选 · 推荐
客诉单查:按 record_id 查单笔游戏记录,最近 1 个月在线可查(更早归档调取,与列表查询窗口同口径)。查无此记录(含不属于你的记录)404 record_not_found。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| record_id | string | 必填 | rec_ 前缀(records/list 返回) | "rec_01H8XGJWBW" |
| locale | string | 可选 | 预留(详情页语言);目前无详情页,可省略 | "pt" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data | 总是有 | Record 对象(与 records/list 同一形状) |
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/campaigns/create 可选 · 发活动才接
一个模型发三种赠品:type: freespin | freechip | bonus_buy(与方向 B 的派彩 source 一一对称:freespin↔freespin_payout、freechip↔freechip_payout、bonus_buy↔bonus_buy_payout)。名单内嵌:player_ids 随 create 提交(≤1000/次)——给 1000 人发 = 1 次调用,逐玩家成败在响应 results 给出(整批 2xx,单人失败不连坐)。
type 决定两个骨架字段的语义
| type | bet_amount 是什么 | free_game_count 是什么 | 典型 provider_options |
|---|---|---|---|
| freespin | 单次免费旋转的投注额 | 旋转次数(必填) | 厂商倍数等 |
| freechip | 单枚筹码面值(须 ≥ 游戏最小注,厂商侧校验) | 筹码枚数(必填) | max_win 等 |
| bonus_buy | 功能购买的基础投注额(成本 = 基础注 × 厂商 cost_multiplier) | 固定 1,可省略 | bonus_type(值域经 options 的 schema enum 发现) |
请求字段
| 字段 | 必填 | 说明 | 示例 |
|---|---|---|---|
| idempotency_key | 必填 | 幂等键(见方向 A 总览)——5xx/超时后同 key 重试,绝不发出两份活动 | "camp-2026w27-vip" |
| type | 必填 | freespin | freechip | bonus_buy | "freespin" |
| name | 必填 | 活动名——create 回执与对账日志里的可读标签 | "vip-july-freespins" |
| provider | 必填 | 厂商显式单列(厂商族码,来自本面 providers/list 的 code;所有 game_codes 必须属于它,校验更早) | "pg" |
| game_codes | 必填 | 游戏数组(同一厂商) | ["pg:fortune-tiger"] |
| currency | 必填 | 一活动一币种(bet_amount 语义无歧义);多币种发放 = 多个活动 | "BRL" |
| free_game_count | 按 type | 整个活动的总量——玩家可在 game_codes 内任意游戏消耗,不是每游戏各一份 | 10 |
| bet_amount | 必填 | 金额字符串。games/list 该游戏带 bet_levels 时从中选值;不带(厂商不给面值表,属常态)则按厂商活动文档给的合法面值填——金额→厂商索引的翻译我们做 | "0.50" |
| starts_at | 必填 | 活动开始时刻(起止都有 = 可预建定时活动) | "2026-07-04T00:00:00.000Z" |
| ends_at | 必填 | 活动截止时刻。厂商时长上限先查 options 的 constraints;违规 → 422 + fields.ends_at 明细 | "2026-07-11T00:00:00.000Z" |
| player_ids | 必填 | 发放名单(≤1000/次)。活动 = 一次性名单快照——中途追加 = 另建活动 | ["wang-123","li-456"] |
| provider_options | 可选 | 类型/厂商专属参数,按 options 返回的 (provider, type) JSON Schema 校验;不合 → 422 provider_options_invalid | {"multiplier":10} |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data.campaign_id | 总是有 | 活动 ID(cmp_)——cancel 的唯一引用键,须持久化 |
| data.status | 总是有 | 初始恒 pending;状态全枚举 pending | started | ended | cancelled(经 create / cancel / get / list 响应可见) |
| data.results[] | 总是有 | 名单逐玩家答复 {player_id, status: ok|failed, code?}——整批 2xx、单人失败不连坐;code 见下表。create 首次应答只反映受理校验,厂商侧逐玩家结果在投放后经 campaigns/get 给出 |
响应里的 campaign_id 是 cancel 的唯一引用键。响应丢失时 7 天内可凭同 idempotency_key 重放找回;超期可经 campaigns/list 按名称/时间定位、campaigns/get 取回全量(v1.x 纯加法补齐,2026-07-09——查询面是兜底,不是持久化义务的替代)。
campaigns/options 对该厂商回空数组,即该厂商暂不支持活动发放;此时建活动返回 403 feature_not_enabled——不是你的配置问题,等我方通知开通即可。
results:200 里唯一的逐项业务失败
| results[].code | 语义 |
|---|---|
| player_id_invalid | player_id 格式不合法(受理校验;预留码——目前名单含不合法 ID 时整单回 422 validation_failed) |
| campaign_player_limit_reached | 活动人数达厂商上限,本项未加入 |
| provider_rejected | 厂商拒绝向该玩家发放——投放后才知道,经 campaigns/get 查看;本项未发放,名单内其余照常生效 |
你 → PLAYROUTER
(点创建)
PLAYROUTER → 你
POST/v1/seamless/campaigns/cancel 可选 · 发活动才接
取消活动(变更类,带幂等键)。请求就两个字段:idempotency_key + campaign_id(create 响应发的 cmp_,全局唯一,不需要厂商辅助定位)。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| idempotency_key | string | 必填 | 幂等键(变更类统一规则,见方向 A 总览) | "cancel-cmp01H" |
| campaign_id | string | 必填 | 要取消的活动(create 响应发的 cmp_,全局唯一) | "cmp_01H…" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data.campaign_id | 总是有 | 回显活动 ID |
| data.status | 总是有 | 终态 "cancelled"——回执带终态,不用猜取消成没成 |
pending 状态必可取消(活动还没到厂商,我们自己说了算);started 取消受理即回执终态 cancelled,厂商侧未使用发放的撤回由平台后台异步完成(撤回失败自动重试;发放随活动截止时刻自然失效兜底),你无需跟进。422 campaign_state_invalid 只余两种形态:活动已 ended;或该厂商没有撤回能力(options 的 constraints.cancellable 为 false,可先查)时取消 started 活动。对已取消的活动再次取消,返回同一终态回执 cancelled,不报错。
你 → PLAYROUTER
(选一个活动)
PLAYROUTER → 你
POST/v1/seamless/campaigns/options 可选 · 发活动才接
厂商参数发现,按 (provider, type) 组合返回两样东西:json_schema 管「怎么填合法」——SDK 发请求前本地校验 provider_options;provider_data 管「填哪个划算/可用」——bonus_buy 的逐游戏成本表 cost_multipliers、freechip 的动态面值 chip_values、通用 constraints(时长上限、可否取消等 JSON Schema 表达不了的约束)。data 恒为数组:指定 type = 单元素,缺省 = 该厂商全部 type 逐项。
查选项对某厂商回空数组时,即该厂商暂不支持活动发放;此时对它建活动返回 403 feature_not_enabled——不是你的配置问题,等我方通知开通即可。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| provider | string | 必填 | 查哪家厂商(厂商族码,来自本面 providers/list 的 code) | "pg" |
| type | string | 可选 | freespin | freechip | bonus_buy;缺省 = 返回该厂商全部 type 逐项 | "freespin" |
响应字段(data[] 按 (provider, type) 逐项)
| 字段 | 出现 | 说明 |
|---|---|---|
| provider | 总是有 | 本项对应的厂商 |
| type | 总是有 | 本项对应的类型(freespin | freechip | bonus_buy) |
| json_schema | 总是有 | 该组合下 provider_options 的 JSON Schema——SDK 发请求前本地校验;值域类信息(bonus_type 枚举、离散面值)经 enum 承载 |
| provider_data | 总是有 | 数据类发现信息,无数据时为 null(以下三个子字段按需出现) |
| provider_data.cost_multipliers[] | 不一定有 | type=bonus_buy——逐游戏成本表 {game_code, bonus_type, multiplier}(发放成本 = bet_amount × multiplier) |
| provider_data.chip_values | 不一定有 | type=freechip——动态面值厂商按币种的合法面值数组 |
| provider_data.constraints | 不一定有 | 通用厂商约束:max_duration_days(活动时长上限)· cancellable(可否取消)等——JSON Schema 表达不了的跨字段/能力类限制,创建前自查 |
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/campaigns/list 可选 · 发活动才接
活动列表查询(v1.x 纯加法补齐,2026-07-09——"最小面"原则下实需回补的演进案例):cursor 分页列出你创建过的活动,status / type / provider / 创建时间窗可过滤。典型用途:create 响应丢失且幂等超期后按名称/时间找回 campaign_id;营销后台的活动列表页。只读、无幂等键。返回活动骨架(不含逐玩家 results——全量用 campaigns/get)。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| cursor | string | 可选 | 上页 next_cursor 原样带回 | "eyJv…" |
| limit | integer | 可选 | 每页条数,默认 20、上限 100 | 20 |
| status | string | 可选 | 过滤:pending | started | ended | cancelled | "started" |
| type | string | 可选 | 过滤:freespin | freechip | bonus_buy | "freespin" |
| provider | string | 可选 | 过滤:厂商代号(厂商族码,来自本面 providers/list 的 code) | "pg" |
| from | string | 可选 | 创建时间窗起(RFC 3339,按 created_at 过滤) | "2026-07-01T00:00:00.000Z" |
| to | string | 可选 | 创建时间窗止 | "2026-07-09T00:00:00.000Z" |
响应字段(data[] 的活动骨架;外层 next_cursor / has_more 与 transactions/list 同款)
| 字段 | 出现 | 说明 |
|---|---|---|
| campaign_id | 总是有 | cmp_ |
| name / type / provider / currency | 总是有 | create 时所传原值 |
| status | 总是有 | 当前状态(全枚举见 create) |
| starts_at / ends_at | 总是有 | create 时所传原值 |
| created_at | 总是有 | create 受理时刻(本次加法新增字段,list/get 同出) |
| player_count | 总是有 | 名单人数(逐玩家明细用 campaigns/get 取) |
POST/v1/seamless/campaigns/get 可选 · 发活动才接
按 campaign_id 查单个活动全量(v1.x 纯加法补齐,2026-07-09):create 请求回显 + 当前 status + 名单逐玩家 results(与 create 响应同构,反映发放时点结果)。查无此单或不属于你 → 404 campaign_not_found(防枚举口径)。只读、无幂等键。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| campaign_id | string | 必填 | create 响应发的 cmp_(或经 campaigns/list 找回) | "cmp_01H…" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| campaign_id / name / type / provider / game_codes / currency / bet_amount / free_game_count / starts_at / ends_at / provider_options | 总是有(free_game_count / provider_options 随 create 传否) | create 请求回显 |
| status | 总是有 | 当前状态 |
| created_at | 总是有 | create 受理时刻 |
| results[] | 总是有 | 名单逐玩家 {player_id, status: ok|failed, code?}——与 create 响应同构 |
POST/v1/seamless/jackpots/feed 可选
彩池当前金额的只读缓存 feed(TTL 分钟级)。典型用途是 lobby 展示彩池金额(跑马灯、游戏卡角标等)——数据怎么用是你的自由,我们只设一条硬约束:非结算依据(派彩以方向 B 的 credit(source=jackpot) 回调为准)。currency 必填单币种查询,amount 以请求币种计(换币种再查即可)。pool_id 与对账里 Transaction.jackpot.breakdown[].pool_id 同一命名空间——feed、派彩、对账三处对得上。无彩池数据的厂商游戏不出现在响应中。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| currency | string | 必填 | 查询币种——amount 以它计价(换币种再查) | "BRL" |
| provider | string | 可选 | 按厂商过滤(厂商族码,来自本面 providers/list 的 code) | "pp" |
| game_code | string | 可选 | 按游戏过滤(不透明键,原样传) | "pp:jackpot-game" |
响应字段(data[] 逐游戏条目)
| 字段 | 出现 | 说明 |
|---|---|---|
| game_code | 总是有 | 带彩池的游戏(与 games/list 同一把键) |
| provider | 总是有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) |
| pools[] | 总是有 | {pool_id, amount}——pool_id 与对账 Transaction.jackpot.breakdown[].pool_id 同一命名空间;amount 以请求币种计(金额字符串) |
| updated_at | 总是有 | 数据时刻(缓存 TTL 分钟级)——非结算依据 |
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
POST/v1/seamless/sessions/terminate 可选 · best-effort
把玩家请出游戏(风控/自我排除场景)。请求给 player_id(终止其全部活跃会话)或 session_id(指名终止)二选一;非资金、天然幂等,不带 idempotency_key;响应 200 + {"data":{}}(受理即 200,不担保厂商侧结果)。
请求字段
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| player_id | string | 二选一 | ≤ 64 字符;终止该玩家的全部活跃会话 | "wang-123" |
| session_id | string | 二选一 | 指名终止单个会话(ses_ 前缀,sessions/create 响应发的) | "ses_01H…" |
响应字段
| 字段 | 出现 | 说明 |
|---|---|---|
| data | 总是有 | v1 为空对象 {}——受理即 200(best-effort,不担保厂商侧踢出结果);演进按加法补逐厂商扇出结果 |
这是尽力而为的踢出扇出(能否踢出、踢出后玩家能否重登,效果因厂商而异)。RG/自我排除的硬闸门永远在你的钱包侧:对 debit 答 declined(player_blocked)——实时、资金边界强制、不依赖任何厂商。terminate 是体验层(把人请出桌),player_blocked 是资金层(让他玩不了)。
你 → PLAYROUTER
(点查询)
PLAYROUTER → 你
方向 B:实现 5 个钱包回调 DIRECTION B
从这里到「错误与重试」都是方向 B——我们调用你注册的 {base}/wallet/*。角色再次反转:恒 200 + JSON(业务拒绝放 body)、幂等键是 transaction_id(我们生成)、你只会写三种响应。这 5 个回调全部属上线必须面——资金边界没有可选项。
响应契约:你只会写三种 JSON
| reason(declined 时) | 什么时候用 | balance 要不要带 |
|---|---|---|
| insufficient_funds | 余额不够扣 | 必带(游戏客户端靠它刷新显示) |
| player_blocked | 玩家被禁投 / 自我排除 | 必带 |
| bet_limit_exceeded | 责任博彩限额(自设或监管) | 必带 |
| player_not_found | 玩家在你系统里不存在 | 可省(钱包定位不到) |
| currency_mismatch | 交易币种不在该玩家支持的币种范围内(玩家可多币种) | 可省 |
金额与时间
| 类型 | 格式 | 示例 |
|---|---|---|
| 金额(请求) | 字符串十进制,^\d{1,18}(\.\d{1,12})?$,恒非负——方向由端点表达(debit 恒扣、credit 恒入),你永远不用判断正负号 | "10.00" · "0.000001" · "0.00" |
| balance(响应) | 同上,但唯一允许负号(credit/rollback 强制执行后可能为负,见铁律①) | "114.50" · "-95.00" |
| 时间 | RFC 3339 UTC 恒 3 位毫秒,字段名以 _at 结尾 | "2026-07-03T12:00:00.000Z" |
我们发出的金额(回调请求、各查询端点的响应)小数位按 currencies 公布的 decimals 写满,零也一样(2 位小数币种写 "0.00",6 位写 "0.000000")。判断「是否为零」「两笔是否相等」时,先解析成十进制数(Java BigDecimal.compareTo、Python Decimal、JS 用十进制库)再比较;amount == "0" 这类字符串比较会出错。
幂等:一张表就够
每个资金回调带全局唯一的 transaction_id(txn_ 前缀)。网络重试时同一笔会再来——你要保证只记一次账,重复的答 duplicate: true + 当前余额。不需要保存原响应体。
收到任何回调时,按这个顺序判断
→ 返回 400(框架默认行为即可,零代码)。
→ 200 + {"status":"error","reason":"invalid_signature"}。
→ 200 + {"status":"ok","balance":当前余额,"duplicate":true},到此为止,什么都不改。
玩家存在吗?币种对吗?余额够吗?限额允许吗?不行 → 200 + declined + reason。credit 和 rollback 跳过这一步——它们没有拒绝路径。
动余额 + 写幂等表,一起提交。→ 200 + ok + 最新余额。
→ 返回 5xx。我们会按 5s→1m→5m→…→24h 的时间表自动重试(全表见「错误与重试」;幂等表保证重试安全)。
我们对你的承诺(这些麻烦你永远见不到)
游戏厂商的事件是乱的:win 可能先于 bet 到达、rollback 可能引用一笔从未送达的交易。这些混乱全部在 PlayRouter 层终止——下面这张动图里,右边(你)永远只看到干净有序的流。
不用写乱序状态机、不用建墓碑表、不用兜底"未知交易的冲正"——来一条记一条账。收口也有保证:每个回合最终恰好一次 round.finished: true(收口关闭的是游戏流;收口后若来迟到冲正,照常按 rollback 处理即可,不影响该保证)。
source 全解:每种钱是什么、走哪个端点
端点名回答"钱往哪个方向动"(debit 扣 / credit 入 / settle 一扣一入),source 回答"这是什么钱"。你的记账代码不需要按 source 分支——它只是写进账本的分类标签(报表、返水口径、风控用)。铁律③:没见过的 source 值照常入账/扣款。
端点 × source 矩阵
| source | 走哪个端点 | 这是什么钱 | 带 round? | 带 campaign_id? |
|---|---|---|---|---|
| bet | debit | 游戏下注(两段式厂商的扣款) | 有 | 无 |
| bonus_buy | debit | 功能购买——玩家花钱直接买进免费特性玩法(如 100 倍注价直接进免费旋转) | 有 | 无 |
| tip | debit | 真人桌小费(给荷官)。不属于任何一局,可被冲正 | 无 | 无 |
| entry_fee | debit | 锦标赛/活动报名费 | 无 | 可能有 |
| adjustment | debit 或 credit | 事后更正(真人桌复核改判等):要收回走 debit、要补发走 credit——唯一双向的 source,方向永远由端点表达,你不用碰负数 | 通常有 | 无 |
| game_win | credit | 游戏派彩(两段式厂商的赢钱),常带 related_transaction_id 指回原下注 | 有 | 无 |
| freespin_payout | credit | 免费旋转结算——你发的赠品旋转玩完,总赢额一笔入账(之前没有对应 debit:玩的是赠品不是玩家的钱) | 可能无 | 有 |
| freechip_payout | credit | 免费筹码结算——同上,赠品形态是筹码(桌面/真人用) | 可能无 | 有 |
| bonus_buy_payout | credit | 功能购买的派彩。玩家自费买的配对 bonus_buy 扣款;你活动白送的则无扣款、带 campaign_id | 有 | 白送时有 |
| promo_win | credit | 活动奖金:锦标赛奖金、奖品雨、厂商促销。不属于任何一局 | 无 | 你办的活动才有 |
| jackpot | credit | 彩池派彩(累积奖金命中)。池明细不进回调,走对账 transactions/get | 有 | 无 |
| round_finish | credit | 零额回合收口——没钱要动但这局该关了(amount 为零,如 "0.00";余额不变),读 round.finished:true 关局即可 | 有(finished:true) | 无 |
| game | settle | 合笔结算(少数厂商):一局的扣与派一笔完成。settle 只有这一个 source | 有 | 无 |
实际调用长什么样(公共信封字段省略,只看区别)
八个场景,你的 handler 还是那三段直线代码:debit 扣、credit 入、settle 一扣一入。source 原样写进账本即可——它服务的是报表和运营(哪些钱算流水、哪些算营销成本、哪些是更正),不服务记账分支。
5 个回调 · 端点参考 + Playground
下面每个端点都有可交互 Playground,共享同一个沙箱钱包(就是这条常驻状态栏)。字段表读法:请求表的「总是有 / 不一定有」说的是我们传不传——「总是有」直接用、「不一定有」判空再用,两种都不构成拒绝理由;响应表的「必填 / 可选」说的才是你要不要返回。改改金额、连点两次同一笔、把余额打成负数——把所有边界都亲手试一遍。
POST{base}/wallet/balance 上线必须
查玩家当前余额。游戏启动前我们会先调它做预检(所有厂商统一),游戏内刷新余额也走它。只读、无幂等键、无副作用——五个端点里最简单的一个。
请求字段
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| player_id | string | 总是有 | 你系统里的玩家 ID——就是你在 sessions/create 时传给我们的那个,原样带回 | "wang-123" |
| currency | string | 总是有 | 查哪个币种的钱包。法币 = ISO 4217 三字母;加密货币 3–4 位 | "BRL" |
| game_code | string | 不一定有 | 有游戏上下文时给(<provider>:<游戏段>,不透明键)。有的厂商查余额不带游戏 | "pg:fortune-tiger" |
| session_id | string | 不一定有 | 我们的会话 ID(ses_)。排障时报给支持用,不需要校验 | "ses_01H…" |
| session_payload | string | 不一定有 | 你启动游戏时塞的透传串,原样奉还(用于零查表路由回你的内部上下文) | "wsess-8f2|ios" |
你要返回什么
| 字段 | 必填 | 说明 |
|---|---|---|
| status | 必填 | "ok";玩家不存在/币种不符可 "declined" |
| balance | 必填 | 当前余额,字符串十进制 |
PLAYROUTER → 你
(点上面的按钮)
你 → PLAYROUTER
POST{base}/wallet/debit 上线必须
从玩家钱包扣款。方向永远是扣、金额永远非负——你不用判断正负号。业务上不能扣(余额不足等)就返回 declined,这是合法答复不是错误,我们收到就终止、绝不重试。
请求字段(完整)
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| transaction_id | string | 总是有 | 幂等键。全局唯一(txn_ 前缀)。见过 → 答 duplicate:true,别记账 | "txn_01H…" |
| player_id | string | 总是有 | 你系统里的玩家 ID(sessions/create 时你传的,原样带回) | "wang-123" |
| currency | string | 总是有 | 交易币种(与本次会话/回合的币种一致)。玩家可支持多币种;此币种不在该玩家支持范围内 → declined currency_mismatch | "BRL" |
| amount | string | 总是有 | 扣多少。字符串十进制、非负 | "10.00" |
| source | string | 总是有 | 这笔扣款是什么:bet 游戏下注 · bonus_buy 功能购买 · tip 真人桌小费 · entry_fee 锦标赛报名费 · adjustment 事后更正。没见过的值也照扣(我们上新类型你零改动) | "bet" |
| round | object | 不一定有 | {id, sequence, finished}——回合 ID、回合内序号(1,2,3…可用于断言顺序)、收口标志。小费等无回合事件不带 | {"id":"rnd_…","sequence":1,"finished":false} |
| campaign_id | string | 不一定有 | 这笔钱源于你创建的活动时出现(cmp_),到账即可归因 | "cmp_01H…" |
| game_code | string | 不一定有 | 游戏标识(<provider>:<游戏段>,不透明键,原样入账),记账/风控归因用;有游戏上下文时给 | "pg:fortune-tiger" |
| provider | string | 不一定有 | 厂商代号(厂商族码,来自本面 providers/list 的 code;game_code 的前半段单独再给一份,省你拆字符串) | "pg" |
| provider_product | string | 不一定有 | 厂商产品维度键回显(多产品线厂商才出现,如 db 的 card)——按产品线记账/报表用,零处理义务 | "card" |
| session_id | string | 不一定有 | 我们的会话 ID(ses_)。排障时报给支持用,零处理义务 | "ses_01H…" |
| session_payload | string | 不一定有 | 你启动游戏时塞的透传串,原样奉还(零查表路由回你的内部上下文),零处理义务 | "wsess-8f2|ios" |
| occurred_at | string | 总是有 | 事件发生时刻(RFC 3339),直接可入账本 | "2026-07-03T12:00:00.000Z" |
你要返回什么
| 字段 | 必填 | 说明 |
|---|---|---|
| status | 必填 | "ok" 记账成功 · "declined" 业务拒绝(余额不足等)· "error" 验签失败 |
| balance | 必填 | 扣款后余额;declined 为 insufficient_funds / player_blocked / bet_limit_exceeded 时也必带 |
| reason | declined 时必填 | 拒绝原因(见通用约定的 reason 表) |
| operator_transaction_id | 可选 | 你侧账目 ID 回执,对账互查用 |
| duplicate | 重复时 | 幂等命中时为 true(返回当前余额、不记账) |
PLAYROUTER → 你
(点投递)
你 → PLAYROUTER
POST{base}/wallet/credit 上线必须
向玩家钱包入账。游戏派彩、免费旋转结算、活动奖金、彩池、零额收口——全走这一个端点,source 告诉你是哪种钱。入账逻辑只写一次,以后我们上任何新资金类型,对你都只是一个新的 source 字符串。
credit 永远不许 declined(唯二例外 player_not_found / currency_mismatch——账本无法定位、拒收合理,PlayRouter 收到即进对账队列 + 双方告警,不重试不丢弃)。玩家赢的钱必须到账——哪怕他此刻不存在余额异常、哪怕入账后你想拒。无法处理(你内部故障)返回 5xx,我们自动重试直到送达。
请求字段(完整)
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| transaction_id | string | 总是有 | 幂等键。全局唯一(txn_ 前缀)。见过 → 答 duplicate:true,别记账 | "txn_01H…" |
| player_id | string | 总是有 | 你系统里的玩家 ID(sessions/create 时你传的,原样带回) | "wang-123" |
| currency | string | 总是有 | 交易币种(与本次会话/回合一致)。玩家可支持多币种;不在其支持范围内 → declined currency_mismatch | "BRL" |
| amount | string | 总是有 | 入多少。字符串十进制、非负(source=round_finish 时为零,如 "0.00") | "24.50" |
| source | string | 总是有 | game_win 游戏派彩 · freespin_payout 免费旋转结算 · freechip_payout 免费筹码结算 · bonus_buy_payout 功能购买派彩 · promo_win 活动奖金 · jackpot 彩池 · adjustment 事后更正 · round_finish 零额回合收口。未知值照收 | "game_win" |
| related_transaction_id | string | 不一定有 | 这笔派彩对应的原下注(txn_)。出现则保证那笔已成功送达过你——放心用它做注赢配对,永不悬空 | "txn_01H…" |
| campaign_id | string | 不一定有 | 源于你的活动时出现——两个活动并行时,这笔派彩算哪个活动的成本一目了然 | "cmp_01H…" |
| round | object | 不一定有 | {id, sequence, finished}。活动奖金等无回合事件不带;round_finish 收口时 finished 恒为 true | {"id":"rnd_…","sequence":2,"finished":true} |
| game_code | string | 不一定有 | 游戏标识(<provider>:<游戏段>,不透明键,原样入账),有游戏上下文时给 | "pg:fortune-tiger" |
| provider | string | 不一定有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) | "pg" |
| provider_product | string | 不一定有 | 厂商产品维度键回显(多产品线厂商才出现,如 db 的 card)——按产品线记账/报表用,零处理义务 | "card" |
| session_id | string | 不一定有 | 我们的会话 ID(ses_),排障用,零处理义务。异步派彩(免费旋转结算等)可能不带——会话早结束了 | "ses_01H…" |
| session_payload | string | 不一定有 | 你的透传串原样奉还,零处理义务 | "wsess-8f2|ios" |
| occurred_at | string | 总是有 | 事件发生时刻(RFC 3339) | "2026-07-03T12:00:00.000Z" |
你要返回什么
| 字段 | 必填 | 说明 |
|---|---|---|
| status | 必填 | "ok" 或 "error"(验签失败)。credit 没有 declined(铁律①)——内部故障返回 5xx 让我们重试 |
| balance | 必填 | 入账后余额(round_finish 时余额不变,照常返回) |
| operator_transaction_id | 可选 | 你侧账目 ID 回执 |
| duplicate | 重复时 | 幂等命中时为 true |
回合结束但没有钱要动时(比如厂商的结束信号迟到),我们发一笔金额为零(如 amount:"0.00")的 credit 来收口。你按普通 credit 处理即可:入账 0(余额不变)、读 round.finished:true 关闭回合。收口保证:每个回合最终恰好一次 finished:true——放心在它上面挂"清理未完成回合表"这类逻辑。唯一例外语义:收口后仍可能到达迟到的 rollback(更正流,不属游戏流)——冲正不改变收口状态,照常处理。
PLAYROUTER → 你
(点投递)
你 → PLAYROUTER
POST{base}/wallet/settle 上线必须
一次调用完成"扣注 + 派彩"。这是少数厂商的形态:多数厂商把下注和派彩分成两次回调(走上面的 debit + credit),少数厂商把一局输赢合并成单笔结算发来——那部分流量走这里。五个端点认证时都要实现,但你实际收到多少 settle 取决于开通了哪些厂商。你在同一个 DB 事务里:扣 bet、加 win、返回最新余额。扣款部分不足 → 整体 declined,没有"扣了一半"这种状态。
请求字段(完整)
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| transaction_id | string | 总是有 | 幂等键。整局一笔、一个键 | "txn_01H…" |
| player_id | string | 总是有 | 你系统里的玩家 ID | "wang-123" |
| currency | string | 总是有 | 交易币种(与本次会话/回合一致)。玩家可支持多币种;不在其支持范围内 → declined currency_mismatch | "BRL" |
| bet | string | 总是有 | 本局下注额(要扣的) | "10.00" |
| win | string | 总是有 | 本局派彩额(要加的)。输了的回合照常发:win:"0.00",此时这笔的意义就是记下注 + 收口 | "24.50" / "0.00" |
| source | string | 总是有 | 恒为 "game"(合笔结算只服务游戏回合) | "game" |
| round | object | 总是有 | {id, sequence, finished}。合笔结算通常即回合终局,finished 常为 true | {"id":"rnd_…","sequence":1,"finished":true} |
| game_code | string | 不一定有 | 游戏标识(<provider>:<游戏段>,不透明键,原样入账) | "pg:fortune-tiger" |
| provider | string | 不一定有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) | "pg" |
| provider_product | string | 不一定有 | 厂商产品维度键回显(多产品线厂商才出现,如 db 的 card)——按产品线记账/报表用,零处理义务 | "card" |
| session_id | string | 不一定有 | 我们的会话 ID(ses_),零处理义务 | "ses_01H…" |
| session_payload | string | 不一定有 | 你的透传串原样奉还,零处理义务 | "wsess-8f2|ios" |
| occurred_at | string | 总是有 | 事件发生时刻(RFC 3339) | "2026-07-03T12:00:00.000Z" |
你要返回什么
| 字段 | 必填 | 说明 |
|---|---|---|
| status | 必填 | "ok" · "declined"(扣款部分不足 → 整体拒绝,没有"扣一半")· "error" |
| balance | 必填 | 结算后余额(= 原余额 − bet + win);insufficient_funds 拒绝时也必带 |
| reason | declined 时必填 | 拒绝原因 |
| operator_transaction_id | 可选 | 你侧账目 ID 回执 |
| duplicate | 重复时 | 幂等命中时为 true |
PLAYROUTER → 你
(点投递)
你 → PLAYROUTER
POST{base}/wallet/rollback 上线必须
撤销一笔早前交易的余额影响:原来是扣的就退回去,原来是入的就收回来。厂商超时取消、真人桌判定作废时会发它。
rollback 不许 declined,余额可以为负。典型场景:玩家赢 100 → 提现走了 → 该局被判作废 → 冲正要收回 100,余额只剩 5 → 记成 -95.00,返回 ok。负数不是系统错误,是在诚实记录玩家欠的钱(下次入金抵扣,你自行风控追偿)。若你的钱包表不支持负数:扣到 0 + 差额挂应收账,同样合规。
① original_transaction_id 必然是你成功处理过的交易——不存在"冲正一笔没见过的交易"这种事(厂商的乱序冲正我们吸收,见上文动图)。② rollback 之后,那笔原交易绝不会再投递给你。
请求字段(完整)
| 字段 | 类型 | 出现 | 说明 | 示例 |
|---|---|---|---|---|
| transaction_id | string | 总是有 | 幂等键——注意这是本次冲正自己的交易 ID(冲正也是一笔交易),别和下一行搞混 | "txn_01J…" |
| original_transaction_id | string | 总是有 | 要撤销哪笔。查你的账本,按那笔的方向反向操作(原为扣则退、原为入则收回)。保证是你成功处理过的交易 | "txn_01H…" |
| player_id | string | 总是有 | 你系统里的玩家 ID | "wang-123" |
| currency | string | 总是有 | 交易币种 | "BRL" |
| round | object | 不一定有 | {id, sequence, finished}——回合可以以冲正收口(finished:true 随冲正携带)。小费冲正等无回合事件不带 | {"id":"rnd_…","sequence":3,"finished":true} |
| game_code | string | 不一定有 | 游戏标识(<provider>:<游戏段>,不透明键) | "pg:fortune-tiger" |
| provider | string | 不一定有 | 厂商代号(厂商族码,来自本面 providers/list 的 code) | "pg" |
| provider_product | string | 不一定有 | 厂商产品维度键回显(多产品线厂商才出现,如 db 的 card)——按产品线记账/报表用,零处理义务 | "card" |
| session_id | string | 不一定有 | 我们的会话 ID,零处理义务(冲正常发生在会话结束后,可能不带) | "ses_01H…" |
| session_payload | string | 不一定有 | 你的透传串原样奉还,零处理义务 | "wsess-8f2|ios" |
| occurred_at | string | 总是有 | 冲正事件时刻(RFC 3339) | "2026-07-03T12:05:00.000Z" |
你要返回什么
| 字段 | 必填 | 说明 |
|---|---|---|
| status | 必填 | "ok" 或 "error"(验签失败)。rollback 没有 declined(铁律①)——余额不够收回也要执行 |
| balance | 必填 | 冲正后余额,可以为负(如 "-95.00"——钱包回调里唯一允许负号的字段) |
| operator_transaction_id | 可选 | 你侧账目 ID 回执 |
| duplicate | 重复时 | 幂等命中时为 true |
PLAYROUTER → 你
(选一笔交易)
你 → PLAYROUTER
错误与重试(我们侧的行为,写给你参考)
| 你返回 | 我们做什么 |
|---|---|
| 200 + ok / declined | 终态。declined 绝不重试 |
| 200 + error(invalid_signature) | 配置故障桶:告警 + 冻结投递(修复确认后恢复),不重试轰炸 |
| 任何 4xx(429 除外——429 按 5xx 进重试时间表) | 同上(按类别解读,401 写成 403 也不出事故) |
| 3xx(跳转) | 同上:我们不跟随跳转,按配置故障处理(告警 + 冻结投递,delivery_status 为 frozen);把注册地址改成最终地址、确认后恢复 |
| 5xx / 超时(10s) | 重试时间表:5s → 1m → 5m → 1h → 6h → 12h → 24h(共 8 次)。同回合后续事件排队等待、不跳队。全部失败 → 对账队列 + 双方告警 + delivery_status:"failed" 可查 |
附录 · 速查
写代码时贴在手边的三张表:ID 前缀、完整错误码、方向 B 的 declined reason。逐字段权威以规范 md / yaml 为准。
ID 前缀约定
| 前缀 | 含义 |
|---|---|
| txn_ | 资金交易(方向 B 回调的幂等键,也是 transactions/get 的查询键) |
| rnd_ | 游戏回合 |
| rec_ | 游戏记录(records/list 去重主键、records/get 查询键) |
| ses_ | 游戏会话(sessions/create 返回) |
| cmp_ | 活动(campaigns/create 返回,cancel 的唯一引用键) |
| req_ | 请求追踪(错误体 / 响应头 X-Request-Id) |
完整错误码目录 · 方向 A(30 个)
完整错误码目录(方向 A · 30 个)
| HTTP | code | 触发场景 |
|---|---|---|
| 400 | malformed_request | body 非合法 JSON |
| 401 | invalid_signature | HMAC 对不上 |
| 401 | invalid_timestamp | t 出 ±300s 窗(重放或时钟漂移) |
| 401 | unknown_key | Key-Id 不存在 / 已摘除 |
| 403 | account_suspended | 账号被停用 |
| 403 | feature_not_enabled | 功能未开通(如活动模块未启用) |
| 403 | provider_not_enabled | 本面当前不提供该厂商(或产品线)——未接入本面,或已被平台停用;以本面 providers/list 为准 |
| 403 | product_not_enabled | 产品线未开通——账号未开通 Seamless 面时,/v1/seamless/… 前缀下的全部端点(含 sessions / games / providers / currencies)均返本码;已开通的 transfer 面前缀不受影响(契约 v2:面由路径前缀承载) |
| 403 | ip_not_allowed | 来源 IP 不在你为该环境配置的白名单内(可选功能;名单为空不校验;无法判定来源 IP 时同码)——把服务器全部出口 IP(双栈含 v4 与 v6)加入 console「API settings」名单,约 30 秒生效 |
| 404 | not_found | 路径不存在 |
| 404 | game_not_found | game_code 不存在 |
| 404 | campaign_not_found | 引用的活动不存在 |
| 404 | transaction_not_found | 引用的交易不存在 |
| 404 | record_not_found | records/get 查无此记录(含不属于你的记录) |
| 404 | session_not_found | 引用的会话不存在 |
| 409 | idempotency_key_reused | 同 key 不同 body(绝不执行,修 bug) |
| 409 | request_in_progress | 同 key 同 body、首请求在途(瞬态)——按 Retry-After 秒数用同 key 重试即命中重放 |
| 413 | payload_too_large | 请求 body 超过大小上限——报文瘦身后重试 |
| 422 | validation_failed | 字段缺失 / 类型错 / 超限(fields 逐字段明细) |
| 422 | currency_not_supported | 平台 / 游戏 / 厂商三级币种不支持,一码讲完。实例:saba 实例只登记 USD,real 模式 saba:lobby + CNY → currency_not_supported(建户 / 打厂商之前拒);demo 不校验,展示币种由厂商侧静默降级。平台侧暂时不可用时 real 启动也返此码,fields.currency = temporarily unavailable(固定值,可按它程序判断)——此时不要换币种,稍后重试同一请求即可 |
| 422 | currency_mismatch | 该玩家在这个厂商(多产品线厂商按产品线)已使用其他币种——同一厂商内一个 player_id 只能使用一个币种。message 点名该币种("Player currency at this provider is CNY."),按它开局,或按币种拆 player_id。仅 sessions/create(real)返回 |
| 422 | game_not_available | 游戏存在但当前不可用(地域 / 下架 / 未到 released_at),或厂商拒绝该玩家进入(如玩家账号被停用)——重试同一请求不会成功。平台侧暂时不可用时 sessions/create 也返此码,fields.game_code = temporarily unavailable(固定值,可按它程序判断)——游戏没有下架,不要从大厅撤下,稍后重试同一请求即可 |
| 422 | demo_not_available | 游戏不支持试玩 |
| 422 | provider_options_invalid | 厂商参数不合 campaigns/options 的 schema |
| 422 | campaign_state_invalid | 活动状态机不允许(活动已 ended,或厂商没有撤回能力时取消 started 活动) |
| 429 | rate_limited | 超配额,按 Retry-After 重试 |
| 500 | internal_error | 我们的故障(带 request_id 报障) |
| 502 | provider_error | 上游厂商故障(仅瞬态——上游的确定性拒绝会映射为对应 4xx 语义码,不会以 502 出现;收到 502 放心重试) |
| 503 | service_unavailable | 维护 / 过载 |
| 504 | provider_timeout | 预留码,目前不会出现;如遇到,按 5xx 处理(变更类同 idempotency_key 重试) |
注:405 / 415 由基础设施层返回、可能不带错误体——按 HTTP 码行动即可,未列入本表。
404 也用于存在但不属于你的资源(越权响应与"不存在"不可分辨——防跨商户枚举探测);403 只表示功能 / 厂商未开通类主体级权限,不涉及具体资源。
200 里唯一的业务失败:campaigns/create 名单逐项 results(3 个)
整批 HTTP 2xx,每个玩家的结果在 results[].status(ok | failed)逐项给出——这是逐项答复不是错误,名单内其余成员照常生效。create 首次应答只反映受理校验,厂商侧逐玩家结果在投放后经 campaigns/get 给出。
| results[].code | 语义 |
|---|---|
| player_id_invalid | player_id 格式不合法(受理校验;预留码——目前名单含不合法 ID 时整单回 422 validation_failed) |
| campaign_player_limit_reached | 活动人数已达厂商上限,本项未加入 |
| provider_rejected | 厂商拒绝向该玩家发放(投放后才知道,经 campaigns/get 查看),本项未发放 |
方向 B · declined reason 枚举(5 个)
全部通过 200 + {"status":"declined","reason":…} 返回——业务答复不是错误;我们收到即终态,绝不重试。
| reason | 适用回调 | 语义 |
|---|---|---|
| insufficient_funds | debit / settle | 余额不足以扣款(须附 balance) |
| player_not_found | 全部 | 玩家在你系统中不存在(balance 可省略) |
| player_blocked | debit / settle | 玩家被禁投 / 自我排除 / 游戏对其禁用(须附 balance) |
| bet_limit_exceeded | debit / settle | 责任博彩限额(玩家自设或监管强制)(须附 balance) |
| currency_mismatch | 全部 | 交易币种不在该玩家支持的币种范围内(玩家可多币种;balance 可省略) |
duplicate: true 是成功答复不是 decline;未知 reason 值我们照收并原样记录(前向兼容)。status 全枚举:ok(含 duplicate 重放)· declined(业务拒绝,终态)· error(非业务处理失败,非终态——进配置故障桶:告警 + 冻结投递)。上线前,认证套件会测什么
幂等与签名
同 transaction_id 重复投递 → duplicate:true 且余额不变 · 篡改 body 验签拒绝 · 时间戳出窗拒绝 · 双 key 轮换期两把都能验
铁律三条
credit 在余额异常时照常入账 · rollback 打出负余额且返回 ok · 未知 source 值照收 · 会话过期后资金回调照常处理
回合与收口
settle win 为零("0.00")输局照常记账 · 零额 round_finish 正确关闭回合 · sequence 单调性 · finished 恰好一次 · 收口后迟到冲正照常处理
业务拒绝
余额不足 declined + 必附 balance · declined 后我们不重试 · 5xx 后同笔重试只记一次账
方向 A · 核心卷
providers/list · currencies 目录拉取 · games/list 增量与翻页正确 · sessions/create 三连降级(locale / demo 币种 / launch_mode)· 同一厂商换币种开局返回 currency_mismatch 并按 message 处理 · 429 按 Retry-After 退避 · 5xx 后同 idempotency_key 重试
方向 A · 活动卷(启用活动模块才考)
create 幂等重放答复一致 · 名单逐项 results 处理(failed 不连坐)· 422 厂商约束(时长 / provider_options schema)· 取消回执处理(started 取消即回 cancelled、再次取消返回同一回执)· create 响应持久化
FAQ
我的钱包表不允许负数怎么办?
扣到 0,差额记进对该玩家的应收账(debt ledger),下次入金先抵扣——协议要求的是 effect-once 记账并返回 ok,内部怎么表示是你的自由。参考实现里带这个模式。
会话过期了,资金回调要拒吗?
绝对不要。合法性只由签名决定。会话真伪我们在厂商边界已经验过了;异步派彩(免费旋转结算等)天然发生在会话死后,按会话拒钱会把玩家赢的钱卡在协议外。
收到不认识的 source 值?
照常入账/扣款,把值原样记进账本即可。这是前向兼容机制:我们上新资金类型时你零代码改动。
为什么响应恒 200,不用 HTTP 语义?
HTTP 码在这套协议里只回答"要不要重试":2xx=终态、5xx=重试、4xx=配置故障(429 除外——429 按 5xx 进重试时间表)。业务结果(ok/declined)是应用层语义,放 body。这样你写错分支也不会触发意外重试。
duplicate:true 需要返回当初的原响应吗?
不需要。返回当前余额 + duplicate:true 即可,你只需存"处理过的 ID",不用持久化响应体。
时钟不准会怎样?
签名头的 t 有 ±300 秒容忍窗。服务器 NTP 正常就没问题;若持续验签失败,先查时钟漂移。
round.sequence 我必须处理吗?
不必须。它是给你的可选防御工具:断言"同回合序号必须递增",若告警说明出了怪事。忽略它,账照样是对的。
注单(records)和交易流水(transactions)有什么区别,对账用哪个?
资金对账用 transactions/list——它是逐笔资金流水,和你收到的钱包回调一一对应。records/list 是按局汇总的游戏记录,给玩家游戏记录、客服查询、按局统计用;它不含活动 / 奖励类入账,也只计已入账的流水,所以不能当资金对账依据。两者用 round_id + player_id 关联。
campaigns/create 的响应丢了,活动找不回来了?
7 天内用同一个 idempotency_key 重发同一请求——拿回的是当初的原响应(带 Idempotency-Replayed: true),campaign_id 就在里面。超过 7 天用 campaigns/list 按名称/时间窗定位活动、再用 campaigns/get 取回全量(含逐玩家 results)。「持久化 create 响应」仍是接入要求,认证套件会测——查询面是兜底,不是替代。
为什么没有 players/add 这类端点?
最小面设计:名单随 create 内嵌(≤1000/次,一次调用发千人),中途追加 = 另建活动。活动查询面(list / get)是这一原则下的实证演进案例——v1 初版刻意不设,实需出现后按纯加法回补(2026-07-09),未发生破坏性变更。
launch_mode:"html" 什么时候用?
你的市场存在域名封锁风险时。默认 url 形态下游戏域名是第三方的,封锁单点不在你手里;html 直出让游戏从你自己的域名伺服(厂商支持时),封锁面收敛为你自己的域名。厂商不支持时自动降级返 url,不报错。游戏能力看 games/list 的 launch_modes。
方向 A 验签失败是 401,方向 B 为什么让我回 200?
角色不同。方向 A 你是调用方,我们用真 HTTP 码告诉你接下来该干什么(401 = 修配置)。方向 B 你是被调方,恒 200 是钱包回调的行业惯例——而且我们按类别解读你的答复:就算你回了 4xx(429 除外——429 按 5xx 进重试时间表)也只会进配置故障桶(告警 + 冻结投递),不会被重试轰炸。
下载与资源
⬇ 方向 A · OpenAPI 3.1(playrouter-seamless-wallet-api-v1.openapi.yaml) ⬇ 方向 B · OpenAPI 3.1(playrouter-seamless-wallet-callbacks-v1.openapi.yaml) ⬇ 本页 Markdown 版 · for LLM(playrouter-seamless-docs.llm.md)两个 OpenAPI 文件可直接导入 Postman / Insomnia 生成请求集合;方向 A 可生成客户端 SDK 桩,方向 B 可生成服务端桩代码。Markdown 版与本页内容一致,专供投喂 AI 编码助手。