← 返回金牛游戏中心 排行榜

游戏排行榜 API · 企业接入文档

给 站内游戏模块第三方网页内嵌企业服务端 使用的排行榜接入说明。接口域名:https://bbs.jnqj.net

【接入资格 · 必须严格执行】

一、接入流程(企业)

  1. 企业身份核验

    使用金牛ID 企业账号登录本站。系统以金牛ID OAuth 返回的企业信息为准,并可叠加管理白名单。个人账号、邮箱注册账号无法通过。

  2. 申请发放令牌

    管理员审核通过后发放 AppID + AppSecret(Secret 仅显示一次)。请保存在企业服务端,严禁写入前端 JS / 公开仓库。

  3. 按签名规范调用

    每次请求携带时间戳 + Nonce + HMAC-SHA256 签名。服务端校验通过且会话用户为企业身份后才允许上报/上榜。

二、接口一览

接口方法身份说明
/api/game/leaderboardGET公开读?game=all 总分榜;?game=raiden 分游戏榜。榜单只统计企业用户
/api/game/sessionGET否登录态 + CSRF + is_enterprise / account_label
/api/game/scorePOST企业提交成绩(进分榜,总分自动汇总)
/api/game/coin-opPOST企业开局投币,返回 fee/balance/round_ref
/api/game/settlePOST企业对局结算(羊/斗地主/极速狂飙)
/api/game/pointsGET登录积分余额

三、安全模型(多重校验)

为防止黑客与同行恶意刷榜/盗用,写接口按顺序执行以下门禁,任一失败立即拒绝并审计:

层机制说明
1AppID 存在且启用未知 / 禁用 AppID 一律 403
2时间戳窗口X-Timestamp 与服务器时间差 ≤ 300 秒
3Nonce 防重放同一 Nonce 只能使用一次(600 秒内)
4HMAC-SHA256 签名用 AppSecret 对请求行 + Body 哈希签名
5IP 白名单(可选)企业可限制来源 IP / CIDR
6会话 / CSRF站内内嵌走 Cookie + CSRF;服务端走签名
7企业身份双因子金牛ID OAuth 企业字段 ∪ 管理白名单;且必须有金牛ID openid
8限流 + 审计默认写 30 次/分、App 60 次/分;失败全部落 game_api_audit

签名算法

signString =
  METHOD + "\n" +
  PATH   + "\n" +
  timestamp + "\n" +
  nonce + "\n" +
  sha256_hex(body)

X-Signature = hex( HMAC-SHA256( app_secret , signString ) )

请求头:

X-App-Id:     JNGA_XXXXXXXXXXXXXXXX
X-Timestamp:  1761300000
X-Nonce:      a1b2c3d4e5f6g7h8
X-Signature:  6f1c...(小写 hex)

四、Python 签名示例(企业服务端)

import hashlib, hmac, time, uuid, requests

APP_ID = "JNGA_..."
APP_SECRET = "..."  # 仅服务端保存
BASE = "https://bbs.jnqj.net"

def signed_headers(method, path, body: bytes):
    ts = str(int(time.time()))
    nonce = uuid.uuid4().hex
    body_hash = hashlib.sha256(body).hexdigest()
    sign_string = "\n".join([method.upper(), path, ts, nonce, body_hash])
    sig = hmac.new(APP_SECRET.encode(), sign_string.encode(), hashlib.sha256).hexdigest()
    return {
        "X-App-Id": APP_ID,
        "X-Timestamp": ts,
        "X-Nonce": nonce,
        "X-Signature": sig,
    }

# 上报成绩(需企业用户 Cookie 会话,或站内内嵌走 CSRF)
path = "/api/game/score"
body = b"game=raiden&score=12345&detail=wave%3D12"
h = signed_headers("POST", path, body)
h["Content-Type"] = "application/x-www-form-urlencoded"
# 浏览器内嵌时另附 Cookie + X-CSRF-TOKEN
r = requests.post(BASE + path, data=body, headers=h, cookies={"BBS_JNQJ_SESSION": "..."})

五、站内内嵌(Cookie + CSRF)

第三方网页内嵌游戏时,玩家必须以金牛ID 企业账号登录本站会话。脚本:

<script src="https://bbs.jnqj.net/game/js/auth.js"></script>
<button id="go">打一局雷电风暴</button>
<ol id="rank"></ol>
<script>
document.getElementById("go").onclick = async () => {
  const s = await JNGameAuth.session();
  if (!s || !s.logged_in) {
    location.href = JNGameAuth.loginUrl("/game/raiden/");
    return;
  }
  if (s.is_enterprise === false) {
    alert("排行榜仅支持金牛ID企业用户接入,当前账号:" + (s.account_label || "不允许"));
    return;
  }
  await JNGameAuth.coinOp("raiden");
  // …你的游戏逻辑…
  await JNGameAuth.submitScore({ game: "raiden", score: 1000, detail: "demo" });
  const j = await JNGameAuth.leaderboard("raiden");
  document.getElementById("rank").innerHTML = j.data.list.map(x =>
    "<li>#" + x.rank + " " + x.nickname + " " + x.score + "</li>").join("");
};
</script>
session() 额外返回字段: is_enterprise(bool)、 account_type(email / jnqj_personal / jnqj_enterprise)、 account_label(人类可读)、 access_policy(政策说明)。

六、投币费用(与前台一致)

游戏每局
极速狂飙 / AI / AI V2250(胜退 250 + 净赚 250)
雷电风暴100
其余10

七、游戏合法 slug

game说明
all全游戏总分(每款最佳分相加)
raiden雷电风暴
sheep羊了个羊
doudizhu斗地主
makaimura / mojiecun魔界村 / 摩羯村
speed-rush / speed-rush-ai / speed-rush-ai-v2极速狂飙三端
neon-racer霓虹飞车

八、错误码

HTTP含义
401未登录 / 缺少签名头 / 签名失败 / 时间戳过期
403非金牛ID企业用户 / AppID 无效或禁用 / IP 不在白名单
409Nonce 重放
429限流
422参数不合法
再次强调: 本接口目前暂时仅支持金牛ID 企业用户接入; 不支持自定义注册账号接入; 不支持金牛ID 个人账号接入。 任何伪造 user_id、盗用 Cookie、重放签名、刷分行为都会被拒绝并审计,情节严重将封禁 AppID。

九、企业管理员操作(服务端 CLI)

# 列出应用
php scripts/game_api_admin.php apps

# 发放令牌(owner 必须是金牛ID企业用户)
php scripts/game_api_admin.php create-app \
  --name="某某游戏" --owner=123 --ent="某某科技有限公司" \
  --ips=203.0.113.10 --rpm=60

# 白名单强制开通企业身份(双因子之一)
php scripts/game_api_admin.php approve-user --uid=123

# 审计
php scripts/game_api_admin.php audit --limit=50