server/laf-cloud/functions/goldMiner/FRONTEND-API.md
2026-09-24 17:59:52 +08:00

710 lines
44 KiB
Markdown
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.

# 黄金矿工前端接口文档 V1.7
更新时间:2026-09-24。依据当前 `local` 分支实现编写,包含精简后的 `info` 协议与服务器到期结算协议。示例价格、目标和金币仅用于联调,页面必须使用接口返回值。部署环境需具有相同版本的后端代码。V1.7 增加 VIP 六档礼包和 pending_unlock 状态,允许任意领取达标任务。补发查询:分页大小由后端控制,新增 no_pending_rewards 状态,未完成结算的期不再阻塞其他期奖励。配置/资格/发奖存储为两个集合;配置示例和迁移见 [公共配置说明](../activityConfig/README.md)。
## 1. 接入约定与接口总览
所有下列请求均使用 **POST + JSON Body**,请求头为 `Content-Type: application/json`。地址前缀 `{{baseUrl}}` 替换为后端提供的测试服或正式服域名;不要将两个环境的账号、订单和 token 混用。部分通用函数配置也允许 GET,但实现读取 `ctx.body`,前端统一使用 POST。
| 用途 | 地址 | action |
| --- | --- | --- |
| 活动信息、任务状态 | `/goldMiner/index` | `info` |
| 客服渠道创建活动订单 | `/goldMiner/index` | `create_order` |
| 申请领取单档奖励 | `/goldMiner/index` | `claim` |
| 确认客户端已保存奖励 | `/goldMiner/index` | `confirm_delivery` |
| 查询往期补发列表 | `/goldMiner/index` | `settlements` |
| 确认补发奖励已保存 | `/goldMiner/index` | `confirm_settlement_delivery` |
| 兼容:确认补发通知已处理 | `/goldMiner/index` | `ack_settlement` |
| 通关上报、读取主线进度 | `/userLevel` | `save` / `read` |
| 保存、读取金币余额 | `/userCoin` | `save` / `read` |
| 原生支付下单 | `/wx/orderPaySig`;iOS 入口 `/wx/iosorderPaySig` | 不传 |
| 查询支付及活动权益 | `/wx/getPayInfo`、`/wx/iosgetPayInfo` | 不传 |
| 兼容旧订单领取入口 | `/wx/getOrderReward` | 不传 |
| 外部支付页转单 | `/wx/checkIos` | 不传 |
`iosorderPaySig.ts` 在仓库中没有配套 YAML,使用该入口前需后端确认测试服已发布同名 POST 云函数;不能仅凭源码存在判断线上可调用。
### 公共参数与类型
除支付页票据鉴权方式外,游戏端请求均在 Body 中携带以下字段:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| uid | string | 是 | 当前登录玩家 ID,使用 `users` 账号 |
| token | string | 是 | 当前登录 token;过期后按游戏登录流程刷新 |
| action | string | 按接口 | `/goldMiner/index`、`userLevel`、`userCoin` 必填 |
黄金矿工不支持 `gameName: "iaa"`,游戏端不传此值。无需在 Authorization Header 中重复传 token。
时间戳均为 Unix **毫秒**;日期安排按北京时间 `Asia/Shanghai`。活动周四 00:00 开始,下周一 00:00 结束,结束时间为不包含的边界。金额 `priceFen` 单位为分,显示元时除以 100。数量及进度使用整数,布尔值使用 JSON boolean。
普通周活动的 `periodId` 格式为 `goldMiner:YYYY-MM-DD`,日期是该期周四,例如 `goldMiner:2026-09-17`。测试环境临时期使用 `goldMiner:test:标识`,起止时间由服务端配置。客户端必须原样使用服务端返回的期 ID 和起止时间,不要自行按周历生成或截取期 ID;其他请求和返回结构不变。详见 [临时期说明](TEST-PERIODS.md)。`eventId`、`requestId`、`createRequestId` 等标识支持 1~100 位英文字母、数字、下划线、冒号及连字符;可使用 UUID。生成后必须随原操作保存,重试时复用。
### 返回包装
活动接口及黄金矿工支付分支成功结构:
```json
{"code":1,"data":{},"msg":"成功"}
```
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| code | number | `1` 为业务成功,`0` 为业务失败;不能仅依靠 HTTP 状态判断 |
| data | object / null | 各接口业务数据;活动标准失败时为 null |
| msg | string | 提示信息,不作为程序状态判断依据 |
| errorCode | string | 活动标准失败时返回,用于程序分支 |
```json
{"code":0,"data":null,"errorCode":"NOT_PAID","msg":"本期尚未解锁,请先完成支付并确认活动权益"}
```
配置校验失败时,`msg` 返回首个不合法字段的完整路径及约束;数组下标从 0 开始。例如:
```json
{"code":0,"data":null,"errorCode":"CONFIG_UNAVAILABLE","msg":"config.tasks[1].targetWins 必须大于前一档目标(3)"}
```
普通/临时期发布和运行时配置校验使用同一规则;任务 ID 重复、序号不连续、不支持的奖励类型、金额类型错误等均有对应提示。临时期时间冲突会指出冲突期 ID 和窗口;支付配置错误会指出不合法的服务端环境变量名,不返回密钥值。`errorCode`、`code`、`data` 的结构不变,前端继续使用 errorCode 分支,msg 用于展示及联调排查,不应匹配具体文案。未知系统异常仍返回 RETRYABLE 和重试提示,不直接透传内部异常。
例外:`wx/checkIos` 直接返回 `true` / `false`;通用接口的前置校验可能只有 `code/data/msg`、没有 `errorCode`。`userLevel` 和 `userCoin` 的无效 action 返回 `{ "code":400, "message":"无效的操作类型,请使用 save 或 read" }`。网络错误、空响应也需单独处理。
## 2. 获取活动信息:info
**POST `{{baseUrl}}/goldMiner/index`**
请求只有公共参数,`action` 固定为 `info`,不传 periodId。
```json
{"action":"info","uid":"USER_ID","token":"LOGIN_TOKEN"}
```
### data 字段
| 字段 | 类型 | 返回条件 | 含义 |
| --- | --- | --- | --- |
| serverTime | number | 总是 | 服务器时间,辅助校正倒计时 |
| status | string | 总是 | 下表中的活动状态 |
| periodId | string | 总是 | 当前日历对应的活动期 ID |
| startsAt | number | 除 locked 外 | 该期开始时间 |
| endsAt | number | 除 locked 外 | 该期结束边界,倒计时到此结束 |
| configVersion | string | 可展示活动时 | 本期冻结的配置版本,可用于日志排查 |
| priceFen | number | 可展示活动时 | 本期单次解锁价格,单位分 |
| productId | string | 可展示活动时 | 下单使用的商品 ID,不要写死 |
| vipLevel | number | 使用 VIP 配置且可展示活动时 | 当前礼包档位 0~5;旧单档配置不返回 |
| progressWins | number | 可展示活动时 | 本期累计有效胜利数;VIP 升降档前保留累计值,可能超过当前 maxTarget,见 2.3 |
| maxTarget | number | 可展示活动时 | 最后一档累计目标 |
| tasks | array | 总是 | 按目标关数排好的任务;locked/unavailable 时为空 |
“可展示活动时”指 `purchasable`、`purchase_disabled`、`unlocked`。缺省字段不是 0 或 false,不能用上一期缓存填充。
| status | 含义 | 前端处理 |
| --- | --- | --- |
| unavailable | 非开放时段、无有效配置,或请求期间活动已结束 | 关闭当期购买和领取;往期补发仍可单独查询 |
| locked | 活动开放,但未满足主线资格门槛 | 不展示可购买任务页;按产品要求展示入口门槛提示 |
| purchasable | 已获资格,未付费,允许购买 | 展示价格和购买按钮;未付费也累计进度 |
| purchase_disabled | 已获资格,未付费,本期暂停购买 | 隐藏/禁用购买按钮;仍展示和累计进度 |
| unlocked | 本期资格已解锁 | 隐藏购买按钮,按任务状态展示领取操作 |
默认通过主线第 41 关后获得资格;门槛可由后端配置。info 不返回具体门槛数值,如需显示门槛文案应与活动配置保持一致。`unlocked` 包含正常购买及延期支付顺延生效;前端不需区分来源。
### tasks[] 字段
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| taskId | string | 档位 ID,领取时原样传回;不能拿数组下标替代 |
| targetWins | number | 本期累计通关目标,例如 3、5、7、9、10 |
| itemsSnapshot | array | 本档奖励快照,元素字段见下表 |
| claimStatus | string | locked / pending_unlock / claimable / issuing / claimed |
| completed | boolean | 累计胜利是否达到该档目标,与是否付费、是否可领取无关 |
| progress | number | `min(progressWins, targetWins)`,是通关数,不是百分比 |
| itemsSnapshot[] 字段 | 类型 | 含义 |
| --- | --- | --- |
| type | string | 道具类型,当前配置仅支持 `coin`(金币) |
| count | number | 道具数量,正整数 |
保留数组结构以便以后扩展道具。出现客户端暂不支持的道具类型时,不应静默跳过并确认已到账。
| claimStatus | 含义 | 操作 |
| --- | --- | --- |
| locked | 目标未达成 | 不能领取 |
| pending_unlock | 已达标,但尚未付费解锁 | 展示“已达标待解锁”,支付成功后刷新 |
| claimable | 已付费、已达标,不受其他任务领取状态影响 | 调用 claim |
| issuing | 已取得奖励授权,但尚未确认客户端保存成功 | 活动内对同一 periodId/taskId 再调用 claim;活动结束后从 settlements 恢复原 grantId |
| claimed | 本档客户端到账已经确认 | 展示已领取,不再增加金币 |
任务展示顺序由后端保证,无需 sequence。已付费后,任意已达标任务均可申请领取和确认;第一档未领取不阻止领取后续档。
完整示例:本期尚未购买,但已通关 3 次。
```json
{
"code": 1,
"data": {
"serverTime": 1789723456450,
"status": "purchasable",
"periodId": "goldMiner:2026-09-17",
"startsAt": 1789574400000,
"endsAt": 1789920000000,
"configVersion": "gold_miner_test_20260917_v1",
"priceFen": 100,
"productId": "gold_miner",
"progressWins": 3,
"maxTarget": 10,
"tasks": [
{"taskId":"task_1","targetWins":3,"itemsSnapshot":[{"type":"coin","count":100}],"claimStatus":"pending_unlock","completed":true,"progress":3},
{"taskId":"task_2","targetWins":5,"itemsSnapshot":[{"type":"coin","count":200}],"claimStatus":"locked","completed":false,"progress":3},
{"taskId":"task_3","targetWins":7,"itemsSnapshot":[{"type":"coin","count":300}],"claimStatus":"locked","completed":false,"progress":3},
{"taskId":"task_4","targetWins":9,"itemsSnapshot":[{"type":"coin","count":400}],"claimStatus":"locked","completed":false,"progress":3},
{"taskId":"task_5","targetWins":10,"itemsSnapshot":[{"type":"coin","count":500}],"claimStatus":"locked","completed":false,"progress":3}
]
},
"msg": "成功"
}
```
未达门槛时的完整 data:
```json
{"serverTime":1789723456450,"status":"locked","periodId":"goldMiner:2026-09-17","tasks":[]}
```
不可用时的完整 data(示例为周一休息期):
```json
{"serverTime":1789920000000,"status":"unavailable","periodId":"goldMiner:2026-09-17","startsAt":1789574400000,"endsAt":1789920000000,"tasks":[]}
```
不可用状态下的日期来自活动日历,不能据此认定活动已启用或计算下一期购买资格。
### 2.3 VIP 礼包选择
配置使用 `vipTiers` 时,后端读取 `users.vip_level`(VIP0~VIP5),只返回匹配档位的 `priceFen/productId/tasks/maxTarget`,并返回 `vipLevel` 表示本期礼包档位。前端不提交 VIP 值,也不自行选择价格或奖励。旧版单档配置继续使用原协议,不返回 vipLevel。
VIP 缺失、无效或超出 0~5 时使用 VIP0;VIP0 即使已付费或不符合首次付费资格,也照常展示和购买 VIP0,不做首次付费资格拦截。本规则以本次业务确认口径为准。
未创建订单时,玩家 VIP 更新可刷新本期礼包,已有累计进度保留。创建订单时锁定报价和奖励;未付款订单重试和已付款礼包都保持原快照,不因 VIP 更新改变。页面信息过期导致 itemid 不匹配时返回 INVALID_PRODUCT,刷新 info 后使用新 productId 重新下单;不得改传价格绕过校验。已付费顺延权益保留原购买档位。
未锁定报价前,进度最多累计至配置中最高 VIP 的最终目标,以便 VIP 升档时不丢失已完成通关。因此 progressWins 可能大于当前礼包 maxTarget;maxTarget 是该礼包领满目标,任务 progress 仍限制到本任务 targetWins。未付费达标返回 pending_unlock;付费后所有达标未领取任务同时为 claimable。多档奖励可任意选择,但金币整值保存仍需串行执行,防止余额覆盖。
当前周期和计数口径保持原实现:普通周活动周四至周日,测试临时期使用指定窗口,未付费也累计。不采用支付后 168 小时或支付后才计数。六档完整发布示例见 [VIP 配置](vip-tiers.example.json),部署及配置说明见 [VIP 礼包说明](VIP-TIERS.md)。
## 3. 通关上报:userLevel/save
**POST `{{baseUrl}}/userLevel`**。在现有关卡保存请求中附带 `goldMiner`,不要依赖玩家是否打开活动页面或是否付费。每次有效胜利上报一个事件,黄金矿工每个事件加 1,不按 levelAmount 的差值一次增加多关。
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| action | string | 是 | `save` |
| uid / token | string | 是 | 公共鉴权参数 |
| levelAmount | number | 是 | 本次保存后的主线通关整值;无尽模式保持现有主线值 |
| isWuXian | string | 无尽必填 | 无尽传字符串 `"true"`;主线省略。沿用旧协议字符串形式 |
| goldMiner | object | 活动统计必填 | 不携带则只保存普通关卡,不增加活动进度 |
| goldMiner.periodId | string | 是 | 本次胜利首次上报对应的活动期;重试不更换 |
| goldMiner.eventId | string | 是 | 本次胜利的唯一事件 ID;重试保持不变 |
| goldMiner.outcome | string | 是 | 固定 `win`,失败、放弃不报活动胜利 |
| goldMiner.mode | string | 是 | `main` 或 `endless`,必须与 isWuXian 对应 |
| goldMiner.clearedMainLevel | number | 主线必填 | 本次通过的主线关号,正整数且须等于 levelAmount |
| goldMiner.endlessSequence | number | 无尽必填 | 无尽成功结算序号,正整数;同一胜利重试相同,新胜利使用新序号 |
主线示例:
```json
{"action":"save","uid":"USER_ID","token":"LOGIN_TOKEN","levelAmount":42,"goldMiner":{"periodId":"goldMiner:2026-09-17","eventId":"win-main-42","outcome":"win","mode":"main","clearedMainLevel":42}}
```
无尽示例(1000 仅是假设的当前主线通关值):
```json
{"action":"save","uid":"USER_ID","token":"LOGIN_TOKEN","levelAmount":1000,"isWuXian":"true","goldMiner":{"periodId":"goldMiner:2026-09-17","eventId":"win-endless-123","outcome":"win","mode":"endless","endlessSequence":123}}
```
无尽序号需持久化,并避免同一账号多设备生成冲突序号;不能使用循环复用的关卡模板号。同一期按 eventId 和“模式 + 关号/序号”两层去重:换 eventId 重报同一关也不会再加活动进度。
### 返回字段
```json
{"code":1,"data":{"goldMiner":{"code":1,"activityCounted":true,"duplicate":false,"progressWins":1},"IP":"127.0.0.1","address":"其他","rank":-1,"levelAmount":42,"film":42,"timestamp":1789723456450},"msg":"用户数据更新成功"}
```
| data 字段 | 类型 | 含义 |
| --- | --- | --- |
| goldMiner | object,可缺省 | 活动处理结果;仅请求携带活动事件时返回 |
| IP / address | string | 原关卡接口的请求 IP / 地址信息 |
| rank | number | 原排行处理结果,默认 -1;活动 UI 不依赖 |
| levelAmount | number | 已保存的主线通关值 |
| film | number | 原关卡进度相关字段,不是活动进度 |
| timestamp | number | 本次响应时间 |
| data.goldMiner 字段 | 类型 | 含义 |
| --- | --- | --- |
| code | number | 活动处理独立结果:1 成功、0 失败 |
| activityCounted | boolean | 成功时是否新增了 1 次活动胜利 |
| duplicate | boolean,可缺省 | 成功时是否命中历史事件;资格关分支没有此字段 |
| progressWins | number | 活动处理成功后的累计胜利数 |
| reason | string,可缺省 | `qualification_level` 表示本次关号不高于资格门槛,不计入活动 |
| data / errorCode / msg | null / string / string | 活动失败时使用标准失败包装字段 |
活动失败仍可能出现外层 code=1,表示主线保存成功。例如 `data.goldMiner={"code":0,"data":null,"errorCode":"PERIOD_ENDED","msg":"PERIOD_ENDED"}`,不能因此重复推进普通关卡。
- 通过第 41 关获得资格,但该次胜利不计;从后续胜利开始计数。开放前的胜利不追溯。
- 开放前开局、开放期间胜利并成功上报可计;结束后才到达服务端的新事件不补记。已记录事件结束后重试可返回 duplicate=true,但不会新增进度。
- 达到 maxTarget 后 activityCounted=false;它不一定意味着 duplicate=true。
- 旧 userLevel 会在与已存主线值相差超过 10 时提前结束,可能没有正常 JSON 返回;应按既有关卡保存约束调用。
- 活动事件去重不等于整个 userLevel 幂等:重试无尽 save 仍可能影响旧的生涯统计。按现有通关队列管理重试,不能把该接口当作纯活动重试入口无限重放。
读取普通主线进度可传 `{"action":"read","uid":"USER_ID","token":"LOGIN_TOKEN"}`;成功 data 为 `levelAmount:number, film:number, timestamp:number`,不返回黄金矿工状态。刷新活动使用 info。
## 4. 支付下单与查询
### 4.1 原生支付下单
**POST `/wx/orderPaySig`** 或 **POST `/wx/iosorderPaySig`**,按项目支付入口选择。
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| uid / token | string | 是 | 公共鉴权参数 |
| itemid | string | 是 | info.productId,注意参数名为小写 itemid |
| createRequestId | string | 是 | 本次创建订单请求标识,重试复用 |
```json
{"uid":"USER_ID","token":"LOGIN_TOKEN","itemid":"gold_miner","createRequestId":"buy-20260917-001"}
```
不需传 periodId、价格或数量,后端绑定当前开放期、冻结价格和数量 1。即使传入 itemPrice/itemCount,也不能修改活动价格或数量。
| 成功 data 字段 | 类型 | 含义 |
| --- | --- | --- |
| outTradeNo | string | 后端订单号,持久化用于查询和恢复 |
| signData | string | 原生支付签名原文,是 JSON 字符串;完整保留,不修改或解析后重新序列化 |
| signature | string | 玩家会话签名,交给现有原生支付流程 |
| paySig | string | 支付签名,交给现有原生支付流程 |
返回结构示例(签名内容仅为占位,不能用于实际支付):
```json
{"code":1,"data":{"outTradeNo":"ORDER_NO_FROM_CREATE","signData":"SIGNED_JSON_STRING_FROM_SERVER","signature":"SESSION_SIGNATURE_FROM_SERVER","paySig":"PAY_SIGNATURE_FROM_SERVER"},"msg":"成功"}
```
测试服与正式服隔离不等于免费支付沙箱;当前原生签名使用支付环境 env=0,联调支付应使用后端确认的测试商品和金额。
同环境、账号、期次只创建一个稳定订单;重试或更换 createRequestId 不会创建第二笔活动订单。不要在重试时切换原生/客服渠道,已有另一渠道订单会报 PAYMENT_CHANNEL_CONFLICT。
### 4.2 客服渠道下单
**POST `/goldMiner/index`**。
```json
{"action":"create_order","channel":"legacy_ios","uid":"USER_ID","token":"LOGIN_TOKEN","itemid":"gold_miner","createRequestId":"buy-20260917-001"}
```
参数与原生下单相同,另外必填 `action:"create_order"` 和 `channel:"legacy_ios"`。成功 data:
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| outTradeNo | string | 本期稳定订单号 |
| periodId | string | 订单所属期 |
| priceFen | number | 订单价格,分 |
| productId | string | 商品 ID |
| quantity | number | 固定 1 |
| channel | string | 固定 legacy_ios |
| expiresAt | number | 本次客服票据到期时间,最长 15 分钟且不超过活动结束 |
| sessionFrom | string | 已签名的客服入口参数字符串,原样传入现有 `wx.openCustomerServiceConversation` 的 sessionFrom |
返回结构示例(sessionFrom 为不可手工生成的占位字符串):
```json
{"code":1,"data":{"outTradeNo":"ORDER_NO_FROM_CREATE","periodId":"goldMiner:2026-09-17","priceFen":100,"productId":"gold_miner","quantity":1,"channel":"legacy_ios","expiresAt":1789724356450,"sessionFrom":"SIGNED_SESSION_FROM_SERVER"},"msg":"成功"}
```
不要自己拼接 sessionFrom、修改内部字段或把登录 token 放进支付页 URL。票据到期且活动仍开放时,可重新调用 create_order 获取新票据,订单号保持稳定。客服网关处理后给玩家发送支付页链接;游戏端不直接调用 `wx/KeFu` 模拟客服事件。
### 4.3 查询支付结果和权益
**POST `/wx/getPayInfo`** 或 **POST `/wx/iosgetPayInfo`**。兼容入口 **POST `/wx/getOrderReward`** 对黄金矿工也返回同一协议,不直接发金币。
```json
{"uid":"USER_ID","token":"LOGIN_TOKEN","outTradeNo":"ORDER_NO_FROM_CREATE"}
```
三个参数均为必填 string,不需 action。原生支付以已验证的服务端通知为依据;客服渠道还会按后端节流策略主动查单。客户端支付成功回调只用于触发查询,不能代替服务端确认。
| 成功 data 字段 | 类型 | 含义 |
| --- | --- | --- |
| pay_state | number | 黄金矿工成功固定为 2,表示支付及权益已确认,不表示所有任务已领奖 |
| rewardDelivery | string | 固定 `goldMiner.claim`,活动内逐档 claim,结束后查询 settlements |
| goldMiner | object | 当前订单的活动元数据,完整字段见下面两表 |
`goldMiner` 目前为订单快照直出,比 info 字段多。前端主要使用 `fulfillmentRoute` 和 `targetPeriodId`,其余字段供核对日志,不用于自行计算领取资格。
| goldMiner 字段 | 类型 | 含义 |
| --- | --- | --- |
| schemaVersion | number | 当前 1 |
| uid / accountScope | string | 所属玩家 / 固定 users |
| paymentChannel | string | native 或 legacy_ios |
| periodId | string | 原购买期 |
| startsAt / endsAt | number | 原购买期时间边界 |
| configVersion / configHash | string | 订单绑定配置版本 / 哈希 |
| productSnapshot | object | `productId:string`、`priceFen:number`,订单冻结商品信息 |
| createRequestId | string | 首次建单请求标识 |
| confirmedAt | number | 服务端确认支付的毫秒时间 |
| fulfillmentStatus | string | 成功响应中为 fulfilled;处理中/失败通过错误响应表示 |
| fulfillmentRoute | string | current_period / settle_original / carry_next,见下表 |
| targetPeriodId | string | 实际分配权益的目标期 |
| revision | number | 内部版本字段,前端无需参与维护 |
| entitlementId | string | 已分配权益记录 ID |
| fulfilledAt | number | 权益处理完成时间 |
| lastErrorCode | string,可缺省 | 曾经处理失败的原因,成功后可能仍保留,不能覆盖当前成功结论 |
| nextRetryAt | number,可缺省 | 曾设置的后端重试时间,同样可能是历史值 |
客服订单还可能携带以下字段;按实际存在读取,前端不需回传:
| goldMiner 字段 | 类型 | 含义 |
| --- | --- | --- |
| merchantSnapshot | object | `mchid/appid/notifyUrl/pageUrl/checkUrl` 均为 string:商户及接入地址快照 |
| prepayState | string | created / creating / ready / paid / closed;成功支付后为 paid |
| prepayId / prepayExpiresAt | string / number | 预支付 ID / 到期时间,可缺省 |
| ioLeaseUntil / ioLeaseToken | number / string | 后端并发处理锁信息,token 可缺省,不是玩家鉴权 token |
| nextQueryAt / queryAttempts | number | 后端下次查单时间 / 查单次数 |
| lastQueryError | string / null | 最近查单错误,可缺省 |
| transactionId | string | 微信交易号 |
| paidAt | number | 支付平台记录的支付成功时间 |
| confirmationSource | string | merchant_notify 或 merchant_query,支付确认来源 |
| fulfillmentRoute | 含义 | 前端下一步 |
| --- | --- | --- |
| current_period | 权益分配到原购买期 | 重新 info;若此时已结束则查询 settlements |
| settle_original | 延迟支付确认时原期已结束,存在达标奖励 | 查询 settlements,按返回的原授权补发 |
| carry_next | 延迟支付确认时原期无可结算奖励,资格顺延下一期 | 提示目标期生效;目标期开放后 info 返回 unlocked,无需再买 |
原生订单正常解锁的完整响应示例(标识和哈希为占位):
```json
{
"code": 1,
"data": {
"pay_state": 2,
"rewardDelivery": "goldMiner.claim",
"goldMiner": {
"schemaVersion": 1,
"uid": "USER_ID",
"accountScope": "users",
"paymentChannel": "native",
"periodId": "goldMiner:2026-09-17",
"startsAt": 1789574400000,
"endsAt": 1789920000000,
"configVersion": "gold_miner_test_20260917_v1",
"configHash": "CONFIG_HASH_FROM_SERVER",
"productSnapshot": {"productId":"gold_miner","priceFen":100},
"createRequestId": "buy-20260917-001",
"confirmedAt": 1789723456450,
"fulfillmentStatus": "fulfilled",
"fulfillmentRoute": "current_period",
"targetPeriodId": "goldMiner:2026-09-17",
"revision": 0,
"entitlementId": "ENTITLEMENT_ID_FROM_SERVER",
"fulfilledAt": 1789723456500
}
},
"msg": "活动权益已确认"
}
```
权益只顺延到紧邻的下一期,不无限滚动。查询成功不代表应一次发放全部任务金币。遇到 PAYMENT_PENDING 可间隔重试原订单查询;网络失败也保存订单号,登录恢复后继续查询,不能直接重新付款。建议有限次数退避轮询,离开支付流程后停止高频请求。
### 4.4 外部支付页转单:checkIos
**POST `/wx/checkIos`**。由现有 order3.html 处理,普通活动页面通常无需主动调用。
支付页票据方式:
```json
{"openid":"ORDER_OWNER_OPENID","time":"HANDOFF_TICKET_FROM_PAYMENT_LINK","outTradeNo":"ORDER_NO_FROM_CREATE"}
```
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| openid | string | 票据方式必填 | 支付页链接携带的订单所属 openid |
| time | string | 票据方式必填 | 链接携带的 handoff 签名票据,不是时间戳,不是 sessionFrom 中的客服票据 |
| outTradeNo | string | 是 | 原订单号 |
| uid / token | string | 游戏鉴权方式必填 | 游戏端也可只用 uid、token、outTradeNo 调用,替代 openid/time |
返回 JSON boolean:`true` 表示订单关联/兼容转单成功,`false` 表示校验或处理失败;不返回 code。**true 不表示已支付,更不表示已发奖**。之后仍由游戏端调用支付查询接口。支付通知接口 `goldMiner/merchantNotify`、原生支付回调和管理接口不属于前端调用范围。
## 5. 奖励申请、金币保存与确认
### 5.1 申请奖励:claim
**POST `/goldMiner/index`**。
```json
{"action":"claim","uid":"USER_ID","token":"LOGIN_TOKEN","periodId":"goldMiner:2026-09-17","taskId":"task_1","requestId":"claim-20260917-task1"}
```
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| action | string | 是 | claim |
| uid / token | string | 是 | 公共鉴权参数 |
| periodId | string | 是 | 活动内奖励所属期;过期后此接口不再用于补发 |
| taskId | string | 是 | info 或补发记录提供的档位 ID |
| requestId | string | 是 | 本次领取请求标识,重试复用 |
```json
{"code":1,"data":{"grantId":"GRANT_ID_FROM_SERVER","taskId":"task_1","items":[{"type":"coin","count":100}],"claimStatus":"issuing","claimedThrough":0,"requiresClientDelivery":true},"msg":"成功"}
```
| data 字段 | 类型 | 含义 |
| --- | --- | --- |
| grantId | string | 服务端稳定的本档发奖标识,用它对客户端到账去重 |
| taskId | string | 本次档位 |
| items | array | 本次授权道具,元素为 type:string、count:number;金币数量以此为准 |
| claimStatus | string | issuing 或 claimed,与 info 的五态枚举范围不同 |
| claimedThrough | number | 从第一档开始连续已确认的档位数,兼容字段,不是领取限制或已领总数 |
| requiresClientDelivery | boolean | true 表示服务端尚未确认到账;false 表示已确认,不再加金币 |
活动有效期内,同账号、期次和档位重复 claim 返回同一 grantId,更换 requestId 也不会产生第二份授权。活动结束时起,所有 claim(包括旧授权重试)统一返回 `PERIOD_SETTLEMENT_REQUIRED` 和“活动已结束,服务器正在结算奖励,请查询补发清单”;该提示引导前端查询,不表示后台一定尚未完成结算,实际状态以 settlements 为准。到期校验覆盖数据库写入边界,跨零点请求不能新建普通领奖授权。`requiresClientDelivery=true` 不代表客户端肯定还未加金币,也可能是金币已保存但确认请求失败,必须结合本地交付记录恢复。
### 5.2 金币保存:userCoin
**POST `/userCoin`**。继续沿用游戏现有金币流程,后端不直接加金币。
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| action | string | 是 | read 或 save |
| uid / token | string | 是 | 公共鉴权参数 |
| coinAmount | number | save 必填 | 保存后的金币总余额,非奖励增量;传 100 表示余额变为 100 |
```json
{"action":"read","uid":"USER_ID","token":"LOGIN_TOKEN"}
```
```json
{"action":"save","uid":"USER_ID","token":"LOGIN_TOKEN","coinAmount":1600}
```
read/save 成功均返回 `data.coinAmount:number`(当前/已保存余额)和 `data.timestamp:number`(用户记录/保存响应时间)。save 示例:
```json
{"code":1,"data":{"coinAmount":1600,"timestamp":1789723456450},"msg":"用户数据更新成功"}
```
协议是整值覆盖,不支持 grantId 或原子增量;负值会被旧接口归零。不得把奖励数量直接当总余额上传,也不能让多条金币写入并发覆盖。按现有金币管理队列串行保存,成功后再确认本档到账。
### 5.3 确认到账:confirm_delivery
**POST `/goldMiner/index`**。
```json
{"action":"confirm_delivery","uid":"USER_ID","token":"LOGIN_TOKEN","periodId":"goldMiner:2026-09-17","taskId":"task_1","grantId":"GRANT_ID_FROM_SERVER"}
```
所有字段必填,均为 string;action 固定 confirm_delivery,periodId/taskId 与 claim 一致,grantId 必须为 claim 返回值。无需 requestId,也不传金币数量。
```json
{"code":1,"data":{"grantId":"GRANT_ID_FROM_SERVER","claimStatus":"claimed","claimedThrough":1},"msg":"成功"}
```
| data 字段 | 类型 | 含义 |
| --- | --- | --- |
| grantId | string | 已确认的授权 ID |
| claimStatus | string | 固定 claimed |
| claimedThrough | number | 当前已确认的连续档位数 |
同一授权重复确认成功且不重复发放。任务可任意顺序确认,不影响其他已达标任务的 claimable 状态。确认接口不会再修改金币余额。到期前已经取得的授权,结束后仍允许用此接口确认;新补发流程统一使用 confirm_settlement_delivery,两者共用同一奖励状态,不重复推进档位。
### 5.4 客户端恢复顺序
1. 调用 claim,持久化 uid、periodId、taskId、grantId、items 和交付阶段。
2. 若服务端已 claimed / requiresClientDelivery=false,不再加金币;若本地记录该 grantId 已保存金币,也跳过加币。
3. 尚未保存时,交由游戏金币模块执行一次到账并保存总余额,记录该 grantId 的保存结果。不要每次重试都重新做“当前余额 + 奖励”。
4. 保存确定成功后调用 confirm_delivery;确认失败只重试确认。成功后刷新 info 或继续补发下一档。
当前 userCoin/save 与发货确认(confirm_delivery / confirm_settlement_delivery)**不是同一个事务**,服务端也不验证金币保存凭证。断线、重装或多设备并发时,仅靠 grantId 不能保证金币端严格只到账一次;余额保存结果不明时,需要沿用游戏已有恢复机制核对,不能仅根据 issuing 自动再次加币,也不能未发奖就提前确认。这是现有金币协议的限制,本次文档不改变该协议。
## 6. 服务器到期结算与前端补发
活动结束后不再允许通过 claim 申请奖励。服务器冻结进度,为已付费、达标且尚未确认发货的奖励生成稳定授权;前端只查询结果、发放资源并确认。未达标奖励失效,未付费不能结束后补买;已有订单的延迟支付确认仍按原支付路由处理。
`goldMiner/jobs` 按部署的每分钟触发器分批处理;延迟支付履约也可生成结算。查询接口不触发结算、不加金币。必须部署更新后的 service/index/jobs 及触发器;没有触发器时不能依赖查询自动补建清单。历史旧数据应先按 [迁移说明](../activityConfig/MIGRATION.md) 完成集合合并;后台再处理未结算状态,保留已有 grantId。
### 6.1 查询所有已结束活动的未确认奖励:settlements
**POST `/goldMiner/index`**。
```json
{"action":"settlements","uid":"USER_ID","token":"LOGIN_TOKEN"}
```
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| action | string | 是 | settlements |
| uid / token | string | 是 | 公共鉴权参数 |
| afterId | string | 否 | 上一页 nextCursor;首次省略,不是 settlementId 或 periodId |
查询覆盖该玩家所有已结束活动,不限最近日历期或最后参与的一期。跨期未发完的奖励不会被较新的活动记录遮蔽。分页使用稳定的内部 ID 顺序,每期 rewards 按档位顺序排列。前端无需传 limit,后端固定每页扫描 20 条历史玩家期记录(兼容忽略旧客户端的 limit),不是限制奖励档位数量。
```json
{
"code": 1,
"data": {
"status": "pending_delivery",
"items": [{
"settlementId": "SETTLEMENT_ID_FROM_SERVER",
"periodId": "goldMiner:2026-09-17",
"rewards": [
{"taskId":"task_1","grantId":"GRANT_ID_1","items":[{"type":"coin","count":100}]},
{"taskId":"task_2","grantId":"GRANT_ID_2","items":[{"type":"coin","count":200}]}
]
}],
"nextCursor": null
},
"msg": "成功"
}
```
| data 字段 | 类型 | 含义 |
| --- | --- | --- |
| status | string | pending_delivery、settling 或 no_pending_rewards,见下表 |
| items | array | 本页需要补发的结算单;只含尚未确认奖励,整单发完后不再返回 |
| nextCursor | string / null | 非空时作为 afterId 继续翻页,settling 时也可翻页;为空表示本轮扫描结束,仍有待处理状态时从第一页复查 |
| status 与列表 | 含义及操作 |
| --- | --- |
| pending_delivery,items 非空 | 优先处理本页已准备好的奖励;其他期仍在结算不会隐藏这些奖励,有 nextCursor 时继续翻页 |
| pending_delivery,items=[] | 本页无可发奖励,但玩家其他位置仍有可发奖励;有 nextCursor 时继续翻页,否则从第一页复查 |
| settling,items=[] | 本页没有可发奖励,但仍有未完成结算的记录;有 nextCursor 时继续翻页寻找已准备好的奖励,扫描结束后稍后从第一页重查 |
| no_pending_rewards,items=[] | 后端已按玩家范围检查,当前环境下所有已结束期均无待发或待结算记录;nextCursor=null,结束本轮检查 |
没有历史记录,或所有已结束期均已完成结算且无未确认达标奖励时,返回以下状态。这个判断不受 afterId 限制;未付费但尚未完成后台结算的记录仍可能使状态为 settling:
```json
{"code":1,"data":{"status":"no_pending_rewards","items":[],"nextCursor":null},"msg":"成功"}
```
正在结算的例子:
```json
{"code":1,"data":{"status":"settling","items":[],"nextCursor":null},"msg":"成功"}
```
| items[] 字段 | 类型 | 含义 |
| --- | --- | --- |
| settlementId | string | 本期结算单 ID,确认时原样回传 |
| periodId | string | 奖励所属的已结束活动期 |
| rewards | array | 本期尚未确认发货的奖励,按任务顺序返回 |
| rewards[].taskId | string | 档位标识 |
| rewards[].grantId | string | 稳定发奖标识;到期前已申请过的奖励继续使用原标识 |
| rewards[].items | array | 授权道具列表,元素 type:string、count:number,当前 type=coin |
**清单表示服务端尚未确认发货,不保证客户端从未加过金币。** 前端按 grantId 查本地记录:已保存金币的只补确认,未发放的才加金币并保存。不要再对清单调用 claim,也不要根据当前 info 配置计算旧期金币。
查询是当时的快照;与另一端确认并发时,返回的奖励可能已经被确认。客户端仍需通过稳定 grantId 与现有资源交付机制去重。新支付确认可能在本次查询之后产生补发,回到游戏、支付恢复后应从第一页重新查询。
### 6.2 确认补发奖励已保存:confirm_settlement_delivery
**POST `/goldMiner/index`**。
```json
{"action":"confirm_settlement_delivery","uid":"USER_ID","token":"LOGIN_TOKEN","settlementId":"SETTLEMENT_ID_FROM_SERVER","grantIds":["GRANT_ID_1","GRANT_ID_2"]}
```
| 参数 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| action | string | 是 | confirm_settlement_delivery |
| uid / token | string | 是 | 公共鉴权参数 |
| settlementId | string | 是 | 查询返回的结算单 ID,必须属于当前玩家 |
| grantIds | string[] | 是 | 本次已保存成功的授权 ID;1~100 个,不允许重复,全部属于同一结算单 |
可一次确认一项,也可在全部保存成功后批量确认多项。可选择任意已授权任务,允许跳过前一档或非连续批量确认。批量请求校验和状态更新在同一玩家期记录中原子完成;任一授权不合法时,本次不会只更新前半部分。
```json
{"code":1,"data":{"settlementId":"SETTLEMENT_ID_FROM_SERVER","confirmedGrantIds":["GRANT_ID_1","GRANT_ID_2"],"deliveryStatus":"client_saved"},"msg":"成功"}
```
| data 字段 | 类型 | 含义 |
| --- | --- | --- |
| settlementId | string | 已处理的结算单 |
| confirmedGrantIds | string[] | 本次确认的授权 ID,含安全重试中已经确认的 ID |
| deliveryStatus | string | partial:该结算单仍有未确认项;client_saved:整单全部确认 |
此接口只记录客户端已保存成功,不增加金币。同一授权重复确认安全,金币保存成功但接口超时只能重试确认;不能再次加金币。与 confirm_delivery 共用原任务发货状态,因此旧的在途确认和新的补发确认不会生成两份奖励。
确认成功后重新查询,已确认项自动消失;整单为空时整单不再返回。当前金币整值保存协议的断线、多设备限制仍见第 5.4 节。
### 6.3 兼容旧展示确认:ack_settlement(新流程可不调用)
**POST `/goldMiner/index`**。
```json
{"action":"ack_settlement","uid":"USER_ID","token":"LOGIN_TOKEN","settlementId":"SETTLEMENT_ID_FROM_SERVER"}
```
所有参数必填且为 string。只有整单发货确认完成后可调用;成功 data 为 `settlementId:string, acknowledged:true`。重复调用安全,未完成时返回 DELIVERY_PENDING。它只保留旧版展示确认功能,不是新发货确认接口,也不决定奖励是否从查询中消失。
### 6.4 推荐执行顺序
1. 从第一页查询 settlements,不传 limit。no_pending_rewards 表示本轮无需补发,可以结束。
2. 按每期 rewards 顺序,使用 grantId 恢复已有交付记录;未发的交给金币模块发放并保存。
3. 对确定保存成功的奖励调用 confirm_settlement_delivery;确认失败复用原 settlementId/grantIds。
4. 不论 pending_delivery 还是 settling,有 nextCursor 就继续翻页,避免某期未结算阻塞后续期。扫描结束后从第一页复查;如果仍在 settling,稍后再查,不要立即无限轮询。直到 no_pending_rewards 才确认当前无待处理奖励。没有奖励时不展示补发动画。
## 7. 错误码与处理
以下错误码可能来自活动或黄金矿工支付分支;通用接口前置错误及 checkIos 的返回例外见第 1 节。
| errorCode | 含义与前端处理 |
| --- | --- |
| UNAUTHORIZED | 登录或订单归属校验失败;刷新登录并核对账号,不跨账号重试订单 |
| INVALID_ACTION / INVALID_INPUT / INVALID_PERIOD | action、参数或期 ID 不合法;修正请求 |
| INVALID_TASK / INVALID_GRANT | 档位或授权不匹配;重新读取任务/原领取结果,不伪造 ID |
| NOT_FOUND | 活动记录、订单快照或结算不存在;核对账号、期和 ID |
| NOT_QUALIFIED | 未满足活动门槛;刷新 info |
| PERIOD_ENDED | 活动不在可操作时间或事件属于旧期;停止新购买/进度补记,刷新 info |
| PERIOD_SETTLEMENT_REQUIRED | 普通 claim 已到期,提示服务器结算,改查 settlements,不重试普通 claim |
| SETTLEMENT_NOT_READY | 补发确认对应期尚未结束或结算未准备好;重新查询 settlements |
| PURCHASE_DISABLED | 本期暂停新购买;刷新 info |
| ALREADY_UNLOCKED | 已有权益或订单已确认;继续原订单查询并刷新 info,不再次付款 |
| NOT_PAID | 本期未解锁;恢复支付查询或展示购买 |
| TARGET_NOT_REACHED | 任务目标未达成;刷新进度 |
| DELIVERY_PENDING | 补发尚未全部确认;继续逐档处理 |
| EVENT_PAYLOAD_CONFLICT | 同一个事件 ID 对应不同胜利内容;检查持久化队列,不能改 ID 来绕过校验 |
| INVALID_PRODUCT | 商品与本期配置不符;使用最新 info.productId |
| PAYMENT_PENDING | 尚无可信支付确认;保留原订单并退避查询,不等同确定支付失败 |
| PAYMENT_CHANNEL_DISABLED | 客服支付渠道未启用;提示暂不可用,交后端确认渠道配置 |
| PAYMENT_CHANNEL_CONFLICT | 本期已有其他渠道订单;继续原渠道 |
| ORDER_CLOSED | 订单已关闭或无法继续支付;停止拉起该支付页并刷新状态 |
| ORDER_ENVIRONMENT_MISMATCH | 订单/商户环境不一致;停止重试并核对部署环境 |
| ORDER_SNAPSHOT_CONFLICT / PAYMENT_CONFLICT | 订单快照或交易号冲突;保留订单号交后端排查 |
| ENTITLEMENT_CONFLICT / MANUAL_REVIEW | 资格分配冲突或进入人工核对;不要重付,交后端核对 |
| CONFIG_UNAVAILABLE / PAYMENT_CONFIG_UNAVAILABLE | 活动或支付配置缺失/异常;提示暂不可用,交后端处理 |
| CUSTOMER_SERVICE_UNAVAILABLE | 客服消息发送失败;保留订单,由原流程恢复 |
| INVALID_PAYMENT / INVALID_PAYMENT_SIGNATURE | 支付响应或签名校验失败;不能按成功处理,交后端排查 |
| MERCHANT_* | 商户请求相关错误,后缀来自商户响应;保留原订单,查询类暂时故障退避恢复 |
| RETRYABLE | 暂时失败或并发冲突;复用原请求标识退避重试,并遵守金币/关卡恢复约束 |
服务端订单元数据内的 lastErrorCode/lastQueryError 可能保留历史错误,不等同本次接口 errorCode。未知错误码按失败处理并保留日志,不能默认支付或领奖成功。
## 8. 前端接入顺序与版本迁移
1. 登录/回到游戏后恢复未完成订单查询及奖励交付记录,拉取 info 和 settlements。
2. 游戏胜利保存关卡时附带活动事件;未打开活动页、未付费仍正常上报。结束后停止新事件补记。
3. 页面直接按 tasks 返回顺序展示,以 status 控制购买,以 claimStatus 控制档位领取;不根据 completed 单独开放按钮。
4. 购买后查询到权益确认,重新读取 info;按 fulfillmentRoute 处理往期结算或下期资格。
5. 活动内金币保存后 confirm_delivery;到期后从 settlements 取得原 grantId,保存金币后 confirm_settlement_delivery。
本版精简仅作用于 **info 的展示响应**:
| 旧字段 | 当前用法 |
| --- | --- |
| availability / qualified / purchasable / entitlement.status | 使用 status 五态 |
| entitlement.source | 删除,前端只判断本期是否 unlocked |
| nextStartsAt / nextPeriodId / currency | info 不返回;金额单位在协议中约定 |
| tasks[].sequence | 删除,使用数组顺序 |
| tasks[].claimed / issuing / claimable / blockReason | 删除,使用 claimStatus 五态 |
| info.claimedThrough | 删除,按任务 claimStatus 展示;claim 和 confirm_delivery 仍返回 claimedThrough |
不要将 info 的 claimable 状态当作后端永久承诺;点击后的 claim 才是实际领取判定。页面倒计时归零、切回前台、支付完成及奖励确认后均应刷新状态。
联调文件:[Postman 使用说明](postman/gold-miner.README.md)、[Collection](postman/gold-miner.postman_collection.json)、[环境模板](postman/gold-miner.postman_environment.json)。内部维护与部署细节见 [后端说明](README.md) 和 [客服支付接入说明](LEGACY-PAYMENT.md)。