Files
lotteryAdmin/src/modules/docs/integration/api-reference-screen.tsx
kang 641c87ff50 feat(docs, agents, risk): enhance documentation, API queries, and UI components
Updated the public documentation site with improved layout and accessibility, including new sections for client integration and admin guides. Enhanced API queries by adding 'active_only' and 'group_by' parameters for better data filtering in risk management. Refined UI components for agent management, ensuring consistent styling and improved user experience across the application. Added localization support for new documentation content in English and Nepali.
2026-06-15 17:21:50 +08:00

374 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import {
DocCode,
DocEndpoint,
DocPage,
DocPageHeader,
DocParagraph,
DocSection,
DocTable,
DocList,
DocOrderedList,
DocNote,
} from "@/components/docs/doc-ui";
const CURL_PLAYER_ME = `curl -sS "https://{lottery_api}/api/v1/player/me" \\
-H "Authorization: Bearer {JWT}" \\
-H "Accept: application/json"`;
const CURL_WALLET_BALANCE = `curl -sS "https://{wallet_host}/wallet/balance?site_code=demo&site_player_id=100001&currency_code=NPR" \\
-H "Authorization: Bearer {wallet_api_key}"`;
const CURL_WALLET_DEBIT = `curl -sS -X POST "https://{wallet_host}/wallet/debit-for-lottery" \\
-H "Authorization: Bearer {wallet_api_key}" \\
-H "Content-Type: application/json" \\
-d '{
"site_code": "demo",
"site_player_id": "100001",
"player_id": 42,
"currency_code": "NPR",
"amount_minor": 100,
"idempotent_key": "accept-debit-001"
}'`;
const IFRAME_EXAMPLE = `<!DOCTYPE html>
<html>
<head><title>主站集成</title></head>
<body>
<iframe id="lotteryFrame" src="https://lottery.example.com/"></iframe>
<script>
const LOTTERY_ORIGIN = "https://lottery.example.com";
window.addEventListener("message", (event) => {
if (event.origin !== LOTTERY_ORIGIN) return;
const { data } = event;
switch (data.type) {
case "LOTTERY_READY":
fetchNewToken().then(token => {
sendToIframe("MAIN_INIT_TOKEN", { token });
});
break;
case "LOTTERY_TOKEN_NEEDED":
case "LOTTERY_TOKEN_REFRESH_REQUEST":
fetchNewToken().then(token => {
sendToIframe("MAIN_REFRESH_TOKEN", { token });
});
break;
}
});
function sendToIframe(type, payload) {
const iframe = document.getElementById("lotteryFrame");
iframe.contentWindow.postMessage(
{ type, payload, timestamp: Date.now(), source: "main-site" },
LOTTERY_ORIGIN
);
}
async function fetchNewToken() {
const res = await fetch("/api/auth/lottery-token", {
method: "POST",
credentials: "include",
});
const data = await res.json();
return data.token;
}
</script>
</body>
</html>`;
const JWT_SIGN_TS = `const header = base64url(JSON.stringify({ alg: "HS256", typ: "JWT" }));
const payload = base64url(JSON.stringify({
site_code: "demo",
site_player_id: "100001",
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 300,
}));
const sig = hmacSha256Base64url(\`\${header}.\${payload}\`, SSO_JWT_SECRET);
const token = \`\${header}.\${payload}.\${sig}\`;`;
export function ApiReferenceDocScreen(): React.ReactElement {
return (
<DocPage>
<DocPageHeader
title="API 对接参考"
description="面向主站开发/集成工程师的技术文档SSO、iframe、钱包网关、错误码与上线清单。"
/>
<DocSection title="1. 接入总览">
<DocTable
compact
headers={["组件", "职责", "实现方"]}
rows={[
["主站", "签发 JWT实现钱包网关余额查询 / 扣款 / 加款)", "客户"],
["彩票 API", "验签、玩法、划转、下注、结算、开奖", "我方"],
["彩票前端", "H5 / iframe 承载,玩家交互界面", "我方"],
]}
/>
<DocNote>
使minor 2000 = 20.00 UTF-8 JSON
</DocNote>
<DocOrderedList
items={[
"主站登录 → 服务端签发 JWT",
"进入彩票URL 跳转或 iframe 嵌入)",
"转入:主站扣款 + 彩票加款",
"下注 / 派奖(彩票内余额)",
"转出:彩票扣款 + 主站加款",
]}
/>
</DocSection>
<DocSection title="2. 快速开始">
<DocParagraph></DocParagraph>
<DocCode language="bash">{CURL_PLAYER_ME}</DocCode>
<DocCode language="bash">{CURL_WALLET_BALANCE}</DocCode>
<DocCode language="bash">{CURL_WALLET_DEBIT}</DocCode>
</DocSection>
<DocSection title="3. 接入配置">
<DocOrderedList
items={[
"超管登录后台 → 平台管理 → 接入配置 → 新建站点",
"填写站点编码site_code、名称、默认币种、wallet_api_url、lottery_h5_base_url、iframe_allowed_origins",
"创建成功后立即保存一次性展示的 sso_jwt_secret 和 wallet_api_key",
"将密钥写入主站 .env执行「连通性测试」",
]}
/>
<DocTable
compact
headers={["项", "后台字段", "主站 .env", "说明"]}
rows={[
["站点编码", "code", "MAIN_SITE_CODE", "JWT 与玩家建档标识;双方须一致"],
["SSO 密钥", "sso_jwt_secret", "MAIN_SITE_SSO_JWT_SECRET", "主站签发;彩票验签"],
["钱包鉴权", "wallet_api_key", "MAIN_SITE_WALLET_API_KEY", "彩票回调主站时 Bearer 携带;主站校验"],
["钱包根地址", "wallet_api_url", "—", "客户 HTTPS 根地址;彩票拼接 /wallet/* 路径"],
["彩票入口", "lottery_h5_base_url", "NEXT_PUBLIC_LOTTERY_IFRAME_URL", "跳转或 iframe 目标"],
["iframe 白名单", "iframe_allowed_origins", "NEXT_PUBLIC_LOTTERY_ORIGIN", "主站 origin彩票允许嵌入"],
]}
/>
<DocNote>
site_code wallet_api_url HTTPS localhost / IP
</DocNote>
</DocSection>
<DocSection title="4. 单点登录SSO">
<DocParagraph> HS256 JWT</DocParagraph>
<DocTable
compact
headers={["字段", "类型", "必填", "说明"]}
rows={[
["site_code", "string", "是", "接入站点编码"],
["site_player_id", "string", "是", "主站用户 ID稳定唯一"],
["iat", "number", "是", "签发时间Unix 秒)"],
["exp", "number", "是", "过期时间Unix 秒);建议 ≤ 300 秒"],
]}
/>
<DocCode language="typescript">{JWT_SIGN_TS}</DocCode>
<DocSection title="入场方式">
<DocParagraph> A URL </DocParagraph>
<DocCode>{`https://{lottery_h5_base_url}/?token={JWT}`}</DocCode>
<DocParagraph> B iframe </DocParagraph>
<DocCode>{`<iframe id="lotteryFrame" src="https://lottery.example.com/"></iframe>`}</DocCode>
<DocNote>
JWT GET /api/v1/player/me username / nickname
</DocNote>
</DocSection>
<DocSection title="入场接口">
<DocEndpoint method="GET" path="/api/v1/player/me" />
<DocCode language="http">{`GET /api/v1/player/me
Authorization: Bearer {JWT}
Accept-Language: zh`}</DocCode>
<DocNote>
JWT API Authorization: Bearer
</DocNote>
</DocSection>
<DocSection title="SSO 错误码">
<DocTable
compact
headers={["错误码", "说明"]}
rows={[
["8001", "缺少 Authorization 头"],
["8002", "JWT 无效或已过期"],
["8003", "玩家未建档"],
["8004", "SSO 密钥未配置"],
["8005", "账号已冻结(站点不存在/停用或玩家冻结)"],
]}
/>
</DocSection>
</DocSection>
<DocSection title="5. iframe 协议">
<DocOrderedList
items={[
"主站页面嵌入 <iframe src=\"{lottery_h5_base_url}\">",
"彩票 H5 加载白名单后发送 LOTTERY_READY",
"主站监听 message校验 origin 后发送 MAIN_INIT_TOKEN",
"彩票 H5 保存 token调用 /api/v1/player/me 入场",
"Token 将过期时:彩票发 LOTTERY_TOKEN_NEEDED → 主站续签后发 MAIN_REFRESH_TOKEN",
]}
/>
<DocNote>
postMessage origin https://www.partner.com禁止使用 *。
MAIN_INIT_TOKEN LOTTERY_READY token
</DocNote>
<DocSection title="彩票 → 主站">
<DocTable
compact
headers={["方向", "消息类型", "说明"]}
rows={[
["→ 主站", "LOTTERY_READY", "子页就绪,请求下发 token"],
["→ 主站", "LOTTERY_TOKEN_NEEDED", "token 失效,请求续签"],
["→ 主站", "LOTTERY_TOKEN_REFRESH_REQUEST", "主动请求刷新 token"],
["→ 主站", "LOTTERY_HEARTBEAT", "心跳(可忽略)"],
["→ 主站", "LOTTERY_TOKEN_REFRESHED", "续签成功通知"],
]}
/>
</DocSection>
<DocSection title="主站 → 彩票">
<DocTable
compact
headers={["方向", "消息类型", "载荷", "说明"]}
rows={[
["→ 彩票", "MAIN_INIT_TOKEN", "{ token }", "首次下发 JWT"],
["→ 彩票", "MAIN_REFRESH_TOKEN", "{ token }", "续签 JWT"],
["→ 彩票", "MAIN_REQUEST_STATUS", "—", "请求子页状态"],
["→ 彩票", "MAIN_NAVIGATE", "{ path }", "导航到指定路径"],
]}
/>
</DocSection>
<DocSection title="完整示例">
<DocCode>{IFRAME_EXAMPLE}</DocCode>
</DocSection>
</DocSection>
<DocSection title="6. 钱包网关">
<DocParagraph>
Authorization: Bearer {"{wallet_api_key}"}
</DocParagraph>
<DocSection title="查询余额">
<DocEndpoint method="GET" path="/wallet/balance" />
<DocTable
compact
headers={["参数", "类型", "说明"]}
rows={[
["site_code", "string", "站点编码"],
["site_player_id", "string", "主站用户 ID"],
["currency_code", "string", "币种代码"],
]}
/>
<DocCode>{`{
"success": true,
"data": { "main_balance": 500000, "currency_code": "NPR" }
}`}</DocCode>
</DocSection>
<DocSection title="扣款(转入时调用)">
<DocEndpoint method="POST" path="/wallet/debit-for-lottery" />
<DocTable
compact
headers={["字段", "类型", "说明"]}
rows={[
["site_code", "string", "站点编码"],
["site_player_id", "string", "主站用户 ID"],
["player_id", "number", "彩票玩家 ID参考"],
["currency_code", "string", "币种"],
["amount_minor", "integer", "minor 正整数"],
["idempotent_key", "string", "幂等键"],
]}
/>
<DocCode>{`{
"success": true,
"external_ref_no": "MW-001",
"data": { "main_balance": 498000, "currency_code": "NPR" }
}`}</DocCode>
</DocSection>
<DocSection title="加款(转出时调用)">
<DocEndpoint method="POST" path="/wallet/credit-from-lottery" />
<DocParagraph></DocParagraph>
</DocSection>
<DocSection title="HTTP 契约">
<DocTable
compact
headers={["场景", "HTTP 状态码", "响应体"]}
rows={[
["扣款/加款成功", "200", "success: true含 external_ref_no 与 data.main_balance"],
["余额查询成功", "200", "success: truedata.main_balance + currency_code"],
["参数非法", "422", "success: falsemessage: invalid request"],
["鉴权失败", "401", "success: falsemessage: unauthorized"],
["业务拒绝", "409", "success: falsemessage 说明原因(如余额不足)"],
["幂等重放", "200", "与首次成功/拒绝响应完全一致"],
]}
/>
</DocSection>
<DocNote>
idempotent_key + JSONHTTP 200/ success: false
</DocNote>
</DocSection>
<DocSection title="7. 错误码汇总">
<DocSection title="SSO 鉴权">
<DocTable
compact
headers={["错误码", "说明"]}
rows={[
["8001", "缺少 Authorization 头"],
["8002", "JWT 无效或已过期"],
["8003", "玩家未建档"],
["8004", "SSO 密钥未配置"],
["8005", "账号已冻结"],
]}
/>
</DocSection>
<DocSection title="彩票钱包 / 划转">
<DocTable
compact
headers={["错误码", "说明"]}
rows={[
["1001", "彩票余额不足(转出时)"],
["1009", "主站钱包处理失败"],
["1010", "幂等键冲突(同键不同金额)"],
["2003", "请先转入后再下注"],
]}
/>
</DocSection>
<DocSection title="客户钱包网关 HTTP">
<DocTable
compact
headers={["HTTP", "message", "原因"]}
rows={[
["401", "unauthorized", "API Key 错误"],
["422", "invalid request", "字段或金额非法"],
["409", "—", "业务拒绝(如余额不足)"],
]}
/>
</DocSection>
</DocSection>
<DocSection title="8. 上线清单">
<DocList
items={[
"测试与生产site_code、密钥、域名完全隔离",
"JWT 仅服务端签发,有效期 ≤ 5 分钟",
"钱包接口走 HTTPS超时建议 ≤ 10 秒",
"idempotent_key 幂等处理已正确实现",
"iframe 模式:已配置 iframe_allowed_origins",
"全链路联调通过:转入 → 下注 → 派奖 → 转出",
]}
/>
</DocSection>
</DocPage>
);
}