# 小程序接口文档:在线支付(旺铺网关 JSAPI) > 门店端在线支付:选择本店未支付账单合并付款,后端经旺铺支付网关(统一下单B-JSAPI)下单, > 小程序调起 `wx.requestPayment` 完成支付;支付结果由**网关后台通知**自动结账, > 小程序端也可**主动查询**同步结果(回调延迟/丢失时的兜底)。 > > 结账效果与线下凭证审核一致:关联账单批量置「已支付」、累加门店总采购金额、门店收到支付成功通知。 ## 整体流程 ``` 小程序 后端 旺铺网关 微信 │ wx.login → code │ │ │ │ POST /mini/payment/online (bill_ids, code) │ │ │────────────────────▶│ code2session 换 openid │──────────────────▶│ │ │ 创建支付单+锁定账单 │ │ │ │ 统一下单(mer_order_id=支付单号) ──────────▶│ │ 返回 pay_params │◀──────── order_id + 调起参数 ────────────│ │◀────────────────────│ │ │ │ wx.requestPayment(pay_params) ─────────────────────────────────▶│ │ │ POST /mini/payment/notify(支付成功通知) │ │ │◀───────────────────────│ │ │ │ 验签 → 幂等结账 → 应答 {"code":"00"} │ │ GET .../query 主动同步(兜底) │ │ │────────────────────▶│ 交易查询 → 已支付则结账 │ │ ``` ## 通用约定 | 项 | 值 | |---|---| | 鉴权 | 门店 token:`Authorization: Bearer `(登录见 `/mini/auth/login`) | | 响应格式 | `{ "success": true|false, "data": {...}, "msg": "..." }` | | 金额单位 | 元,字符串/数字两位小数(如 `150.50`) | --- ## 1. 发起在线支付(合并账单下单) | 项 | 值 | |---|---| | 请求方式 | `POST` | | 路径 | `/mini/payment/online` | | 鉴权 | 门店 token | ### 请求参数 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `bill_ids` | int[] | 是 | 要合并付款的账单 ID 数组(`GET /mini/bill?payable=1` 返回的 `id`),至少 1 个 | | `code` | string | 是 | 小程序 `wx.login()` 返回的登录凭证(后端用它换付款人 openid) | | `remark` | string | 否 | 付款备注,最长 255 字 | ### 校验规则(失败均返回 `success:false`) - 账单必须全部属于当前门店,且为「未支付」且未被其他支付单锁定; - 任一账单已支付 / 正在支付中(含凭证审核中)→ 拒绝并提示对应账单号; - 合计金额必须大于 0; - 微信 `code` 无效(code2session 失败)→ 报错,不产生支付单; - 网关下单失败 → 支付单自动作废、账单释放,可重新发起。 ### 响应示例 ```json { "success": true, "msg": "下单成功,请调起支付", "data": { "id": 12, "payment_no": "ZF202608270001", "amount": "150.50", "pay_params": { "order_id": "202608271201444525348059", "tradeNo": "2021082722001407831438768160", "user_openid": "oXxx123" } } } ``` ### 字段说明 | 字段 | 说明 | |---|---| | `payment_no` | 本系统支付单号(即上送网关的商户订单号 `mer_order_id`),后续查询/对账用 | | `amount` | 应付金额(= 所选账单总额合计,元) | | `pay_params` | 旺铺网关 `data` 节点原样透传。**调起支付所需参数以网关返回为准**(服务商配置后通常包含 `timeStamp`/`nonceStr`/`package`/`signType`/`paySign` 等),直接透传给 `wx.requestPayment` 即可 | ### 小程序端调用示例 ```js // 1. 获取登录凭证 const { code } = await wx.login(); // 2. 后端下单 const res = await request.post('/mini/payment/online', { bill_ids: [101, 102], code }); const { payment_no, pay_params } = res.data; // 3. 调起微信支付(pay_params 以旺铺网关实际返回的调起参数为准) await wx.requestPayment({ timeStamp: pay_params.timeStamp, nonceStr: pay_params.nonceStr, package: pay_params.package, signType: pay_params.signType || 'RSA', paySign: pay_params.paySign, }); // 4. 支付完成后主动同步结果(回调兜底) const q = await request.get(`/mini/payment/online/${payment_no}/query`); // q.data.status === 1 → 支付成功,刷新账单列表 ``` --- ## 2. 查询支付结果(主动同步) | 项 | 值 | |---|---| | 请求方式 | `GET` | | 路径 | `/mini/payment/online/{payment_no}/query` | | 鉴权 | 门店 token(仅可查询本店支付单) | ### 路径参数 | 参数 | 说明 | |---|---| | `payment_no` | 下单返回的支付单号(如 `ZF202608270001`) | ### 行为说明 - 本地已是「支付成功」→ 直接返回,不再请求网关; - 否则向旺铺网关发起「交易查询」,网关返回已支付(`order_status=1`)则**立即结账**(与后台通知同一幂等逻辑); - 建议在 `wx.requestPayment` 成功回调后、以及付款页 `onShow` 时各调一次; - 用户中途取消支付时本接口返回 `status: 0`,账单仍处于锁定中,可稍后重试或联系客服释放。 ### 响应示例 ```json { "success": true, "msg": "支付成功", "data": { "payment_no": "ZF202608270001", "status": 1, "status_name": "支付成功", "paid_at": "2026-08-27 10:00:00", "trade_no": "2021082722001407831438768160" } } ``` ### `status` 取值 | 值 | 含义 | |---|---| | `0` | 待支付(网关未确认) | | `1` | 支付成功(账单已置已支付) | | `2` | 支付失败(网关下单失败作废,账单已释放,可重新下单) | --- ## 3. 支付结果后台通知(服务端对接,无需小程序调用) | 项 | 值 | |---|---| | 请求方式 | `POST`(表单) | | 路径 | `/mini/payment/notify` | | 鉴权 | 无(公开路由,以报文 `sign` 验签) | | 来源 | 旺铺网关(下单时上送的 `notifyurl`,由后端自动生成:`{APP_URL}/mini/payment/notify`) | ### 处理逻辑 1. 按旺铺加签规则验签(参数 ASCII 升序 `key=value` 以 `&` 拼接 + `&key=SignKey`,MD5 大写); 2. 验签失败 → 应答 `{"code":"01"}`; 3. 按 `mer_order_id` 定位支付单,校验 `order_status=1` 且 `order_amt` 与支付单金额一致(防篡改); 4. **幂等结账**:支付单置成功(记录 `trade_no`/`order_id`/支付时间)→ 关联账单批量置已支付 → 累加门店总采购金额(只统计商品金额)→ 站内通知门店; 5. 重复通知直接应答成功,不重复结账。 ### 应答报文(网关约定格式) ```json { "code": "00", "msg": "成功", "timestamp": "20260827100000123" } ``` 网关收到 `code=00` 才认为通知成功,否则按 2ⁿ 分钟重试 7 次。 --- ## 后台配置(上线前必配) 配置优先级:后台「系统设置 → 支付配置」> `.env`。 ### 后台支付配置(site_config `pay` 组,已内置配置项) | 配置项 | 说明 | |---|---| | `wangpu_base_url` | 旺铺网关域名(接口地址前缀) | | `wangpu_organiz_no` | 合作机构渠道号 organiz_no | | `wangpu_mer_no` | 旺铺内部商户号 mer_no(进件入网后返回) | | `wangpu_mer_code` | 商户号 mer_code | | `wangpu_term_code` | 终端号 term_code | | `wangpu_sign_key` | 加签专用 Key(SignKey) | | `wangpu_sub_appid` | 下单微信子 appid,留空取小程序自身 appid | | `wangpu_payway_code` | 支付方式代码(默认 `WECHAT_MINI`,以旺铺数据词典为准) | ### .env 备选配置 ```dotenv WECHAT_MINI_APPID= # 小程序 AppID(code2session 必需) WECHAT_MINI_SECRET= # 小程序 Secret(code2session 必需) WANGPU_BASE_URL= WANGPU_ORGANIZ_NO= WANGPU_MER_NO= WANGPU_MER_CODE= WANGPU_TERM_CODE= WANGPU_SIGN_KEY= WANGPU_SUB_APPID= WANGPU_PAYWAY_CODE=WECHAT_MINI ``` > 注意:`APP_URL` 必须是网关可访问的公网 HTTPS 地址,否则支付成功通知无法送达(此时依赖小程序主动查询兜底结账)。 ## 与线下凭证支付的关系 两种付款方式并存,同一张账单同一时刻只能处于一条支付链路: | | 在线支付(本档) | 线下凭证支付 | |---|---|---| | 入口 | `POST /mini/payment/online` | `POST /mini/payment` | | 支付单 `pay_type` | `2` | `1` | | 支付单 `pay_method` | `4` 旺铺支付 | `1` 微信 / `2` 支付宝 / `3` 对公汇款 | | 结账方式 | 网关通知/主动查询自动结账 | 后台审核通过 | | 失败/拒绝 | 账单自动释放可重新付款 | 审核拒绝后释放 | 账单被任一支付单锁定期间(`payment_id ≠ 0`),两种入口均会拒绝重复提交。