PlayRouter API v1
PlayRouter 的技术总览:先看懂整体架构(§1)与部署拓扑(§2),再选接入方式——Seamless 或 Transfer(§3),最后过一遍两产品共用的全局协议——签名、错误、幂等、投递保证(§4)。端点级字段表与 Playground 在各自的文档站。
§1整体架构 · 混乱止于 PlayRouter
不管选哪种接入方式,架构都是同一张图:你只面对 PlayRouter 一个对手方——上游几十家厂商的事件乱序、重复、迟到,全部在 PlayRouter 层被吸收,到达你的永远是签名可验、幂等可靠、有序的调用流。蓝色方向 A(你调用我们)两种产品皆有;琥珀方向 B(我们回调你的钱包)仅 Seamless 形态存在——两种形态的差异见 §3。
运营商 → PlayRouter
运营商主动调用——两种产品的全部主动操作都走这里:启动游戏、拉目录、对账,Seamless 加注单 / 活动 / 彩池(共 15 个端点),Transfer 加划拨/余额/记录(共 11 个端点)。Base URL {eu|sea|latam}.api.playrouter.io(归属区三选一,§2)。
PlayRouter → 运营商
仅 Seamless:运营商实现、等我们调用的钱包回调——查余额、扣款、入账、结算、冲正,共 5 个端点,注册一个 base URL 即可。Transfer 形态零回调,此方向不存在。
§2部署拓扑 · 运营商选服归属,双向同服,新增延迟保守 ≈50ms / 同城 ≈14ms
聚合器插在资金链路中间,最大的原罪是加延迟。我们的解法:三个服全开放、注册即用——运营商用哪个服,归属就在哪个服,挑离自己钱包后端最近的固定使用,双向流量、全部状态都在这一个服。选址原则只有一条:节点跟着运营商走,厂商在哪我们不关心——没有我们,运营商直连厂商也要走同样的距离;节点贴着运营商,厂商的远近就与新增延迟无关,PlayRouter 真正新增的只有几段同区小跳加处理时间。一个运营商一个家,家就开在运营商门口。
| 层 | 策略 |
|---|---|
| 归属 | 全自助,没有"选服"仪式:注册即得全局通用的密钥,eu. / sea. / latam. 三个服对每个账号都开放,用哪个,会话和账就落在哪个。建议固定用离自己钱包后端最近的一个:方向 A 调它,方向 B 回调从它发出——双向一个家,回调出口 IP 只有一段,白名单一次配好。改用别的区不需要新账号或新密钥,直接调用新区即可;数据按区存放、区与区不互通——旧区的会话、交易、划拨与注单记录留在旧区,照常可查。转账钱包里玩家已在某个区上分进游戏的余额,请在同一个区查询和取回,取回后再到新区上分。拿不到响应要重试时,请留在同一个区:同一个 idempotency_key 只在同一区内去重,换到别的区重发会被当成一笔新请求再执行一次。 |
| 数据 | 运营商的全部数据(会话 / 回合 / 幂等表 / 交易流)都在归属区一套 DB——transactions/* 对账查询就在本区,无需跨区汇聚;游戏目录 / 配置 = 全球复制(读多写少)。 |
| 延迟承诺 | 聚合层新增 = 系统处理全链 p50 ≈10ms(拆解:JSON 解析 + HMAC 验签 <1ms · 幂等检查 同区内存缓存 ≈1ms · 账务落库 同 AZ 关系库 ≈3ms · 归一化 + 重签名 <1ms · 响应侧校验 + 送达记录 ≈2ms;p99 ≤25ms)+ 归属服↔运营商钱包一次往返(同区保守 2×20ms)⇒ 每笔结算新增保守 ≈50ms;钱包与服同城同云时(1–3ms×2)新增 ≈14ms。对照 slot 转轴动画 1000–3000ms,占 1.6–4.8%,玩家不可感知。厂商↔归属服段与直连集成相同,不计入新增。 |
按真实比例(慢放)播放一笔下注结算的完整链路。琥珀 = PlayRouter 新增;灰 = 原生网络段(直连集成同样要走);蓝 = 运营商自身处理。
§3两种接入方式 · 怎么选
很多运营商分不清这两种钱包,一句话版本:Seamless = 钱不动,账单飞过来——玩家资金始终在你的系统里,每笔投注/派彩实时回调你的钱包记账;Transfer = 钱搬家,事后看账——先把钱划进厂商侧的游戏钱包,游戏期间没有任何回调,输赢事后拉记录。两个是独立产品:文档自包含,共享的 5 个目录/会话端点字段逐字相同、只是路径前缀不同(迁移时这部分代码只改前缀);密钥账号级通用(2026-07-20 定案)——一把 pk_/sk_ 对两条产品线同时有效,迁移/增开产品线不换钥,可调端点集取决于账号的产品开通状态、与密钥无关。
怎么选 · 一张表看完
| 维度 | Seamless Wallet(无缝钱包) | Transfer Wallet(转账钱包) |
|---|---|---|
| 玩家的钱在哪 | 始终在你的系统——你的钱包是唯一账本 | 划进厂商侧游戏钱包(逐厂商一个),玩完取回 |
| 游戏怎么记账 | 每笔投注/派彩实时回调你的钱包(debit/credit/settle) | 厂商直扣自己侧钱包,游戏期间你是全盲的 |
| 上线必须 | 调 4 个端点 + 实现 5 个回调(9 件事,其中 5 件是"被调用") | 调 7 个端点 + 0 回调(7 件事,全部是"你调用") |
| 你要开发的服务器 | 一个公网可达、验签、幂等的钱包回调服务(7×24 被调用) | 没有——纯调用方,不暴露任何接口 |
| 数据实时性 | 逐笔实时——回调即账本,风控/反水/展示零延迟 | records/list 分钟级拉取——唯一数据出口,事后对账 |
| 资金控制 | 每笔都过你的手,可实时拒绝(declined:限额/自我排除即时生效) | 钱离开系统后局中无法干预;控制点在上分之前 |
| 玩家换厂商 | 无感(钱本来就没动) | 先从 A 家下分、再向 B 家上分(两笔划拨) |
| 活动模块 | campaigns ×3(freespin / freechip / bonus_buy) | 无(转账形态厂商无活动 API) |
| 厂商覆盖 | 全部接入厂商 | 仅支持转账形态的厂商 |
| 适合谁 | 有后端团队、要实时风控与统一钱包体验的运营商 | 要最快上线、后端改造最小、能接受事后对账的运营商 |
| 对接入口 | Seamless 文档站 ↗(全端点字段表 + Playground)· 本页 = 概念与协议 | Transfer 文档站 ↗(全端点字段表 + Playground)· 本页 = 概念与协议 |
"你愿不愿意运营一个 7×24 可被调用的钱包回调服务?"愿意 → Seamless:体验、实时性、资金控制全面更好,这也是行业主流方向。不愿意(或想先最快跑起来)→ Transfer:零回调、七个接口就上线,代价是局中全盲与跨厂商搬钱。两产品共享签名/错误/幂等协议与 5 个目录/会话端点的逐字定义(只是路径前缀不同)——从 Transfer 起步、以后升级 Seamless,这部分代码只改路径前缀。
§4.1传输
| 规则 | 约定 |
|---|---|
| 方法 | 一律 POST + Content-Type: application/json; charset=utf-8。无 GET——GET 要签名就得定义"规范化请求串",那是集成 bug 的头号来源;全 POST 让签名永远等于 t.body,一行代码,零歧义。 |
| 字段命名 | snake_case;路径 /v1/<面>/<域>/<动作> 全小写(面 = seamless 或 transfer,即钱包模式;两面都有的接口写成 /v1/seamless/currencies 与 /v1/transfer/currencies)。 |
| 版本 | 所有端点带 /v1。演进只做加法(新增可选字段/枚举值);破坏性变更才有 /v2。 |
| 前向兼容 | 接收方必须忽略未知字段。 |
§4.2签名 · HMAC-SHA256,双向同一方案
资金业务不适合 Bearer——双向调用都必须验证请求方身份与内容完整性。我们采用开发者最熟悉的 HMAC 格式(Stripe webhook 同款),并加上两块:防重放与密钥轮换。
- 验签失败 → HTTP 401(方向 A);方向 B 运营商返回
200 + {"status":"error","reason":"invalid_signature"}——handler 里一律回 JSON 即可。PlayRouter 按配置故障处理:告警 + 冻结投递,不重试轰炸(误返回 401 等任何 4xx(429 除外——429 按 5xx 进重试时间表)同样入此桶,不出事故)。 - 密钥账号级、两条产品线通用(2026-07-20 定案):一把
pk_/sk_对 Seamless / Transfer 同时有效,增开产品线不换钥。 - 密钥轮换:每环境最多 2 把 active key 并行(配额不分产品线、账号全局至多 4 把),按
X-Playrouter-Key-Id选 secret 验证,摘掉旧钥即完成轮换——无需停机,无需"联系集成经理"。 - 双向共用同一组密钥(配置概念少一半):
| 用途 | Key-Id 前缀 | Secret 前缀 |
|---|---|---|
| 方向 A 签名 + 方向 B 验签,同一把 | pk_live_ / pk_test_ | sk_live_ / sk_test_ |
跨向 / 跨端点反射防御不依赖密钥隔离,由校验层承担:每端点 schema 严格校验,方向 B 回调必含 transaction_id、方向 A 任何端点不接受该字段(反之亦然)——反射请求在字段校验即被 400/422 拒绝,触不到业务逻辑。
方向 B 里角色反转:PlayRouter 签名、运营商验签——这才是运营商真正要写的代码。选一个场景,回调由"PlayRouter"现场真签发出,右侧走完整验签流程(同样全部在你的浏览器本地计算)。
§4.3错误模型 · HTTP 码管重试,body 管业务
调用方的重试逻辑应当只看 HTTP 层就能定。我们的规则一句话:HTTP 状态码只回答"接下来该干什么"。
| HTTP | 含义 | 调用方动作 |
|---|---|---|
| 2xx | 已收到且给出最终业务答复(含业务拒绝) | 按 body 处理,勿重试 |
| 400 | JSON 不可解析 | 修 bug |
| 401 | 签名 / 时间戳 / 密钥问题 | 修配置,告警 |
| 403 | 无权限(产品线未开通 product_not_enabled——账号未开通某一面时,该面前缀下全部接口返回此码 / 功能未开通 / 厂商未启用) | 联系配置 |
| 404 | 路径不存在,或引用的资源不存在 | 修引用 |
| 405/415 | 方法不是 POST / Content-Type 不是 JSON | 修 bug |
| 409 | idempotency_key_reused(同 key 不同 body) | 修 bug,绝不执行 |
| 422 | 字段校验失败 | 修请求 |
| 429 | 限流 | 按 Retry-After 重试 |
| 5xx | 服务故障 / 超时 | 可安全重试(幂等保障下) |
本节讲的是模型,出现的码只是简例——完整错误码目录(方向 A 语义码 Seamless 面 30 个 / Transfer 面 27 个 + 名单逐项 3 个 + 方向 B 5 个 reason,含 message 样例)收在 文档站「附录 · 速查」↗。
{
"error": {
"code": "game_not_available",
"message": "Game 'pg:fortune-tiger' is not enabled for your operator.",
"doc_url": "https://docs.playrouter.io/errors/game_not_available",
"request_id": "req_01H...",
"fields": { "game_code": "not enabled" }
}
}
每个 4xx/5xx 都带上述错误体(code 给程序判断、message 给人读、fields 逐字段定位);唯一例外是基础设施层 5xx(LB 超时等)body 可能非 JSON——解析失败时直接按 HTTP 码行动,5xx 的动作本就是重试,不依赖 body。
方向 A 没有"错误藏在 200 里":200 的 body 永远是成功语义——"成功" = 请求合法且已被受理处理完毕、给出最终业务答复:方向 B 的 declined、Transfer 划拨的 status:"failed"、campaigns/create 的 results 逐项,均属合法业务答复而非错误。API 永不返回 3xx 重定向(会破坏签名与幂等语义)。405/415 属基础设施层、可无错误体(按 HTTP 码行动即可)。
余额不足是一个合法答复,不是故障。运营商返回 200 + declined,PlayRouter 绝不重试——重试逻辑纯看 HTTP 层,一条规则讲完。方向 B reason 共 5 个;credit / rollback 永不 declined 的唯二例外:player_not_found / currency_mismatch(账本无法定位、拒收合理——我方进对账队列 + 双方告警)。
这是刻意的不对称:错误模型的复杂度全在我们这侧(方向 A 30 个语义码),运营商侧只需"能记账就 200"——web 框架的默认行为恰好就是正确行为;而当他们真的宕机时,基建自动吐出的 5xx 在本协议里的含义(故障,重试)也恰好为真。运营商不小心做出来的行为 = 协议要求的行为,这才是回调面能压到 5 个端点、集成周期能压到天级的底层原因。
200 { "status": "declined", "reason": "insufficient_funds", "balance": "3.20" }
运营商要处理的全部错误分支 · 一屏写完
30 个语义码是给出事之后的人省排查时间的,不是给写代码的人加活的——code / message 只进日志,不要求逐个 catch;未识别的 code 一律按其 HTTP 码的动作处理(code 列表随时增补,不视为破坏性变更)。代码层面,两个方向合计要写的分支如下,再无其他:
2xx → 用 body,完事 429 → 按 Retry-After 等待重试 5xx / 超时 → 用同一个 idempotency_key 重试 其余 4xx → 终态:不重试,error 体记日志,等人来修
记账成功 / 重复交易 → 200 + 余额 ← 记账逻辑,本来就要写 业务拒绝(5 种 reason)→ 200 + declined ← 同上 验签失败 → 200 + error ← 一行 if,参考实现自带 JSON 解析失败 → 400 ← 零代码:框架默认行为 自己宕机 → 5xx ← 零代码:框架/基建自动
而且写错也安全:我们按类别解读回调响应——任何 4xx(429 除外——429 按 5xx 进重试时间表)都当配置故障(冻结 + 告警,不轰炸),任何 5xx / 超时都进重试时间表;401 写成 403 不会出事故。
§4.4金额与时间
金额 · string decimal
^\d{1,18}(\.\d{1,12})?$
金额 = JSON string decimal:^\d{1,18}(\.\d{1,12})?$,请求侧永非负(方向由字段语义表达);例外:响应中 balance 允许负(^-?…$,credit/rollback 可致负余额必须可表达);另:两面注单(records)的冲销记录(source=adjustment)金额为负(SignedMoney);禁 JSON number。
// 钱包回调 wallet/debit
{ "amount": "10.00", "source": "bet" }
// → 运营商响应
{ "status": "ok", "balance": "114.50", "operator_transaction_id": "op-991" }
// 每币种精度由 POST /v1/seamless/currencies 公布(转账面 /v1/transfer/currencies)
{ "data": [{ "code": "BRL", "decimals": 2, "type": "fiat" },
{ "code": "USDT", "decimals": 6, "type": "crypto" }] }
- 没有浮点误差。JSON number 在多数语言默认解析成 IEEE 754 double,
0.1 + 0.2 ≠ 0.3;资金接口上每笔差一丝,日终对账就对不上。字符串逐字节精确,接入方直接喂给BigDecimal/decimal.Decimal/decimal.js。 - 签名字节稳定。金额在被 HMAC 签名的 body 里(§4.2)。number 的序列化不唯一——
10.00可能被重排成10或1e1,原文一变验签就挂;字符串怎么转发都不变形。 - 免最小单位换算。不学 Stripe 整数分——crypto 12 位小数 + 各国法币 exponent 不一,会逼每个接入方维护一张换算表;
"10.00"所见即所得,精度按/v1/seamless/currencies(转账面/v1/transfer/currencies)的decimals校验即可。 - 按数值比较,不按字符串比较。我方发出的金额小数位按
decimals写满,零也一样(2 位小数币种写"0.00");请按十进制数值解析与比较,不要按字符串比较。 - 上限即建表参数。正则封顶 18 位整数 + 12 位小数,DB 一列
DECIMAL(30,12)装下全币种,无溢出边界要议。
时间 · RFC 3339 UTC
2026-07-03T12:00:00.000Z
全 API 每一个时间字段固定此格式:恒 UTC、恒 Z 结尾、恒 3 位毫秒;字段名以 _at 结尾(created_at、settled_at)。唯一例外:签名头里的 t 是 Unix 秒,属传输层。
// 钱包回调 · 事件发生时刻
{ "occurred_at": "2026-07-03T12:00:00.000Z" }
// 对账 transactions/list · 时间窗
{ "from": "2026-07-02T00:00:00.000Z", "to": "2026-07-03T00:00:00.000Z" }
// 投递日志 · 每次尝试
{ "attempted_at": "2026-07-03T12:00:05.200Z", "http_status": 200, "outcome": "ok" }
- 无时区歧义。恒 UTC,没有本地时间、没有夏令时;巴西运营商和我们说"7 月 2 日的账",指的是同一段时间窗,对账不会差 3 小时。
- 字典序 = 时间序。定宽、零填充、同时区,字符串直接比大小:对账窗口过滤就是字符串比较,日志里
grep "2026-07-03T12:"一把捞出整点段。 - 标准库原生解析。JS
new Date()、Gotime.RFC3339、Pythonfromisoformat零依赖直读;也躲开 Unix 时间戳"10 位秒还是 13 位毫秒"的经典对接事故。 - 格式唯一,签名稳定。毫秒固定 3 位、无可选部分,同一时刻只有一种写法——进被 HMAC 签名的 body 不会因序列化差异变形(与金额字符串同理)。
配套格式基元 · currency / locale 代码
^[A-Z0-9]{3,4}$
// "BRL", "USD" ← 法币 = ISO 4217 三字母
// "USDT", "BTC", "DOG" ← 加密货币 = 3–4 位字母数字
每币种的 type / decimals 由 /v1/seamless/currencies(转账面 /v1/transfer/currencies)公布(§4.4 的配套)。不做计价加密货币(mBTC / µBTC 之类的换算单位)——string decimal 12 位小数直接表达原生单位,计价单位无存在必要。
^[a-z]{2}$
// "en", "pt", "zh" ← ISO 639-1 小写两位
仅 sessions/create 使用,可选、默认 en。不支持的值不报错,静默降级 en。
country 码(ISO 3166-1 alpha-2)v1 不引入:不托管玩家资料,游戏地域可用性由我们按厂商牌照判定(不可用即 game_not_available)。将来若需要,沿用大写两位 ^[A-Z]{2}$。
§4.5幂等 · 一套规则,两种钥匙
底层规律只有一条:调用方生成去重键,放进被签名的 body。区别只在键的"身份"——方向 A 的动作本身没有交易编号,要外挂一把一次性钥匙;方向 B 每笔天生就是账目,主键即幂等键。
方向 A idempotency_key | 方向 B transaction_id | |
|---|---|---|
| 谁生成 | 运营商(调用方) | PlayRouter(调用方) |
| 性质 | 一次性钥匙:防这次网络重试,7 天后遗忘,永不再被引用 | 业务主键:rollback 引用它、对账单里查它、双方账本永久存储 |
| 格式 | 1–128 字符 [A-Za-z0-9_.-];UUIDv4 或确定性派生(推荐 fs-issue-{内部ID}) | txn_ 前缀,全局唯一 |
| 作用域 | 运营商账号级、只在同一区内去重(跨端点也算复用;换区重发会被当成新请求);内部主键 (operator_id, key),跨运营商天然隔离 | 同一区内 |
| 重复时返回 | 原响应 + 头 Idempotency-Replayed: true;同 key 不同 body → 409 | 200 + 当前余额 + "duplicate": true(不要求存原响应) |
正确姿势是 write-ahead:业务意图产生的那一刻生成 key 并随本地记录落库,之后无论重试多少次、进程崩溃后恢复,都用同一个 key 重发。每次重试换新 key = 等于没有幂等 = 超时重发就是双倍发钱。
转账产品的 transfers/create(上分/下分)必填同款 idempotency_key,本节规则逐字生效:同 key 重发恒安全、重放原响应 + Idempotency-Replayed: true、同 key 不同 body → 409、保留 7 天;推荐派生 tf-{内部订单ID}。划拨正是"超时重发 = 双倍打款"风险最高的场景——上表方向 A 列整列适用;方向 B 列不适用(转账产品零回调,输赢记录的去重键是 rec_ 记录 ID)。无论接哪家厂商:一把键,一条规则。
§4.6投递保证 · 对运营商的三条书面承诺
厂商侧的事件可能乱序、重复、迟到。PlayRouter 的答案:混乱在我们这层终止。关键洞察——乱序对钱包数学无害(win 先到只是余额暂时偏高),有害的是引用悬空。
绝不对你未成功收到的交易发 rollback。厂商对未送达交易的回滚,PlayRouter 自行吸收。
rollback 之后绝不再投递原始交易。墓碑状态由 PlayRouter 维护。
引用完整性 + 回合内 FIFO。回调引用的 transaction_id 保证已送达过你;同 round 串行投递 + 单调 sequence。
运营商无需实现乱序状态机、未知交易兜底、墓碑防复活——"来一条记一条账"即可。
三承诺按三层交付,不当物理定律宣传:
机制——全局交易账本之上两条规则:墓碑表(rollback 引用未送达交易 → 吸收登记,原交易后到即拦截);回合内 FIFO(承诺 ③)天然保证原交易终态确认先于其 rollback 下传,覆盖"debit 超时结果未知、厂商随即冲正"的竞态。
验证——认证套件含乱序时序用例;每接新厂商先在其沙箱跑通全部乱序场景再放量。
兜底——机制失手时:每日对账捕获差异、adjustment 事后修正。失手代价是一次对账修正,不是资损事故。
运营商(有序侧)
PlayRouter(吸收层)
厂商(混乱侧)
§4.7重试与限流 · 行为可预期
我们调运营商回调失败时的重试时间表是公开承诺,不是内部实现细节——运营商能精确预期 PlayRouter 的行为。
/v1/seamless/transactions/get 的 delivery_status / delivery_attempts[] 中完整可见。同 round 的后续事件在 FIFO 队列中等待,不跳队。方向 A 限流
- 标准响应头
RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset;超限429+Retry-After。 - 配额按 API Key 计(同商户轮换期的两把 key 各有独立额度),并以下限保证形式公布——每个 API Key 每分钟至少可完成下表次数,实际放行量可能更高;下限值只会放宽、不会收紧。
| 端点 | 每分钟下限(每 API Key) |
|---|---|
POST /v1/seamless/transactions/list | 60 |
POST /v1/transfer/records/list + POST /v1/seamless/records/list(两面合计) | 150 |
| 其余全部端点 | 1000 |
RateLimit-* 头的值按端点给出(重查询端点与普通端点配额不同),请勿跨端点缓存。建议(非强制)以响应中的 RateLimit-Remaining 为准做自适应节流,而非硬编码固定速率。首次接入需大批量回补历史账单时,可联系我们临时提额。
§4.8DX / AI 原生资产 · 随 v1 交付
这些是产品,不是文档附件。
公开可读。OpenAPI 3.1 + 全量 JSON Schema 可下载。
AI agent 直接查规范、调沙箱——接入方的 AI 编码助手是一等公民。
可直接部署的 wallet handler(含幂等表结构)+ TypeScript SDK 先行。
沙箱内置假游戏,按钮触发连消多笔、rollback、断线、迟到结算。
上线 = 认证测试套件全绿,不是人肉验收排期。
CLI 把回调隧道到 localhost(Stripe CLI 同款体验)。
方向 A:playrouter-seamless-wallet-api-v1.openapi.yaml(15 端点全 schema,可导入 Postman / 生成客户端)· 方向 B:playrouter-seamless-wallet-callbacks-v1.openapi.yaml(5 回调,可生成服务端桩代码)
端点级字段表 + 全端点 Playground:Seamless 文档站 ↗(字段级定义以 OpenAPI + 文档站为准)
§4.9游戏素材直链 · assets.playrouter.io
上线一个大堂页,除了接 API,还要给几千款游戏配图标、logo、缩略图——行业常态是找各厂商要素材 zip 包,美术逐张裁切后自建图床,每上新一批游戏再来一轮。我们把这件事做成 imgix 式的图片变换服务:全部游戏素材由我们托管,URL 上改参数即得所需规格,直链嵌进运营商站点即可,无需下载、整理、自建任何图片基础设施。两条产品线共用本服务。
素材直链可能作为独立的服务接口交付,也可能随游戏目录下发——运营商具体从哪个接口拿到素材地址,实施时以接口文档的实际呈现为准,本节不预设。下方 URL 与参数均为演示示例(DEMO),仅用于说明服务形态。
服务形态(DEMO 示意)
每款游戏的各类素材——方图标、横卡、竖版海报、游戏 logo、背景图、厂商 logo——都有一条稳定直链,形态大致如下:
https://assets.playrouter.io/g/{provider}/{game}/tile ← 方图标
https://assets.playrouter.io/g/{provider}/{game}/thumb ← 横卡
https://assets.playrouter.io/g/{provider}/{game}/logo ← 游戏 logo
https://assets.playrouter.io/g/{provider}/{game}/bg ← 背景图
https://assets.playrouter.io/p/{provider}/logo ← 厂商 logo
大堂页里直接引用,参数在 URL 上现改现生效:
<img src="https://assets.playrouter.io/g/pragmatic/gates-of-olympus/tile?w=360&h=240&fit=cover&fmt=auto&dpr=2">
变换参数(DEMO 口径)
| 参数 | 取值 | 说明 |
|---|---|---|
w / h | 像素 | 目标宽高;只给一边则等比缩放 |
fit | cover / contain / pad | 裁切模式;pad 配合 bg 补底 |
fmt | auto / webp / avif / png / jpg | 默认 auto:按浏览器 Accept 头自动协商最优格式 |
dpr | 1–3 | 高倍屏:w=360&dpr=2 输出 720px 物理像素,CSS 尺寸不变 |
q | 1–100 | 压缩质量,默认 80 |
r | 像素 / max | 圆角;max 出正圆(provider logo 常用) |
bg | hex 色值 | pad 补边色 / 透明 PNG 转 JPG 时的底色 |
lang | BCP-47 | 本地化 logo(部分厂商分语种);无该语种回退 en,不报错 |
四条行为约定
- 公开只读、免签名——素材 URL 不含密钥,前端直接引用,不需要任何鉴权改造;写操作不存在,无攻击面扩大。
- 直链授权、流量全包——明确允许热链到运营商生产站点;全球边缘缓存就近出图,流量包含在聚合服务内,不另出账单。
- 地址永不脏——厂商换图时素材地址随内容版本更新下发,旧地址继续可用;运营商侧无需清缓存、无需回源校验。
- 缺图兜底、绝不开天窗——厂商未提供的规格由原图实时派生;连原图都缺时返回带游戏名的统一占位图。大堂页上永远不会出现破图(与 §4.3 的理念一致:可预期的降级优于报错)。
接完钱包接口只解决了「游戏能玩」;大堂页好不好看,通常还压着几周的素材整理与美术工作量。素材直链让大堂页与 API 同一天出活,之后每次上新游戏,素材自动就位——这是「别家还在排期,您已经上线」在素材侧的拼图。
§5环境与配置
| 沙箱 | 生产 | |
|---|---|---|
| API | api.sandbox.playrouter.io(单区——验证协议正确性与认证套件,无归属概念、不承诺延迟 SLA) | eu.api.playrouter.io / sea.api.playrouter.io / latam.api.playrouter.io——三服全开放,固定用离钱包后端最近的一个;用哪个,归属就在哪个(§2) |
| 密钥 | *_test_* | *_live_* |
| 回调 base URL | 独立注册 | 独立注册 |
- 沙箱数据与生产完全隔离;沙箱含边界模拟器与认证测试套件。
- 密钥前缀可辨识环境——日志里泄露了也立刻知道影响面。
- 上线流程:沙箱跑认证套件全绿 → 后台一键申请生产密钥。