server/laf-cloud/functions/goldMiner/LEGACY-PAYMENT.md

14 KiB
Raw Blame History

黄金矿工旧 iOS 客服支付接入

本分支基于现有 wx/KeFuInfo、wx/checkIos 和提供的 order3.html 扩展。未改动游戏前端仓库,未部署或调用真实支付。下载目录中的原文件未修改,可部署副本为 web/order3.html。

客户端和客服入口

  1. 游戏客户端先以登录态请求 POST /goldMiner/index:
{
  "action": "create_order",
  "channel": "legacy_ios",
  "uid": "玩家ID",
  "token": "登录token",
  "itemid": "当期配置的gold_miner商品ID",
  "createRequestId": "本次购买请求ID"
}

成功外壳为 {code:1,data,msg},data 包含 outTradeNo, periodId, priceFen, productId, quantity:1, channel, expiresAt, sessionFrom。价格、用户、期和数量均来自服务端。每次调用都生成新订单(createRequestId 相同也如此);未解锁时允许重新下单及切换原生/客服渠道。过期或已关闭订单不能继续付款。

sessionFrom 包含旧客服渠道需要的 count: 1 和 price(单价,单位分),由后端按当前订单价格填写。前端原样传递该字符串即可,不需要自行拼接价格。wx/KeFuInfo 解析并规范这两个字段后交给黄金矿工发送函数,核对与订单一致,再使用 count * price / 100 生成客服卡片金额,支付页参数使用同一份 count(映射为 quantity)和 price。旧请求缺少字段时从订单补齐;无效金额返回 INVALID_INPUT,与订单不一致返回 ORDER_SNAPSHOT_CONFLICT,不生成支付链接。升级需同时发布 wx/KeFuInfo 和 goldMiner/legacyPayment。

  1. 保存返回的 outTradeNo,将 data.sessionFrom 原样传入微信 openCustomerServiceConversation 的 sessionFrom。不能继续沿用旧客户端自己生成订单号、拼接价格的黄金矿工路径。expiresAt 为本期截止时间。sessionFrom 中 goldMinerTicket 仅为兼容原入口的渠道标识,不再是签名票据;活动结束后不能重新购买该期。
  2. 现有客服网关将 FromUserName、SessionFrom 转发到 wx/KeFuInfo。黄金矿工按订单记录核对归属 openid、商品和环境,不再校验客服票据签名;支付链接仍只发给订单本人。普通商品保留现有流程。 当前通用 PostImg 生成黄金矿工链接后,将本次秒级 time 写入主订单 iosTime,并保存完整活动订单兼容副本。商品展示名与真实商品 ID 分开处理,兼容副本沿用主订单中的商品 ID 和活动快照。
  3. 新支付链接的 time 是生成链接时的秒级时间字符串,与该次签名 body.timeStamp 相同;页面原样提交 wx/checkIos,匹配对应订单 iosTime。订单 time 仍为毫秒创建时间。旧订单没有 iosTime 时,仅兼容原先的毫秒创建时间链接;不能把秒级 time 乘 1000 替代。也支持游戏内以 uid/token/outTradeNo 调用该接口,返回严格布尔值 true/false。
  4. checkIos 只校验和关联订单。order 在预下单时就是主记录,iosOrder 保留完整兼容副本;主记录存在时不覆盖支付状态,不丢失 goldMiner 快照。主记录缺失时只从已验证的服务端副本恢复。同一操作可重试,付款早于转单也不会回退订单。
  5. 支付成功后,用已有 wx/getPayInfo、wx/iosgetPayInfo 或 wx/getOrderReward 携带 uid/token/outTradeNo 查询。这些接口可触发商户主动查单。返回 rewardDelivery=goldMiner.claim 时进入活动领奖协议;禁止套用普通金币商品的通用补发。

客户端预下单调用是必需接入点,本提交提供后端和页面,未修改 MatchMaster 游戏代码。现有客服网关需原样转发 SessionFrom;仍需微信实际会话联调确认网关没有截断或改写参数。

