# 黄金矿工前端接口文档 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)。