微信支付已成为了当前移动支付领域的大佬,各互联网公司和个人开发者都在利用微信支付为自己的产品赋能。Hapi 是 Node.js 生态系统中一款优秀的框架,通过 Hapi 可以快速构建出一个稳定可靠的应用程序。本篇文章将会详细介绍如何利用 Hapi 框架实现微信支付和退款功能,并且涉及到一些具体的实现细节,可以为开发者提供学习和参考价值。
一、微信支付的实现
在实现微信支付前,首先需要了解微信支付的流程。微信支付主要由以下几个环节组成:
- 用户在小程序或公众号中发起支付请求。
- 小程序或公众号将支付请求发送到商户服务器。
- 商户服务器接到支付请求后,按照微信支付开发文档中的要求,构建出支付请求参数并发送给微信支付平台。
- 微信支付平台接收到商户的支付请求后,将请求加工并返回给商户服务器二维码链接或 payment_id。
- 商户将微信支付的二维码或 payment_id 呈现给用户,用户通过扫码或点击链接进入微信支付页面进行支付。
- 用户支付成功后,微信支付平台将支付成功的通知推送给商户服务器。
- 商户服务器接收到微信支付平台的通知并返回 success 给微信支付平台,微信支付平台不再推送通知。
在 Hapi 框架下,实现微信支付主要有以下步骤:
1. 实现微信支付相关接口
商户在与微信支付平台协商支付方式并开通商户号后,就可以使用商户号、应用ID、商户密钥等信息调用微信支付平台提供的支付接口,具体的接口和使用方式,参见微信支付官方文档。
在 Hapi 中可以通过定义路由和控制器实现微信支付相关接口,以下以 /wxpay
路径为例:
// javascriptcn.com 代码示例 const Hapi = require('@hapi/hapi'); const app = new Hapi.Server({ host: 'localhost', port: 4000 }); app.route({ method: 'POST', path: '/wxpay', handler: async (request, h) => { // TODO 调用微信支付 API } });
2. 构建微信支付请求参数
商户传递给微信支付平台的请求参数是一个 JSON 格式的对象,需要按照微信支付平台开发文档的要求构建。具体来说,包括以下几个参数:appid
、mch_id
、nonce_str
、sign
、body
、out_trade_no
、total_fee
、spbill_create_ip
、notify_url
、trade_type
、openid
。
其中,appid
为商户的应用ID,mch_id
为微信支付服务商的商户号,nonce_str
是一个不重复的随机数,用于防止重放攻击,sign
是一个签名值,用于验证请求数据的完整性和真实性,body
为商品或支付单简要描述,out_trade_no
为商户订单号,total_fee
为订单总金额,spbill_create_ip
为用户IP地址,notify_url
为回调地址,trade_type
为交易类型,openid
为用户的 OpenID。
构建支付请求参数的代码如下:
// javascriptcn.com 代码示例 const querystring = require('querystring'); const crypto = require('crypto'); const BASE_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; function sign(data, key) { const str = Object.keys(data).sort().reduce((str, key) => { return str + `${key}=${data[key]}&`; }, ''); return `${str}key=${key}`.toUpperCase(); } function generateNonceStr() { return Math.random().toString(36).substr(2, 15); } function generateTimestamp() { return Math.floor(new Date().getTime() / 1000); } function generatePayRequestParams(options) { const key = options.key; const nonce_str = generateNonceStr(); const timestamp = generateTimestamp(); const body = options.body; const out_trade_no = options.out_trade_no; const total_fee = options.total_fee; const spbill_create_ip = options.spbill_create_ip; const notify_url = options.notify_url; const trade_type = options.trade_type; const openid = options.openid; const data = { appid: options.appid, mch_id: options.mch_id, nonce_str, body, out_trade_no, total_fee, spbill_create_ip, notify_url, trade_type, openid }; data.sign = crypto.createHash('md5').update(sign(data, key)).digest('hex'); return querystring.stringify(data); }
3. 调用微信支付接口
在构建完微信支付的请求参数后,就可以通过调用微信支付接口实现支付功能。以 Hapi 的 request 方法和 axios 库为例:
// javascriptcn.com 代码示例 const axios = require('axios'); const options = { appid: 'wx8888888888888888', mch_id: '1900000001', nonce_str: '5K8264ILTKCH16CQ2502SI8ZNMTM67VS', key: 'paytest_key', body: 'APP支付测试', out_trade_no: '1415659990', total_fee: 100, spbill_create_ip: '127.0.0.1', notify_url: 'http://www.weixin.qq.com/wxpay/pay.php', trade_type: 'JSAPI', openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o' }; const payRequestParams = generatePayRequestParams(options); const response = await axios.post(BASE_URL, payRequestParams);
4. 处理微信支付的回调通知
微信支付的回调通知是指当用户支付成功后,微信支付平台将支付成功的消息推送给商户服务器,商户服务器需要对消息进行验证和处理。在 Hapi 中,可以通过定义路由和控制器来实现微信支付回调通知的处理:
app.route({ method: 'POST', path: '/wxpay/notify', handler: async (request, h) => { // TODO 处理微信支付回调通知 } });
需要注意的是,在接收到微信支付回调通知后,商户服务器需要返回 success
给微信支付服务器,确保微信支付平台不再推送通知。具体实现代码如下:
// javascriptcn.com 代码示例 app.route({ method: 'POST', path: '/wxpay/notify', handler: async (request, h) => { const notifyData = request.payload; const sign = notifyData.sign; delete notifyData.sign; const verifySign = crypto.createHash('md5').update(sign(notifyData, options.key)).digest('hex').toUpperCase(); if (verifySign === sign) { // TODO 处理订单 return h.response('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>').type('application/xml'); } else { return h.response('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[Invalid Sign]]></return_msg></xml>').type('application/xml'); } } });
二、微信退款的实现
在实现微信退款前,同样牵涉到退款的流程。微信支付平台提供了以下几个退款接口:
- 商户按照微信支付开发文档的要求,将退款请求数据发送至微信支付平台。
- 微信支付平台收到请求后,处理并返回结果至商户服务器。
- 商户接收到结果后,进行相应的处理。
在 Hapi 框架下,实现微信退款主要有以下步骤:
1. 实现微信退款接口
商户可以按照微信支付开发文档的要求,管理和使用退款接口。在 Hapi 中可以通过定义路由和控制器实现微信退款的接口:
app.route({ method: 'POST', path: '/wxrefund', handler: async (request, h) => { // TODO 调用微信支付退款 API } });
2. 构建微信退款请求参数
微信退款请求参数同样也是一个 JSON 格式的对象,具体包括以下参数:appid
、mch_id
、nonce_str
、sign
、out_refund_no
、out_trade_no
、total_fee
、refund_fee
。其中,appid
、mch_id
、nonce_str
和 sign
参数都与微信支付参数一致,out_refund_no
为商户生成的退款单号,out_trade_no
为商户交易号,total_fee
为订单总金额,refund_fee
为退款金额。
构建微信退款请求参数的代码如下:
// javascriptcn.com 代码示例 function generateRefundParams(options) { const key = options.key; const nonce_str = generateNonceStr(); const out_refund_no = options.out_refund_no; const out_trade_no = options.out_trade_no; const total_fee = options.total_fee; const refund_fee = options.refund_fee; const data = { appid: options.appid, mch_id: options.mch_id, nonce_str, out_refund_no, out_trade_no, total_fee, refund_fee }; data.sign = crypto.createHash('md5').update(sign(data, key)).digest('hex'); return querystring.stringify(data); }
3. 调用微信退款接口
与微信支付相似,调用微信退款接口同样需要发送退款请求数据并处理返回结果。以下以 Hapi 的 request 方法和 axios 库为例:
// javascriptcn.com 代码示例 const axios = require('axios'); const options = { appid: 'wx8888888888888888', mch_id: '1900000001', nonce_str: '5K8264ILTKCH16CQ2502SI8ZNMTM67VS', key: 'paytest_key', out_refund_no: '1415701182', out_trade_no: '1415757673', total_fee: 100, refund_fee: 80 }; const refundRequestParams = generateRefundParams(options); const response = await axios.post(BASE_URL, refundRequestParams);
三、总结
通过 Hapi 框架实现微信支付和退款主要分为以下几个步骤:实现微信支付、构建微信支付请求参数、调用微信支付接口、处理微信支付的回调通知、实现微信退款、构建微信退款请求参数、调用微信退款接口。
需要注意的是,微信支付和退款过程中涉及到非常多的细节问题,如微信支付的回调通知的安全性等,开发者在实现功能的过程中,需要认真阅读微信支付官方文档,并且针对特定的业务情况,根据微信支付的要求进行上述流程的实现。
示例代码部分参考自:https://github.com/holdmoon/hapi-wechat-pay/blob/master/lib/index.js。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652b85c67d4982a6ebd5e09d