# 虚拟支付接入修复 + 部署验收清单 ## 一、本次修复的两个问题 ### 问题 1:后台「存储空间管理」提示「请先登录」 - **根因**:新增的 `admin_storage_users`、`admin_storage_user_update` 两个接口未加入后台鉴权白名单,被当成普通用户接口校验,所以返回「请先登录」。 - **修复**:已加入 `adminActions` 白名单,现在走后台 Cookie 会话鉴权,可正常拉取用户列表。 ### 问题 2:前端存储支付页显示「支付失败,请稍后再试」 - **根因 A(后端)**:订单号 `out_trade_no` 为 `"SP" + 32 位随机串 = 34 位`,**超过微信要求的 8–32 位**,被微信拒绝。 - 修复:改为 `"S" + 31 位随机串 = 32 位`。 - **根因 B(前端)**:`wx.requestVirtualPayment` 误把 `offerId / env / productId / goodsPrice / buyQuantity / currencyType` 作为**顶层参数**传入。这些字段只能存在于 `signData` JSON 字符串内部,顶层仅允许 `signData / paySig / signature / mode` 四个参数。 - 修复:`pages/storage/storage.js` 已改为只传这四个参数。 ### 额外增强:兜底查单(query_order) - 新增服务端主动查单逻辑:服务启动后定时(默认 5 分钟)扫描 24 小时内未发货订单,调用微信 `query_order` 接口,已支付但推送丢失的订单会**自动补发货开通会员**,已关闭订单自动标记 `closed`。 - 新增 `internal/virtualpay` 的 `QueryOrder` 方法(签名:`pay_sig = HMAC-SHA256(AppKey, "/xpay/query_order&" + body)`)。 --- ## 二、部署步骤 1. **后端**:用 `dist/past-windows-deploy.zip` 替换服务器上的部署目录并重启。 2. **小程序前端**:`weapp/pages/storage/storage.js` 已改动,请在微信开发者工具重新上传并提交审核发布。 3. **配置**:按下面清单逐项配置虚拟支付。 --- ## 三、部署前检查清单(逐项验收) > 对照微信个人主体虚拟支付接入文档,逐项验收。 | # | 检查项 | 本项目验收方法 / 操作 | |---|--------|------------------------| | 1 | 开放条件:个人主体 + 大陆身份证 + 服务类目「工具」+ 已完成认证与备案 | 微信公众平台(MP)后台确认 | | 2 | 全终端月支付限额 10 万元,已了解并告知用户 | 确认知晓,可在会员页注明 | | 3 | MP 后台「虚拟支付」能力已开通 | MP 后台 → 功能 → 虚拟支付,确认状态为已开通 | | 4 | 已拿到 AppID / OfferID / 现网 AppKey | 见下方「配置项 A」 | | 5 | 需 iOS 支付时已配置小程序简称 | 仅 iOS 需要,Android 可跳过 | | 6 | 消息推送接收已部署 | 服务已提供回调地址(见「配置项 B」) | | 7 | MP 后台已配置发货推送 URL,并完成一笔测试支付 | 见下方「配置项 B」+「沙箱测试」 | | 8 | 服务器验签逻辑通过 | 已实现 `sha1(sort(token,timestamp,nonce))` 明文验签,`token` 需与 MP 后台一致 | | 9 | 发货推送已做幂等 | 已实现:按 `out_trade_no` + `status='success'` 去重,重复推送不会重复开通 | | 10 | 前端 `wx.requestVirtualPayment` 打通 | 本次已修复参数,见「问题 2」 | | 11 | 兜底查单逻辑就绪(query_order) | 本次已新增,服务启动后自动运行 | | 12 | 退款规则 / 结算周期与费率已说明 | 虚拟支付结算周期 T+1,费率见 MP 后台说明,需在会员页告知 | | 13 | 上线后小额真单验证 | 正式上线后做一笔真实小额支付,确认到账与发货 | --- ## 配置项 A:支付通道配置(后台「存储空间」→「存储收费设置」) | 字段 | 说明 | 沙箱(测试) | 正式(上线) | |------|------|-------------|-------------| | `pay_env` | 支付环境 | `1` | **`0`** | | `pay_offer_id` | 支付应用 ID | 沙箱 OfferID | 正式 OfferID | | `pay_app_key` | 正式环境 AppKey | 留空 | **正式 AppKey** | | `pay_sandbox_key` | 沙箱 AppKey | 沙箱 AppKey | 可留空 | | `month_product_id` | 包月道具 ID | 沙箱道具 ID | 正式道具 ID | | `year_product_id` | 包年道具 ID | 沙箱道具 ID | 正式道具 ID | | `month_price_cents` | 包月价格(分) | 与道具价格一致 | 与道具价格一致 | | `year_price_cents` | 包年价格(分) | 与道具价格一致 | 与道具价格一致 | | `pay_mode` | 支付模式 | `short_series_goods` | `short_series_goods` | > **重要**:`month_product_id / year_product_id` 必须在微信「虚拟支付 → 道具管理」里创建,且**道具价格(分)必须与 `month_price_cents / year_price_cents` 完全一致**,否则支付时价格校验失败。 > `config.yaml` 里 `virtual_pay.env` 默认是 `1`(沙箱)。**正式上线前务必改成 `0`**,或在后台把 `pay_env` 改为 `0`(后台优先)。 --- ## 配置项 B:发货推送回调(微信后台「消息推送」) - **URL**:`https://你的域名/api/storage_pay_notify` - **Token**:与 `config.yaml` 的 `virtual_pay.notify_token` 保持一致 - **消息加密方式**:选择 **明文模式**(当前代码按明文模式验签;若选安全模式需另行实现 AES 解密) 配置 URL 时微信会发起 GET 验证,服务端已实现 `echostr` 回显 + 签名校验,验证通过即可。 --- ## 沙箱测试流程(上线前必做) 1. 后台将 `pay_env` 设为 `1`,填入沙箱 OfferID / 沙箱 AppKey / 沙箱道具 ID。 2. 用测试微信号打开小程序,进入「我的 → 存储空间」发起支付。 3. 确认能拉起 `wx.requestVirtualPayment`,完成沙箱支付。 4. 观察:支付成功后会员自动到账;若推送延迟,5 分钟内兜底查单会自动补发货。 5. 测试通过后,把 `pay_env` 改为 `0`、换成正式 OfferID / AppKey / 道具 ID,再提交审核发布。