GoodRoad 后端接口文档
这份文档详细说明 GoodRoad 插件与后端交互的所有 API。
接口概览
GoodRoad 目前只保留两个主要阶段的接口调用:
| 阶段 | 接口 | 方法 | 说明 |
|---|---|---|---|
| 1. 获取快照 | GET /plugin/v1/tables/snapshot |
GET | 公开接口,获取桌台列表及路盘数据 |
| 2. 进入游戏 | POST /plugin/v1/game/enter |
POST | 使用 我方免转文档的Login接口返回 的 token 进入指定桌台 |
接口调用流程是 token-first:宿主先拿到 Login接口 颁发的 token,再把 token 传给插件;
snapshot 可公开访问,enter 需要
Authorization: Bearer <token>。
认证与签名
签名校验(可选)
如果后端启用了 GOODROAD_CHANNEL_SECRET 环境变量,所有请求都需要包含签名头。
签名算法
signText = "{channelId}|{timestamp}|{nonce}"
signature = HMAC-SHA256(signText, secret).hex()
必需头部
x-channel-id: 渠道号x-timestamp: 当前 Unix 时间戳(秒),允许偏差 ±300 秒x-nonce: 随机字符串,每个时间窗口内一次性使用x-signature: 上述签名结果
域名白名单(可选)
如果配置了 GOODROAD_CHANNEL_DOMAIN_MAP,请求时需要在 domain 字段里传当前页面域名,后端会校验是否在该 channel 的白名单内。
3. token 说明
插件初始化时可以接收宿主传入的会员 token。当前版本不再使用 init-ticket / session-exchange,第三方只需要在宿主侧拿到登录 token 并传给插件即可。
推荐写法是把 token 直接传给
GoodRoadWidget.mount({ token }),或者在零代码模式下使用 data-goodroad-token。
GET tables/snapshot
/plugin/v1/tables/snapshot
获取可用桌台列表及其路盘数据。这个接口是公开的,不需要登录 token。插件会定期调用此接口以刷新桌台列表。
请求字段
| 字段 | 来源 | 类型 | 说明 |
|---|---|---|---|
| limit | Query | number | 最多返回多少个桌台(默认 6,范围 1-50) |
| include_good_road | Query | bool | 是否包含好路数据(默认 1) |
| include_history | Query | bool | 是否包含游戏历史(默认 1) |
成功响应
result = 0
{
"result": 0,
"desc": "ok",
"data": {
"generated_at": 1718358000,
"user_type": "guest",
"channel_id": "demo-channel",
"table_count": 3,
"table_list": [
{
"room_id": 1001,
"game_code": "baccarat_001",
"game_name": "百家乐 1 号桌",
"game_type": 4,
"online": 1,
"shoe_id": 42,
"round_count": 28,
"player_count": 10,
"banker_count": 16,
"tie_count": 2,
"good_road": {
"list": ["b", "p", "b", "b", "t", "p"],
"is_good_road": 1,
"rule": "BACCARAT_LONG_BANKER_4",
"rule_name": "长庄 >=4",
"duration_rounds": 4
},
"game_history": [ ... ],
"analy_data": "{...}"
}
]
}
}
重要:仅返回有路盘数据的桌台(即路盘列表不为空)。无任何落点的空桌将被过滤。
POST game/enter
/plugin/v1/game/enter
请求进入指定桌台。仅会员可进入,调用时必须携带 Authorization: Bearer <token>。
请求字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
| room_id | number | 是 | 目标桌台 ID |
成功响应
result = 0
{
"result": 0,
"desc": "ok",
"data": {
"room_id": 1001,
"game_code": "baccarat_001",
"open_url": "https://game.example.com/baccarat?room=1001&token=..."
}
}
常见错误
TOKEN_INVALID
登录 token 无效或已过期
NEED_LOGIN
当前没有可用的会员 token
GUEST_FORBIDDEN_ENTER
游客无法进入游戏
ROOM_NOT_AVAILABLE
目标桌台不可用或不存在
完整示例
JavaScript/TypeScript 联调示例
async function setupGoodRoadWidget() {
const apiBase = 'http://127.0.0.1:13100';
const token = window.USER_TOKEN || '';
// 1. 获取桌台快照(公开接口)
const snapshotResp = await fetch(`${apiBase}/plugin/v1/tables/snapshot?limit=8`).then(r => r.json());
console.log('Tables:', snapshotResp.data.table_list);
// 2. 进入游戏(需要会员 token)
if (token) {
const enterResp = await fetch(`${apiBase}/plugin/v1/game/enter`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({ room_id: 1001 })
}).then(r => r.json());
console.log('Enter response:', enterResp);
}
}
setupGoodRoadWidget().catch(console.error);