# 智价云(药店版) - 微信小程序支付接口文档 > **适用对象**:小程序前端开发 > **版本**:v1.0 | 更新日期:2026-07-09 > **Base URL**:`http://localhost:8080`(开发环境) --- ## 1. 概述 小程序支付使用微信 **JSAPI 支付**,与扫码支付(NATIVE)流程完全不同:不是展示二维码,而是**一键调起微信支付面板**。 | | WECHAT / ALIPAY(扫码) | WECHAT_MINIAPP(小程序) | |---|---|---| | 用户操作 | 展示二维码 → 扫码付款 | **直接弹出微信支付面板** | | 返回凭证 | `qrCodeBase64`(Base64图片) | `miniAppPayParams`(签名对象) | | openId | 不需要 | 自动从用户记录读取(登录时已存储) | | 下单接口 | `TradeTypeEnum.NATIVE` | `TradeTypeEnum.JSAPI` | --- ## 2. 前置条件 1. ⚠️ **用户必须已通过小程序登录**,`t_user.wechat_mini_open_id` 不为空 2. 如果用户未登录小程序,调用创建订单会返回错误:`"未获取到小程序 openId,请先通过小程序登录"` 3. `openId` 在登录时通过 `code2Session` 获取并持久化,支付时从数据库直接读取,**不需要重复 wx.login 授权** --- ## 3. 完整前端调用流程 ```javascript // ──── 步骤 1:用户登录(已有流程,无需改动)──── // 小程序端调用 wx.login() + 后端 miniappUnifiedLogin // → 后端 code2Session 获取 openId 并存入 t_user.wechat_mini_open_id // → 返回 JWT token(后续请求携带) // ──── 步骤 2:创建小程序支付订单 ──── const res = await request({ url: '/api/payment/order/create', method: 'POST', header: { Authorization: `Bearer ${token}` }, data: { planId: 1, channel: 'WECHAT_MINIAPP' // ⚠️ 不传 openid,后端从用户记录读取 } }) // ──── 步骤 3:调起微信支付面板 ──── const params = res.data.miniAppPayParams wx.requestPayment({ timeStamp: params.timeStamp, nonceStr: params.nonceStr, package: params.package, // "prepay_id=wx..." signType: params.signType, // "RSA" paySign: params.paySign, success(res) { // 支付成功 → 查询订单确认状态 queryOrderStatus(orderNo) }, fail(err) { // 用户取消或支付失败 console.log('支付取消', err) } }) ``` --- ## 4. 接口详情 ### 4.1 获取支付方案列表(公开) ``` GET /api/payment/plans ``` **无需认证** **响应:** ```json { "code": 200, "data": [ { "id": 1, "planCode": "MONTHLY_PRO", "planName": "高级会员·月卡", "membershipLevel": "PRO", "price": 29.90, "originalPrice": 59.90, "durationDays": 30, "sortOrder": 3, "status": 1, "description": "每天不限量· 1个月29.9(原价59.9)" } ] } ``` --- ### 4.2 创建支付订单 ``` POST /api/payment/order/create ``` **需要认证**(Header: `Authorization: Bearer {token}`) **请求体:** ```json { "planId": 1, "channel": "WECHAT_MINIAPP" } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | planId | Long | 是 | 支付方案ID(从 4.1 接口获取) | | channel | String | 是 | 支付渠道,小程序固定传 `WECHAT_MINIAPP` | > ⚠️ **不需要传 `openid`**,后端自动从当前登录用户的 `t_user.wechat_mini_open_id` 读取。 **响应(WECHAT_MINIAPP — 小程序支付):** ```json { "code": 200, "data": { "orderNo": "OP1234567890123456", "planName": "高级会员·月卡", "amount": 29.90, "channel": "WECHAT_MINIAPP", "status": "PENDING", "qrCodeBase64": null, "payUrl": null, "miniAppPayParams": { "appId": "wxabc123...", "timeStamp": "1752060000", "nonceStr": "a1b2c3d4e5f6...", "package": "prepay_id=wx1234567890...", "signType": "RSA", "paySign": "XXXX..." }, "expireTime": "2026-06-30T22:00:00", "expireHint": "请在15分钟内完成付款,超时订单将自动过期", "createTime": "2026-06-30T21:45:00" } } ``` **响应字段说明:** | 字段 | 类型 | 说明 | |------|------|------| | orderNo | String | 订单号(后续查询/切换渠道用) | | planName | String | 方案名称 | | amount | BigDecimal | 金额(元) | | channel | String | 支付渠道 | | status | String | PENDING-待支付 / PAID-已支付 / EXPIRED-已过期 | | qrCodeBase64 | String | 小程序支付为 null | | payUrl | String | 小程序支付为 null | | **miniAppPayParams** | Object | **小程序支付参数,用于调起微信支付** | | expireTime | String | 过期时间(创建后15分钟) | | expireHint | String | 过期提示文案 | | createTime | String | 创建时间 | #### miniAppPayParams 字段 → wx.requestPayment 参数映射 | 字段 | 说明 | wx.requestPayment 参数 | |------|------|------------------------| | appId | 小程序AppID | ❌ **不传**(wx.requestPayment 不需要) | | timeStamp | 时间戳(秒,字符串) | ✅ `timeStamp` | | nonceStr | 随机字符串(32位hex) | ✅ `nonceStr` | | package | 格式 `prepay_id=xxx` | ✅ `package` | | signType | 固定 `RSA` | ✅ `signType` | | paySign | RSA-SHA256 签名 | ✅ `paySign` | --- ### 4.3 切换支付渠道 ``` POST /api/payment/order/{orderNo}/switch-channel ``` **需要认证** **业务场景:** 用户创建订单后,如需从扫码支付切换为小程序支付(或反之),调用此接口。 > 不关闭原渠道订单,以第一个支付成功渠道为准,后续重复支付自动退款。 **请求体:** ```json { "channel": "WECHAT_MINIAPP" } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | channel | String | 是 | 新支付渠道:WECHAT / ALIPAY / WECHAT_MINIAPP | > 同样不传 `openid`,后端自动从用户记录读取。 **响应:** 同 4.2 创建订单(根据新渠道返回对应格式) **切换渠道示例:** ```javascript // 从 WECHAT_MINIAPP 切换到 WECHAT 扫码: // → 返回 qrCodeBase64(展示二维码让用户扫码) // 从 WECHAT 切换到 WECHAT_MINIAPP: // → 返回 miniAppPayParams(直接调起支付面板) ``` --- ### 4.4 查询订单状态 ``` GET /api/payment/order/{orderNo} ``` **需要认证** **响应:** ```json { "code": 200, "data": { "orderNo": "OP1234567890123456", "planName": "高级会员·月卡", "amount": 29.90, "channel": "WECHAT_MINIAPP", "status": "PAID", "qrCodeBase64": null, "payUrl": null, "expireTime": "2026-06-30T22:00:00", "expireHint": null, "createTime": "2026-06-30T21:45:00" } } ``` > 支付成功后 `status` 变为 `PAID`,可跳转至支付成功页。 --- ### 4.5 我的订单列表 ``` GET /api/payment/orders ``` **需要认证** **响应:** ```json { "code": 200, "data": [ { "orderNo": "OP1234567890123456", "planName": "高级会员·月卡", "amount": 29.90, "channel": "WECHAT_MINIAPP", "status": "PAID", "createTime": "2026-06-30T21:45:00" } ] } ``` --- ## 5. 错误场景 | 错误信息 | 原因 | 解决方案 | |----------|------|----------| | `未获取到小程序 openId` | 用户未通过小程序登录 | 先调用小程序登录接口 | | `用户不存在` | userId 无效 | 检查 JWT token 是否有效 | | `微信支付未配置` | payment.wechat 参数缺失 | 联系后端检查配置 | | `支付方案不存在` | planId 无效 | 检查 planId 是否正确 | | `支付方案已下架` | 方案已停用 | 提示用户该方案已下架 | | `支付订单已过期` | 超过15分钟未支付 | 重新创建订单 | --- ## 6. 支付模块错误码 | code | 说明 | |------|------| | 2001 | 支付订单不存在 | | 2002 | 支付订单已过期 | | 2003 | 支付金额不正确 | | 2004 | 不支持该支付渠道 | | 2005 | 支付失败 | | 2006 | 退款失败,请重试 | | 2007 | 支付方案不存在 | | 2008 | 支付方案已下架 | | 2010 | 支付渠道未配置 | --- ## 7. 后端配置要求 以下为后端配置,供联调参考: ```yaml # application.yml wechat: miniapp: app-id: wxMINIAPP123... # 小程序appId(登录用) app-secret: secret... # 小程序appSecret(登录用) payment: miniapp: app-id: wxMINIAPP123... # 支付签名用appId(必须与小程序一致) wechat: app-id: wxOFFICIAL... # Native扫码用appId(可与上面不同) mch-id: 1234567890 # 商户号(Native + JSAPI 共用) # ... 密钥、证书等 ``` --- ## 8. 完整 API 清单 | 接口 | 方法 | 认证 | 说明 | |------|------|------|------| | `/api/payment/plans` | GET | 否 | 获取支付方案列表 | | `/api/payment/order/create` | POST | 是 | 创建订单(channel=WECHAT_MINIAPP) | | `/api/payment/order/{orderNo}/switch-channel` | POST | 是 | 切换支付渠道 | | `/api/payment/order/{orderNo}` | GET | 是 | 查询订单状态 | | `/api/payment/orders` | GET | 是 | 我的订单列表 | --- ## 9. 统一响应格式 所有接口返回格式: ```json { "code": 200, "message": "success", "data": {} } ``` | code | 说明 | |------|------| | 200 | 成功 | | 400 | 参数错误 | | 401 | 未认证(Token 无效或过期) | | 403 | 无权限 | | 500 | 服务器内部错误 |