# 秒过小程序虚拟支付接入 本模块与原有 `src/api/pay` 并行,旧支付接口保持不变。新模块继续使用 `ProductPayInfo`、`MiaoguoWXUsers` 和 `MiaoguoCoupon`,订单状态、金额单位及秒过 `PayType=7/9` 的业务含义与旧代码兼容。 ## 环境配置 服务端启动前配置以下环境变量: ```bash WX_VIRTUAL_PAY_OFFER_ID=微信虚拟支付OfferId WX_VIRTUAL_PAY_APP_KEY=正式环境AppKey WX_VIRTUAL_PAY_SANDBOX_APP_KEY=沙箱环境AppKey WX_VIRTUAL_PAY_ENV=1 WX_MIAOGUO_MESSAGE_TOKEN=秒过小程序消息推送Token WX_VIRTUAL_PAY_PRODUCTS_JSON='[ {"productId":"后台正式商品ID","payType":7,"price":19900,"goodsPrice":36500,"env":0}, {"productId":"后台正式试用商品ID","payType":9,"price":100,"env":0}, {"productId":"后台沙箱商品ID","payType":7,"price":19900,"goodsPrice":36500,"env":1}, {"productId":"后台沙箱试用商品ID","payType":9,"price":100,"env":1} ]' ``` - `price` 是用户实际支付金额,单位为分;`goodsPrice` 是小程序后台已发布商品原价。两者不同时,服务端会把 `price` 作为 `activitySellingPrice` 签名。 - 省略 `goodsPrice` 时默认与 `price` 相同。 - `WX_VIRTUAL_PAY_PRODUCTS_JSON` 可以省略。没有匹配配置时,服务端按“内部产品编号_实际金额(元)”生成微信道具 ID。秒过当前对应关系为:1 元使用 `166_1`、199 元使用 `166_199`、258 元使用 `166_258`。以后其他小程序也采用相同规则,例如内部产品编号 205 的 199 元道具为 `205_199`。 - 秒过服务端会校验业务金额:试用 `PayType=9` 只允许 1 元,购买 `PayType=7` 通常只允许 199 元或 258 元。服务端通过 OpenID 查到的真实 `UserID < 8` 或 `UserID=3089` 可用 1 元测试购买;不能依赖客户端自行传入的 UserID,以免普通用户篡改金额后低价开通权益。 - 如果微信后台商品原价与实际支付价不同,需要在配置表中加入对应售价,服务端才能正确传入 `activitySellingPrice`。 - `WX_VIRTUAL_PAY_ENV=1` 为沙箱,`0` 为正式环境。iOS 没有沙箱,服务端会强制使用正式环境商品和正式 AppKey。 - AppKey 只放在服务端环境变量中,不能写入小程序代码或接口响应。 ## 新接口 | 接口 | 用途 | | --- | --- | | `GET /api/MiaoguoVirtualPayLogin500` | 换取登录态、创建兼容订单并返回虚拟支付签名 | | `GET /api/MiaoguoVirtualPayOrderStatus500` | 主动查询微信订单并在漏回调时补发货 | | `POST /api/MiaoguoVirtualPayNotify500` | 接收 `xpay_goods_deliver_notify` 并发放权益 | 微信公众平台的秒过小程序消息推送地址应指向: ```text https://www.kylx365.com/api/MiaoguoVirtualPayNotify500 ``` 当前代码按明文 XML/JSON 消息接收;Token 必须与 `WX_MIAOGUO_MESSAGE_TOKEN` 一致。 同一路径的 GET 请求已实现公众平台首次保存地址时的签名校验与 `echostr` 回显。 若公众平台启用了安全模式(AES 加密消息),需先切换为明文模式,或后续增加消息解密。 ## 客户端与业务处理 - 小程序新增 `main.virtualPayMoney`,原 `main.payMoney` 保留。 - Android、鸿蒙和 Windows 在测试环境使用沙箱;iOS 自动使用 Apple 支付正式环境。 - iOS 要求 iOS 15 及以上、微信 8.0.68 及以上,单笔最低 1 元,并受中国大陆 App Store 账号条件限制。 - 微信重复推送不会重复增加会员有效期:订单和用户更新在同一数据库事务中完成,订单行加锁并以 `Status=1` 判定幂等。 - 支付消息漏推时,小程序会轮询查单;查到状态为已支付后,服务端发放权益并调用 `/xpay/notify_provide_goods`。 本次没有实现退款、iOS 退款询问事件和投诉处理。 ## 本地测试支付完成逻辑 支付完成后的核心逻辑由发货通知和主动查单共同调用 `fulfillOrder`。该函数支持在测试中注入假数据库,因此以下命令不会连接或修改真实数据库,也不会调用微信接口: ```bash npm run test:virtual-pay:completion ``` 测试覆盖:正常购买完成、测试用户 1 元购买、1 元试用、重复发货幂等、金额不一致回滚,以及模拟微信 `xpay_goods_deliver_notify` 发货通知。完整虚拟支付测试可运行: ```bash npm run test:virtual-pay ``` 只有最终的微信客户端拉起支付、微信真实扣款和公网消息推送需要做少量线上联调;会员更新、优惠券消耗、订单完成与异常回滚都可先在本地自动测试。