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)要配置正确订单初始状态设为NOTPAY2. 支付状态管理与异常处理支付状态管理是小程序支付最复杂的部分。在实际项目中我遇到过三种典型场景用户支付成功但没收到回调用户支付中途退出网络问题导致支付状态不一致针对这些问题我们需要设计健壮的状态管理机制。我的做法是使用主动查询被动回调双保险模式// 支付结果查询接口 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 xmlreturn_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 xmlreturn_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 }性能优化支付结果查询接口添加缓存数据库订单表添加适当索引使用消息队列处理支付成功后的业务逻辑监控报警支付失败率监控回调超时监控订单状态异常监控在实际项目中我们还实现了分布式事务来保证订单和库存的一致性。当支付成功时通过本地消息表确保库存扣减和订单状态更新的原子性。