在前端开发中,支付功能是必不可少的一部分。而微信支付作为移动支付中的佼佼者,它的快速、安全和方便性也让它成为了具有广泛应用场景的支付方式之一。本文将详细介绍如何使用 Hapi 框架实现微信支付功能,并分享在开发过程中遇到的 Bug 及处理方式,帮助读者更好地掌握 Hapi 和微信支付的实现技巧。
环境搭建
在开始开发之前,我们需要准备好以下环境:
- 一个微信公众号(或小程序)的 appId 和 secretKey
- Node.js 开发环境
- Hapi 框架
我们先介绍一下如何申请微信支付需要的信息。在微信支付开发者文档中,选择"申请开发者权限",依次填写相关信息如商户账号、邮箱等,提交后会收到审核通过的邮件,并获得自己的 appId 和 secretKey。此时我们已经可以开始在 Hapi 框架中实现微信支付功能。
功能实现
统一下单接口
微信支付流程的第一步是通过微信支付接口向微信后台发起请求,生成预付款订单,之后微信会返回响应结果并附带了 prepay_id(预付款订单号),接口定义如下:
// 统一下单 async function unifiedOrder(productId, ipAddress, openid, amount) { let nonceStr = getNonceStr() // 微信签名所需的随机字符串 let timeStamp = getTimeStamp() // 时间戳 let outTradeNo = `${getOutTradeNo()}${productId}` // 商户订单号 // 构建请求体 let data = { appid: process.env.APPID, mch_id: process.env.MCH_ID, nonce_str: nonceStr, body: '购买商品', // 商品描述 out_trade_no: outTradeNo, // 商户订单号 total_fee: amount, // 订单总金额,单位为分 spbill_create_ip: ipAddress, // 用户IP notify_url: `${process.env.PAY_NOTIFY_SERVER}/api/pay/notify`, // 支付成功后的回调地址 trade_type: 'JSAPI', // 微信小程序支付类型 openid: openid // 用户 openid } // 统一下单接口签名,将参数和签名并上传至微信支付接口 let sign = signData(data) data.sign = sign let result = await axios.post(`${payBaseUrl}/unifiedorder`, json2xml(data)) // 解析结果 let prepayId = result.data.prepay_id // …… }
其中,重点需要注意的是在调用微信支付前需要对请求参数进行签名处理。签名算法有比较严格的要求,具体可以参考微信支付开发者文档中的操作方法,或者在第三方的微信支付 SDK 中查找算法。
订单查询接口
订单查询接口是用于查询订单最新状态,但需要注意的是,退款也属于一种 order_status 的状态,查询到“order_status”为“REFUND”并不意味着该订单已完成支付,接口定义如下:
// 订单查询 async function orderQuery(outTradeNo) { let nonceStr = getNonceStr() // 微信签名所需的随机字符串 let timeStamp = getTimeStamp() // 时间戳 // 构建请求体 let data = { appid: process.env.APPID, mch_id: process.env.MCH_ID, nonce_str: nonceStr, out_trade_no: orderNo // 商户订单号 } // 订单查询接口签名,将参数和签名并上传至微信支付接口 let sign = signData(data) data.sign = sign let result = await axios.post(`${payBaseUrl}/orderquery`, json2xml(data)) // 解析结果 let resultCode = result.data.result_code let tradeState = result.data.trade_state // 交易状态 let totalFee = result.data.total_fee // 订单总金额 // …… }
定义微信支付回调
微信支付支付成功后将异步回调商户服务器,通知支付结果。所以我们需要在服务器端定义回调逻辑。在 Hapi 框架中,可以使用 server.route()
函数创建一个 POST 请求处理路由,接收微信支付回调的 XML 报文,并对其进行验签和业务处理,示例代码如下:
// 定义微信支付回调 server.route({ method: 'POST', path: '/api/pay/notify', handler: async function (request, reply) { try { let data = await parseXML(request.payload) // 解析微信支付回调请求体 XML 格式 if (validateSign(data)) { // 验证请求参数签名是否正确 let { out_trade_no, result_code } = data.xml let tradeState = data.xml.trade_state // 交易状态 if (result_code === 'SUCCESS' && tradeState === 'SUCCESS') { // 验证业务状态是否正确 console.log(`微信支付成功,商户订单号:${out_trade_no}`) // 对于一个商户订单号只能调用一次回调,在业务处理成功后,需要将订单状态以及数据保存入库 // …… } } } catch (e) { console.log(e) } reply('success') // 要及时返回处理结果,让微信支付知道该订单已处理完毕 } })
遗留 Bug 及解决方案
在实际开发过程中,我们也会遇到一些问题,例如开发中出现提示“ERR_SSL_VERSION_OR_CIPHER_MISMATCH”,这是因为微信支付接口采用了 TLS1.2 协议,所以需要升级 Node.js 版本以支持该协议。同时,如果时间戳配置不正确可能会导致签名校验失败,所以需要开启 NTP 服务,并将时间同步为北京时间。
此外,还有一种比较常见的问题:对于一些特殊字符(如“+”、“&”等),微信支付会对其进行 URL 编码,而如果在验签的过程中没有进行 URL 解码,则会导致签名验证失败。所以,在验证签名之前需要将 XML 报文中的字符数据进行 URL 解码。
总结
本文详细介绍了如何使用 Hapi 框架实现微信支付功能,并分享了在开发过程中遇到的 Bug 及处理方式,希望能够帮助读者更好地掌握 Hapi 和微信支付的实现技巧。我们需要注意签名算法的处理、订单状态查询的注意事项,在回调业务处理成功之后,需要及时保存订单状态及相关数据到数据库。
在实际开发中,如果我们需要进行更加复杂的逻辑处理,例如多个订单的批量处理等,可以选择使用队列、定时器等技术方案。本文提供的示例代码可以作为一个参考,并在实际开发中加以调整和优化,以适应不同场景的需求。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a77cdcadd4f0e0ff09cc47