支付确认与恢复

  • /goldMiner/merchantNotify 仅接收通知并返回 SUCCESS,不解密、不查询数据库、不确认付款。通知正文中的交易状态、订单号、金额均不作为付款依据。新旧订单均由游戏查单接口、登录恢复或每分钟任务主动查询微信结果。
  • 后端用现有商户私钥签名请求微信 HTTPS 查单接口,不再额外验证微信响应签名;仍核对查单返回的 appid、mchid、openid、订单号、CNY 金额、JSAPI 类型和支付时间,只有 SUCCESS 才持久化 confirmedAt/transactionId/paidAt 并解锁。禁止从客户端或通知正文直接写入付款成功。HTTP 请求不跟随重定向。
  • 历史 confirmationSource=merchant_notify 保留,新确认统一为 merchant_query。支付成功后前端需要继续调用支付查询接口;通知接口返回 SUCCESS 不代表活动已解锁。网络故障、租约或退避期间可能需要再次查询。
  • 微信通知地址不能携带查询参数,配置 GOLD_MINER_MERCHANT_NOTIFY_URL 时使用不带查询串的 HTTPS 地址。依据:微信支付回调通知注意事项。
  • 登录最多查 2 笔待处理订单,任务每轮最多查 5 笔;30 秒订单租约避免重复并发请求,失败退避从 10 秒到最多 5 分钟。网络超时和查单信息不匹配时保留订单。接口的 PAYMENT_PENDING 不能被当作金币补发授权。
  • 未支付旧期订单经可信查单后关闭;关单与付款竞态会重新查单,成功付款走原活动履约。支付确认延迟时,有达标奖励补发原期,无奖励顺延紧接下一期;重复回调和查询不会重复发资格。冲突保留 manual_review,不自动顺延第三期。
  • goldMiner.paymentChannel 标识 legacy_ios/native,历史缺失该字段的活动订单按原生处理。新增商户快照、预支付状态、租约、重试和支付确认字段均放在原有 goldMiner 对象内,无新增通关事件集合。主订单保留完整原有活动快照。

环境配置和发布

不再读取 GOLD_MINER_WECHATPAY_PUBLIC_KEYS、GOLD_MINER_WECHATPAY_API_V3_KEY 和 GOLD_MINER_ORDER_TICKET_SECRET,三项均可不配置。微信要求的商户请求签名、支付页 paySign 仍沿用现有商户私钥生成。

环境变量 内容
PAYMENT_APP_ENV test 或 production,与所在后端和订单一致
GOLD_MINER_LEGACY_ENABLED 仅字符串 true 开放旧渠道预下单和发支付链接,默认关闭;关闭后仍处理已付款订单
WX_MCH_ID 微信支付商户号
WX_MINIGAME_APP_ID 支付 appid,必须与玩家 openid 和商户绑定关系相符
WX_MCH_CERT_SERIAL_NO 商户 API 证书序列号,用于请求签名
WX_PAY_PRIVATE_KEY_BUCKET 私有存储桶,内含 apiclient_key.pem,沿用现有商户签名配置
GOLD_MINER_MERCHANT_NOTIFY_URL 可选覆盖;默认使用支付应用域名加 /goldMiner/merchantNotify
GOLD_MINER_PAY_PAGE_URL 可选覆盖;默认沿用 https://pay.nika4games.com/order3.html
GOLD_MINER_CHECK_IOS_URL 可选覆盖;默认使用支付应用域名加 /wx/checkIos

支付应用域名默认复用已有配置:正式服读取 WX_PAY_NOTIFY_URL 的域名,未配置则沿用旧支付页中的正式服域名 https://q6rvwvtnga.sealoshzh.site;测试服读取 TEST_WX_PAY_NOTIFY_URL 的域名,不能自动退回正式服。这里复用的是域名,商户通知路径仍为 /goldMiner/merchantNotify,不把加密商户通知发送到原生 payCallBack。三个 GOLD_MINER_*_URL 均可省略或留空;显式填写时仍须为正确的 HTTPS 地址。测试服若既无测试回调地址,又未覆盖通知/转单地址,会明确提示缺少 TEST_WX_PAY_NOTIFY_URL。

这些默认值在创建订单时写入 merchantSnapshot,后续修改配置不改变已有订单;调整地址后应重新创建订单进行联调。默认支付页地址仍需部署仓库中的新版 order3.html。

依赖已有 cloud.shared 中的 wxaccess_token 和微信有效客服会话;失效时接口返回 CUSTOMER_SERVICE_UNAVAILABLE,有效 prepay 会话下可重发同一链接,不再次向微信下单。预下单超时或会话过期时会先查单恢复付款结果;未付款则返回 ORDER_RECREATE_REQUIRED,游戏重新调用 create_order,使用新订单及 sessionFrom。已关闭订单返回 ORDER_CLOSED,也应重新下单。环境密钥不得放进客户端、Postman 或 Git。

先按 总部署清单 发布依赖和入口,再运行 setup_indexes(包含商户流水唯一索引和查单索引)、绑定每分钟任务、部署支付页,最后启用开关。除 goldMiner 目录外,本次需更新:

  • login.ts
  • wx/KeFuInfo.ts、wx/checkIos.ts
  • wx/getPayInfo.ts、wx/iosgetPayInfo.ts、wx/getOrderReward.ts

如果测试服尚未部署上一版黄金矿工,还必须同步总部署清单中的 userLevel、原生下单、原生回调等文件。现有 YAML 方法未变;专属通知新增 YAML 必须一起发布。

