1. 微信小程序支付全链路开发实战
第一次做微信小程序支付时,我被各种状态流转搞得头大。用户支付成功后回调没收到怎么办?订单超时未支付怎么处理?退款流程如何设计?这些问题在实际开发中都会遇到。下面我就结合电商项目的实战经验,带你完整走通支付全链路。
小程序支付的核心流程可以分为五个阶段:下单、支付、回调、查询、关单。每个阶段都需要处理不同的状态,比如支付中的NOTPAY、支付成功的SUCCESS、已关闭的CLOSED等。在实际项目中,我建议把状态管理单独抽离出来,用状态模式来封装不同状态下的业务逻辑。
先看下单环节的代码示例:
// 服务端下单接口 router.post('/createOrder', async (ctx) => { const { openid, goodsId } = ctx.request.body const outTradeNo = generateOrderNo() // 生成商户订单号 // 调用微信统一下单API const params = { appid: config.appId, mch_id: config.mchId, out_trade_no: outTradeNo, total_fee: 100, // 单位:分 openid, body: '商品描述', notify_url: config.notifyUrl // 支付回调地址 } const prepayRes = await wxPay.unifiedOrder(params) if (prepayRes.return_code === 'SUCCESS') { // 保存订单到数据库 await saveOrder({ outTradeNo, status: 'NOTPAY', // 初始状态 // 其他订单字段... }) // 返回支付参数给小程序端 ctx.body = { timeStamp: Date.now().toString(), nonceStr: generateNonceStr(), package: `prepay_id=${prepayRes.prepay_id}`, signType: 'MD5', paySign: generateSign(prepayRes) } } })这里有几个关键点需要注意:
- 商户订单号(out_trade_no)必须全局唯一
- 金额单位是分,要处理好小数转换
- 回调地址(notify_url)要配置正确
- 订单初始状态设为NOTPAY
2. 支付状态管理与异常处理
支付状态管理是小程序支付最复杂的部分。在实际项目中,我遇到过三种典型场景:
- 用户支付成功但没收到回调
- 用户支付中途退出
- 网络问题导致支付状态不一致
针对这些问题,我们需要设计健壮的状态管理机制。我的做法是使用"主动查询+被动回调"双保险模式:
// 支付结果查询接口 router.get('/checkPayment', async (ctx) => { const { outTradeNo } = ctx.query // 先查本地数据库 const order = await getOrder(outTradeNo) if (order.status !== 'NOTPAY') { return ctx.body = { status: order.status } } // 本地无结果则查询微信支付 const queryRes = await wxPay.orderQuery({ out_trade_no: outTradeNo }) if (queryRes.trade_state === 'SUCCESS') { await updateOrderStatus(outTradeNo, 'SUCCESS') // 处理支付成功业务逻辑 processPaymentSuccess(order) } ctx.body = { status: queryRes.trade_state } })同时要处理好微信的异步回调:
// 支付结果回调接口 router.post('/notify', async (ctx) => { const xmlData = ctx.request.body const result = parseXml(xmlData) // 验证签名 if (verifySign(result)) { if (result.return_code === 'SUCCESS') { await updateOrderStatus(result.out_trade_no, 'SUCCESS') // 处理业务逻辑 processPaymentSuccess(result) } ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>' } })对于超时未支付的订单,需要定时任务处理:
// 定时关闭未支付订单 schedule.scheduleJob('0 */30 * * * *', async () => { const expiredOrders = await getExpiredOrders() for (const order of expiredOrders) { try { await wxPay.closeOrder({ out_trade_no: order.outTradeNo }) await updateOrderStatus(order.outTradeNo, 'CLOSED') // 处理关单业务逻辑 processOrderClosed(order) } catch (err) { logger.error('关单失败', err) } } })3. 退款流程设计与实现
退款是支付链路中另一个重要环节。在实际项目中,我建议将退款流程设计为:
- 用户发起退款申请
- 系统验证退款条件
- 调用微信退款API
- 处理退款结果
- 更新订单状态
关键代码实现:
// 退款接口 router.post('/refund', async (ctx) => { const { outTradeNo, refundReason } = ctx.request.body // 检查订单是否可退款 const order = await getOrder(outTradeNo) if (order.status !== 'SUCCESS') { throw new Error('订单未支付,不能退款') } // 调用微信退款API const params = { out_trade_no: outTradeNo, out_refund_no: generateRefundNo(), total_fee: order.totalFee, refund_fee: order.totalFee, notify_url: config.refundNotifyUrl } const refundRes = await wxPay.refund(params) // 更新订单状态 await updateOrderStatus(outTradeNo, 'REFUND_PROCESSING') ctx.body = { success: true } }) // 退款结果回调 router.post('/refundNotify', async (ctx) => { const xmlData = ctx.request.body const result = parseXml(xmlData) if (verifySign(result)) { if (result.refund_status === 'SUCCESS') { await updateOrderStatus(result.out_trade_no, 'REFUND_SUCCESS') // 处理退款成功业务逻辑 processRefundSuccess(result) } ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>' } })退款过程中需要注意:
- 退款金额可以小于原支付金额(部分退款)
- 同一笔订单可以多次退款
- 退款结果需要通过回调或主动查询确认
- 退款状态要与原订单状态分离管理
4. 支付安全与性能优化
在电商项目中,支付环节的安全性和性能至关重要。我总结了几个实战经验:
- 防重复支付:前端按钮防抖+服务端幂等控制
// 前端防抖处理 let isPaying = false function handlePay() { if (isPaying) return isPaying = true wx.requestPayment({ // 支付参数 complete() { isPaying = false } }) }- 数据加密:敏感数据加密传输
// 敏感字段加密 function encryptData(data) { const cipher = crypto.createCipheriv('aes-256-cbc', key, iv) let encrypted = cipher.update(data, 'utf8', 'hex') encrypted += cipher.final('hex') return encrypted }- 性能优化:
- 支付结果查询接口添加缓存
- 数据库订单表添加适当索引
- 使用消息队列处理支付成功后的业务逻辑
- 监控报警:
- 支付失败率监控
- 回调超时监控
- 订单状态异常监控
在实际项目中,我们还实现了分布式事务来保证订单和库存的一致性。当支付成功时,通过本地消息表确保库存扣减和订单状态更新的原子性。