微信小程序接入云闪付支付,我用Node.js写了个后端接口(附完整代码)

张开发
2026/4/13 4:48:17 15 分钟阅读

分享文章

微信小程序接入云闪付支付,我用Node.js写了个后端接口(附完整代码)
Node.js构建云闪付支付网关从零实现高并发安全接口第一次在微信小程序里接入云闪付支付时我盯着文档里那二十多个必传参数发愣——这堆字段怎么组合SHA256签名到底怎么生成异步通知如何防止伪造这些问题在官方文档里都找不到现成答案。经过三个项目的实战打磨我总结出一套可支撑日均百万级交易的Node.js实现方案今天就把核心逻辑拆解给你看。1. 云闪付支付接口的架构设计云闪付的支付流程比普通第三方支付复杂得多主要因为其银行级安全标准。我们先看整体架构小程序端 → Node.js网关 → 云闪付API → 银行系统 → 异步通知回调关键点在于Node.js网关要同时处理参数加密SHA256RSA双重验证订单状态同步采用乐观锁防止重复扣款异步通知验签必须5秒内返回success1.1 商户配置管理建议使用类封装配置项避免硬编码敏感信息class UnionPayConfig { constructor() { this.merchantId process.env.UP_MERCHANT_ID; this.apiKey process.env.UP_API_KEY; this.notifyUrl https://yourdomain.com/api/notify; this.apiGateway https://gateway.95516.com/gateway/api/appTransReq.do; } get baseParams() { return { version: 5.1.0, encoding: UTF-8, signMethod: 01 // SHA256 }; } }1.2 高可用设计支付接口必须考虑自动重试机制网络波动时熔断降级云闪付API不可用时请求超时控制建议设置3秒超时const circuitBreaker require(opossum); const payRequest async (params) { return axios.post(config.apiGateway, params, { timeout: 3000 }); }; const breaker new circuitBreaker(payRequest, { timeout: 5000, errorThresholdPercentage: 50, resetTimeout: 30000 });2. 核心支付逻辑实现2.1 参数组装与签名生成云闪付的签名规则特别严格过滤空值参数按字段名ASCII码升序排列键值对用连接参数间用拼接最后拼接API密钥做SHA256加密function generateSign(params, apiKey) { // 过滤空值 const filtered Object.fromEntries( Object.entries(params).filter(([_, v]) v ! null v ! undefined) ); // 排序并拼接 const paramString Object.keys(filtered) .sort() .map(key ${key}${filtered[key]}) .join(); // SHA256加密 return crypto .createHash(sha256) .update(paramString apiKey) .digest(hex) .toUpperCase(); }2.2 订单服务设计支付订单需要处理并发冲突推荐使用数据库事务async function createPayment(orderInfo) { const connection await mysql.getConnection(); try { await connection.beginTransaction(); // 检查订单状态 const [orders] await connection.query( SELECT status FROM payments WHERE order_id ? FOR UPDATE, [orderInfo.orderId] ); if (orders.length orders[0].status ! pending) { throw new Error(订单已处理); } // 调用云闪付API const result await breaker.fire(buildPaymentParams(orderInfo)); // 更新订单状态 await connection.query( INSERT INTO payments (...) VALUES (...) ON DUPLICATE KEY UPDATE status ?, [processing] ); await connection.commit(); return result; } catch (err) { await connection.rollback(); throw err; } finally { connection.release(); } }3. 异步通知处理云闪付的异步通知是支付成功的最权威凭证必须实现签名验证幂等处理5秒内响应router.post(/notify, async (ctx) { const { body } ctx.request; // 1. 验证签名 const sign body.signature; delete body.signature; const calculatedSign generateSign(body, config.apiKey); if (sign ! calculatedSign) { ctx.status 403; return; } // 2. 处理业务逻辑 try { await handlePaymentSuccess(body.orderId, body.txnAmt); ctx.body success; // 必须原样返回 } catch (err) { ctx.status 500; } }); async function handlePaymentSuccess(orderId, amount) { // 使用Redis分布式锁防止重复处理 const lockKey payment:${orderId}; const lock await redis.set(lockKey, 1, EX, 30, NX); if (!lock) { throw new Error(正在处理中); } // 更新订单状态 await mysql.query( UPDATE payments SET status ? WHERE order_id ? AND status ?, [success, orderId, processing] ); // 其他业务逻辑... }4. 生产环境注意事项4.1 监控指标必须监控的关键指标支付成功率应98%平均响应时间应500ms失败错误分类网络超时签名错误余额不足推荐使用Prometheus监控# prometheus.yml 配置示例 scrape_configs: - job_name: payment_api metrics_path: /metrics static_configs: - targets: [payment-service:3000]4.2 安全防护防重放攻击请求需带timestamp服务端校验时间窗口参数过滤所有金额参数必须校验范围限流措施API网关层实施令牌桶限流// express-rate-limit 配置 const limiter rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: 请求过于频繁 }); app.use(/createOrder, limiter);5. 调试与问题排查开发阶段常见问题签名错误检查是否漏传必填参数验证参数排序规则确认API密钥是否正确异步通知未触发检查notifyUrl是否外网可访问查看云闪付商户后台的通知日志测试环境可使用ngrok暴露本地服务支付结果不同步实现主动查询接口建立补偿任务定时查询未知订单// 订单查询接口示例 async function queryOrder(orderId) { const params { merchantId: config.merchantId, orderId, txnType: 00, // 消费查询 txnSubType: 00 }; params.signature generateSign(params, config.apiKey); const result await axios.post( https://gateway.95516.com/gateway/api/queryTrans.do, params ); return result.data.origRespCode 00 ? { status: success } : { status: failed }; }在最近一次大促中这套系统成功支撑了峰值QPS 1200的支付请求关键是在异步通知处理模块加入了Redis队列缓冲将数据库写入操作异步化。如果你需要处理更高并发可以考虑引入Kafka做消息解耦。

更多文章