{
  "shell": {
    "title": "彩票接入文档",
    "admin": "管理后台"
  },
  "nav": {
    "overview": "概览",
    "api": "接口",
    "ship": "发布",
    "home": "总览",
    "quickstart": "快速开始",
    "fundamentals": "资金模型",
    "setup": "接入配置",
    "sso": "单点登录",
    "iframe": "iframe 协议",
    "wallet": "钱包网关",
    "transfer": "划转（参考）",
    "errors": "错误码",
    "golive": "上线清单"
  },
  "headers": {
    "component": ["组件", "职责", "实现方"],
    "convention": ["项", "规则"],
    "claim": ["字段", "类型", "必填", "说明"],
    "param": ["参数", "用途"],
    "methodPath": ["方法", "路径", ""],
    "query": ["参数", "类型", ""],
    "field": ["字段", "类型", "说明"],
    "code": ["错误码", "说明"],
    "http": ["状态码", "message", "原因"],
    "message": ["方向", "消息类型", "载荷"],
    "balance": ["字段", "账户", "说明"],
    "call": ["方向", "接口", "鉴权"],
    "sequence": ["步骤", "发起方", "说明"],
    "envMap": ["项", "后台接入站点", "主站 .env", "说明"],
    "account": ["账号", "密码", "site_player_id"],
    "contract": ["场景", "HTTP", "响应体"],
    "adminField": ["字段", "说明", "示例"]
  },
  "pages": {
    "overview": {
      "title": "接入总览",
      "description": "主站 SSO + 钱包网关。身份用 JWT；资金分主站钱包与彩票内余额。",
      "roles": "职责",
      "flow": "链路",
      "e2eSequence": "端到端时序",
      "conventions": "约定",
      "readingOrder": "阅读顺序",
      "matrix": [
        ["主站", "签发 JWT；实现钱包网关", "客户"],
        ["彩票 API", "验签、玩法、划转、下注", "我方"],
        ["彩票前端", "H5 / iframe 承载", "我方"]
      ],
      "flowItems": [
        "主站登录 → 签发 JWT",
        "进入彩票（URL 跳转或 iframe）",
        "转入：主站扣款 + 彩票加款",
        "下注 / 派奖（彩票内余额）",
        "转出：彩票扣款 + 主站加款"
      ],
      "e2eRows": [
        ["1", "主站", "用户登录；服务端签发 JWT"],
        ["2", "主站", "iframe 嵌入彩票 H5，或跳转 ?token="],
        ["3", "彩票 H5", "收到 token；调用 GET /api/v1/player/me 验签建档"],
        ["4", "玩家", "在彩票 H5 内点「转入」"],
        ["5", "彩票 API", "服务端回调主站 POST /wallet/debit-for-lottery"],
        ["6", "主站钱包", "扣减 main_balance；返回 success"],
        ["7", "彩票 API", "彩票内加款"],
        ["8", "玩家", "在 H5 内下注 / 派奖"],
        ["9", "玩家", "（可选）在 H5 内转出"],
        ["10", "彩票 API", "回调主站 POST /wallet/credit-from-lottery"]
      ],
      "conventionRows": [
        ["金额", "最小货币单位整数（minor），如 2000 = 20.00"],
        ["编码", "UTF-8 JSON"],
        ["时间", "JWT：Unix 秒（iat / exp）"],
        ["鉴权", "玩家 API：Bearer JWT；钱包网关：Bearer wallet_api_key"]
      ],
      "readingItems": ["快速开始 → 接入配置 → 单点登录 → iframe 协议 → 钱包网关 → 错误码 → 上线清单"]
    },
    "quickstart": {
      "title": "快速开始",
      "description": "本地联调参考。仓库内 main-site/ 为可运行示例，密钥须与后台接入站点或彩票 .env 一致。",
      "prereq": "前置条件",
      "steps": "联调步骤",
      "testAccounts": "测试账号（main-site）",
      "reference": "参考实现",
      "note": "生产环境须使用 HTTPS、独立 site_code 与密钥。本地未配置 wallet_api_url 时，彩票 API 可能以 stub 模式跳过主站扣款（仅非 production）。",
      "prereqItems": [
        "彩票 API（lotterLaravel）与彩票前端（lotteryfront）已启动",
        "main-site 已启动（默认 http://localhost:5173）",
        "后台已创建接入站点，或彩票 .env 配置 MAIN_SITE_* 与主站 .env 对齐"
      ],
      "stepItems": [
        "超管在后台创建接入站点（见「接入配置」）并保存密钥",
        "密钥写入主站 .env；后台填入 wallet_api_url 与 iframe_allowed_origins",
        "主站登录测试账号 → iframe 打开彩票 H5（/player）",
        "确认收到 LOTTERY_READY 后下发 MAIN_INIT_TOKEN",
        "在彩票 H5 内发起转入 → 观察主站 /wallet/debit-for-lottery 被回调",
        "彩票内余额增加后在 H5 内下注",
        "（可选）H5 内转出 → 观察 /wallet/credit-from-lottery",
        "按「验收清单」用 curl 自测 JWT 与钱包网关"
      ],
      "accountRows": [
        ["alice", "alice123", "10001"],
        ["bob", "bob123", "10002"],
        ["demo", "demo123", "10003"]
      ],
      "referenceItems": [
        "代码：monorepo 内 main-site/（Next.js 测试主站壳）",
        "主站：http://localhost:5173；彩票 H5：http://localhost:3800",
        "主站 README 含环境变量与消息协议说明",
        "配置字段对照见「接入配置」页的映射表"
      ],
      "acceptance": "验收清单",
      "acceptanceItems": [
        "签发 JWT → curl GET /api/v1/player/me 返回 code=0",
        "自测钱包网关 debit：返回 success:true 且 main_balance 正确",
        "相同 idempotent_key 重放：响应与首次一致，余额不重复扣",
        "iframe：子页 LOTTERY_READY 后收到 MAIN_INIT_TOKEN，可进入大厅",
        "H5 内转入成功：主站网关日志有 debit-for-lottery 记录"
      ]
    },
    "fundamentals": {
      "title": "资金模型",
      "balances": "两层余额",
      "calls": "调用方向",
      "note": "金额一律使用 minor 整数。信用盘（代理授信）不在本文档范围。",
      "balanceRows": [
        ["main_balance", "主站钱包", "客户实现网关；彩票回调"],
        ["lottery balance", "彩票内余额", "转入后用于下注"]
      ],
      "callRows": [
        ["彩票 → 主站", "balance / debit / credit", "wallet_api_key"],
        ["彩票 H5 → 彩票 API", "me / 划转 / 下注", "玩家 JWT（主站不接）"]
      ]
    },
    "setup": {
      "title": "接入配置",
      "description": "接入站点创建后一次性下发密钥，请立即保存。",
      "weProvide": "我方提供",
      "youProvide": "客户需提供",
      "defaultPaths": "默认钱包路径",
      "envMapping": "配置映射",
      "note": "测试与生产环境的 site_code、密钥、域名须隔离。密钥写入主站 .env，不会从后台自动同步。本地开发可在彩票 .env 用 MAIN_SITE_* 作兜底。",
      "receiveRows": [
        ["site_code", "站点编码"],
        ["sso_jwt_secret", "JWT 签名密钥（主站持有）"],
        ["wallet_api_key", "钱包回调鉴权（主站校验）"],
        ["lottery_h5_base_url", "彩票入口地址"]
      ],
      "provideRows": [
        ["wallet_api_url", "HTTPS 钱包根地址"],
        ["测试账号", "site_player_id + 初始余额"],
        ["iframe origin", "嵌入时提供主站 origin"]
      ],
      "pathRows": [
        ["GET", "/wallet/balance", "余额查询"],
        ["POST", "/wallet/debit-for-lottery", "扣款"],
        ["POST", "/wallet/credit-from-lottery", "加款"]
      ],
      "envMappingRows": [
        ["site_code", "site_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；彩票允许嵌入"],
        ["彩票 API", "—", "LOTTERY_API_BASE_URL", "仅参考实现需要"]
      ],
      "adminSop": "后台建站步骤",
      "adminSopSteps": [
        "超管登录管理后台 → 配置 → 接入站点",
        "新建站点：填写站点编码（site_code）、名称、币种",
        "填写 wallet_api_url（HTTPS 根地址，无 path）、lottery_h5_base_url、iframe_allowed_origins（主站 origin，每行一个）",
        "创建成功后立即保存一次性展示的 sso_jwt_secret、wallet_api_key",
        "将密钥写入主站 .env；在列表中对站点执行「连通性测试」（探测 GET /wallet/balance）",
        "本地联调可用 main-site/ 参考实现；生产 wallet_api_url 须公网 HTTPS"
      ],
      "adminFieldRows": [
        ["code", "站点编码，写入 JWT site_code", "demo"],
        ["wallet_api_url", "客户钱包网关 HTTPS 根地址", "https://wallet.partner.com"],
        ["lottery_h5_base_url", "彩票 H5 入口", "https://lottery.partner.com"],
        ["iframe_allowed_origins", "允许嵌入的主站 origin", "https://www.partner.com"],
        ["sso_jwt_secret", "创建后一次性下发", "—"],
        ["wallet_api_key", "创建后一次性下发", "—"]
      ],
      "network": "网络要求",
      "networkItems": [
        "钱包回调由彩票服务端发起，非玩家浏览器；客户网关须对彩票服务器可达",
        "生产环境 wallet_api_url 仅允许 HTTPS 公网地址（拒绝 localhost / 私网 IP）",
        "路径固定为 /wallet/balance、/wallet/debit-for-lottery、/wallet/credit-from-lottery（可后台改 path 前缀）",
        "建议超时 ≤ 10 秒；超时可能进入待对账状态"
      ]
    },
    "sso": {
      "title": "单点登录（SSO）",
      "description": "HS256 JWT。主站签发，彩票验签。进入方式：URL 跳转或 iframe postMessage。",
      "claims": "JWT 字段",
      "sign": "签名示例",
      "entryA": "方式 A：URL 跳转",
      "entryB": "方式 B：iframe 嵌入",
      "noExchangeNote": "彩票不提供「登录换票」接口。主站登录后自行签发 JWT，玩家 API 统一用 Authorization: Bearer 携带。首次有效 JWT 调用 GET /api/v1/player/me 时自动建档。",
      "entryApi": "入场接口（彩票）",
      "entryApiNote": "可选：主站登录后服务端代调一次，用于验签与建档（参考 main-site）。日常业务由彩票 H5 自行调用玩家 API。",
      "publicApis": "公开接口（无需 token）",
      "h5ScopeNote": "划转、下注、彩票内余额查询等玩家业务接口由我方 H5 携带 JWT 调用，不在主站接入范围内。主站只需签发 JWT 并实现钱包网关。",
      "partnerApis": "主站侧接口（客户实现）",
      "refreshNote": "iframe 续签详见「iframe 协议」页。主站收到 LOTTERY_TOKEN_NEEDED 或 LOTTERY_TOKEN_REFRESH_REQUEST 后重新签发 JWT，发送 MAIN_REFRESH_TOKEN。",
      "authResponse": "鉴权失败响应",
      "errors": "错误码",
      "iframeNote": "须配置 iframe_allowed_origins。收到 token 后勿重复发送 LOTTERY_READY。",
      "claimRows": [
        ["site_code", "string", "是", "接入站点编码"],
        ["site_player_id", "string", "是", "主站用户 ID，稳定唯一"],
        ["iat", "number", "是", "签发时间（秒）"],
        ["exp", "number", "是", "过期时间（秒）；≤ 300 秒"]
      ],
      "messageRows": [
        ["→ 主站", "LOTTERY_READY", "子页就绪"],
        ["→ 主站", "LOTTERY_TOKEN_NEEDED", "请求续签"],
        ["→ 彩票", "MAIN_INIT_TOKEN", "{ token }"],
        ["→ 彩票", "MAIN_REFRESH_TOKEN", "{ token }"]
      ],
      "publicApiRows": [
        ["GET", "/api/v1/player/ping", "玩家 API 连通性探测"],
        ["GET", "/api/v1/integration/runtime-origins", "iframe 允许嵌入的 origin 列表"]
      ],
      "partnerApiRows": [
        ["POST", "/api/auth/refresh", "（参考）续签 JWT，返回新 token 供 MAIN_REFRESH_TOKEN"]
      ],
      "errorRows": [
        ["8001", "缺少 Authorization 头"],
        ["8002", "JWT 无效或已过期"],
        ["8003", "玩家未建档"],
        ["8004", "SSO 密钥未配置"],
        ["8005", "账号已冻结"]
      ]
    },
    "iframe": {
      "title": "iframe 协议",
      "description": "主站嵌入彩票 H5 时的 postMessage 约定。URL 跳转模式可跳过本章。",
      "sequence": "推荐时序",
      "envelope": "消息结构",
      "childMessages": "彩票 → 主站",
      "parentMessages": "主站 → 彩票",
      "targetOrigin": "targetOrigin",
      "envelopeNote": "消息为 JSON 对象。彩票发出 type 前缀 LOTTERY_；主站发出 MAIN_。建议带 timestamp 与 source。",
      "targetOriginNote": "postMessage 第二参数须为具体 origin（如 https://www.partner.com），禁止使用 *。主站只接受后台 iframe_allowed_origins 中的 origin；彩票子页校验 event.origin 在白名单内。",
      "timingNote": "收到 MAIN_INIT_TOKEN 后彩票子页勿再发送 LOTTERY_READY，避免主站重复下发 token。续签：子页发 LOTTERY_TOKEN_NEEDED 或 LOTTERY_TOKEN_REFRESH_REQUEST → 主站回 MAIN_REFRESH_TOKEN。",
      "sequenceSteps": [
        "主站页面嵌入 <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"
      ],
      "childMessageRows": [
        ["→ 主站", "LOTTERY_READY", "子页就绪，请求下发 token"],
        ["→ 主站", "LOTTERY_TOKEN_NEEDED", "token 失效，请求续签"],
        ["→ 主站", "LOTTERY_TOKEN_REFRESH_REQUEST", "主动请求刷新 token"],
        ["→ 主站", "LOTTERY_HEARTBEAT", "心跳（可忽略）"],
        ["→ 主站", "LOTTERY_TOKEN_REFRESHED", "续签成功通知"]
      ],
      "parentMessageRows": [
        ["→ 彩票", "MAIN_INIT_TOKEN", "{ token } 首次下发"],
        ["→ 彩票", "MAIN_REFRESH_TOKEN", "{ token } 续签"],
        ["→ 彩票", "MAIN_REQUEST_STATUS", "请求子页状态"],
        ["→ 彩票", "MAIN_NAVIGATE", "{ path } 导航"]
      ]
    },
    "wallet": {
      "title": "钱包网关",
      "description": "由客户实现。彩票服务端调用。鉴权：Bearer wallet_api_key。",
      "balance": "查询余额",
      "debit": "扣款",
      "credit": "加款",
      "response": "响应示例",
      "httpContract": "HTTP 契约",
      "httpErrors": "HTTP 错误",
      "creditNote": "请求体与扣款相同；用于转出或失败回滚加款。",
      "idempotentNote": "idempotent_key：相同键 + 相同操作须返回首次 JSON（HTTP 200），禁止重复记账；同键不同操作/金额 → success: false。",
      "queryRows": [
        ["site_code", "string", ""],
        ["site_player_id", "string", ""],
        ["currency_code", "string", ""]
      ],
      "fieldRows": [
        ["site_code", "string", ""],
        ["site_player_id", "string", ""],
        ["player_id", "number", "彩票玩家 ID"],
        ["currency_code", "string", ""],
        ["amount_minor", "integer", "minor 正整数"],
        ["idempotent_key", "string", "幂等键"]
      ],
      "httpErrorRows": [
        ["401", "unauthorized", "API Key 错误"],
        ["422", "invalid request", "字段或金额非法"],
        ["409", "main balance insufficient", "业务拒绝（如余额不足）；可含 data.main_balance"]
      ],
      "httpContractRows": [
        ["扣款/加款成功", "200", "success: true；含 external_ref_no（建议）与 data.main_balance"],
        ["余额查询成功", "200", "success: true；data.main_balance + currency_code"],
        ["参数非法", "422", "success: false；message: invalid request"],
        ["鉴权失败", "401", "success: false；message: unauthorized"],
        ["业务拒绝", "409", "success: false；message 说明原因"],
        ["幂等重放", "200", "与首次成功/拒绝响应完全一致"]
      ]
    },
    "transfer": {
      "title": "资金划转（参考）",
      "description": "内部说明：由彩票 H5 调用，非主站接入面。",
      "outOfScopeNote": "客户无需实现本节 API。转入/转出由我方 H5 携带玩家 JWT 调用；主站只需实现钱包网关 debit/credit。本节仅供理解资金如何在两端移动。",
      "requestFields": "请求字段",
      "transferIn": "转入（主站 → 彩票）",
      "transferOut": "转出（彩票 → 主站）",
      "transferResponse": "成功响应",
      "errors": "常见错误码",
      "inNote": "流程：彩票调主站 debit-for-lottery → 彩票内加款。",
      "outNote": "流程：彩票内扣款 → 彩票调主站 credit-from-lottery。",
      "responseNote": "转入与转出结构相同；direction 为 in / out。幂等重放返回相同 data。",
      "requestFieldRows": [
        ["amount", "integer", "minor 正整数"],
        ["currency", "string", "可选；默认玩家 default_currency"],
        ["idempotent_key", "string", "全局唯一；重复须返回相同结果"]
      ],
      "errorRows": [
        ["1001", "彩票余额不足（转出）"],
        ["1009", "主站钱包处理失败"],
        ["1010", "幂等键冲突（同键不同金额）"],
        ["2003", "请先转入后再下注"]
      ]
    },
    "errors": {
      "title": "错误码",
      "sso": "SSO 鉴权",
      "lotteryWallet": "彩票钱包 / 划转",
      "gateway": "客户钱包网关（HTTP）",
      "idempotentNote": "幂等：同一 idempotent_key 须返回相同结果；同键不同金额 → 1010。",
      "ssoRows": [
        ["8001", "缺少 Authorization 头"],
        ["8002", "JWT 无效或已过期"],
        ["8003", "玩家未建档"],
        ["8004", "SSO 密钥未配置"],
        ["8005", "账号已冻结"]
      ],
      "lotteryRows": [
        ["1001", "彩票余额不足"],
        ["1009", "主站钱包处理失败"],
        ["1010", "幂等键冲突"],
        ["2003", "请先转入后再下注"]
      ],
      "gatewayRows": [
        ["401", "unauthorized", "API Key 错误"],
        ["422", "invalid request", "字段或金额非法"],
        ["409", "—", "业务拒绝（如余额不足）"]
      ]
    },
    "golive": {
      "title": "上线清单",
      "checklist": "检查项",
      "items": [
        "测试与生产：site_code、密钥、域名完全分离",
        "JWT 仅服务端签发，有效期 ≤ 5 分钟",
        "钱包接口走 HTTPS，超时建议 ≤ 10 秒",
        "idempotent_key 幂等处理",
        "iframe 模式：配置 iframe_allowed_origins",
        "全链路：转入 → 下注 → 派奖 → 转出"
      ]
    }
  }
}