支付页优先使用后端写入链接的 checkUrl,普通旧链接未提供时保留原生产地址。必须部署本仓库副本后才可用测试服链接;Downloads 原页硬编码生产地址。支付页只接受转单接口明确返回 true,并等待 WeixinJSBridge 就绪。小游戏支付参数继续由后端签名。

支付页“该订单已过期”目前代表 checkIos 返回非 true,不一定实际超时。排查时先核对页面实际请求的 checkUrl 与订单所在环境,再查看 [GoldMiner checkIos] rejected 或 [checkIos] order not found 日志。日志区分订单缺失、环境不匹配、身份/时间参数不匹配和订单关闭;新黄金矿工链接 time 必须等于订单 iosTime(秒),不能替换为订单创建时间;没有 iosTime 的历史订单才按原毫秒创建时间校验。checkIos 仍返回布尔值,保持旧页面兼容;日志不记录账号、token 或支付签名。

排查新卡片仍携带旧参数时,按同一订单号核对 [KeFuInfo] payment route、[GoldMiner customer link] sending、[GoldMiner customer link] sent 三条日志;sending 记录真实发送参数中的 linkTime、orderTime、signatureTime 和必需字段是否存在。普通发送路径另记 [KeFuInfo PostImg] sending payment link。日志不包含完整链接、openid、access_token、nonceStr 或 paySign。若本次发送没有对应入口日志,应继续核对客服网关实际调用的环境和函数;若发送记录正确而卡片参数不同,应检查是否读取了其他卡片或网关缓存。

PAYMENT_APP_ENV=test 只隔离应用和订单,不是微信商户免扣款沙箱;新商户请求仍到微信正式 API。上线前需要实际微信账号验证 appid/openid、客服转发、支付目录/域名、CORS、商户证书和通知地址配置,仓库测试无法替代这些部署条件。测试及正式订单的通知地址各自保存在订单快照,不走原生测试回调转发。

验证

云函数沙箱不要求提供全局 AbortSignal。商户请求和客服消息发送均设置 5 秒等待上限,覆盖响应正文读取;超时不代表微信已经取消请求。预下单超时后保留 creating 状态,后续先查单,未付款时要求创建新订单,不复用旧订单号再次向微信下单。客服消息发送超时保留已有预支付信息,重试可能再次发送同一支付链接。

CUSTOMER_SERVICE_UNAVAILABLE 的 msg 区分当前环境 wxaccess_token 缓存缺失、网络请求失败、响应非 JSON、微信拒绝发送。微信拒绝发送时返回 HTTP 状态及数字 errcode,并在服务端记录订单号和这些状态;不透传原始响应、access_token 或支付签名。若仅客服发送失败且预支付信息仍有效,可重新触发同一订单的客服发送,不会再次向微信预下单;若随后返回 ORDER_RECREATE_REQUIRED 则创建新订单。此诊断更新只需发布 goldMiner/legacyPayment。

修复 AbortSignal is not defined 时需发布 goldMiner/merchant 和 goldMiner/legacyPayment。旧订单已进入 creating 状态的,查单确认后可能返回 ORDER_RECREATE_REQUIRED;此时重新调用 create_order,使用新返回的订单号及 sessionFrom。无需新增环境变量或修改前端价格参数。

node --test laf-cloud/functions/goldMiner/tests/*.test.mjs
node --test --test-name-pattern='gold miner' laf-cloud/tests/login-wucai-state.test.mjs

本地使用模拟数据库与微信 HTTPS 查单结果,未真实支付或发送客服消息。覆盖三项配置缺失、客服与转单归属、价格、重复下单、伪造通知不能解锁、历史订单主动查单恢复、查询租约、期末关单竞态、跨期履约、支付页及 Postman 链路。

参考协议:微信 JSAPI 下单、微信支付通知、Laf 请求上下文。

本次升级需发布 goldMiner/merchant、goldMiner/legacyPayment、goldMiner/merchantNotify、goldMiner/config 及对应 YAML,入口依赖按部署平台规则更新。保留每分钟 goldMiner/jobs。现有前端仍原样传递 sessionFrom 和支付链接参数,无需新增密钥或字段。升级前已发送的签名 time 链接需重新获取新订单的支付链接;已付款旧订单的查询、恢复及活动权益不受影响。普通商品和原生支付签名流程不变。

本次 iosTime 修复发布顺序:先发布 goldMiner/legacyPayment,再发布 wx/KeFuInfo 和 wx/checkIos。无需新建集合、索引或环境变量,不需要改支付页请求格式。生成另一笔订单不影响当前订单的时间校验;重发同一订单时 iosTime 更新,请使用最新链接。旧通用链接若此前没有保存订单 iosTime,需重新获取链接。保存 iosTime 不修改主订单 time、支付状态或履约状态。