微信支付用的V2版本
微信支付說明文檔:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 參數詳細說明請自行查看
微信支付2.0還是xml傳輸數據,用到了解析模塊xml2js
接下來是請求微信支付,付款到零錢的主要代碼,可以自行創建一個wxpay.js,將下面代碼貼進去,
商戶appid需要特別說明一下。如果你要是付款給公衆號下的用戶,就要填寫公衆號appid,付款給小程序下的用戶,就要填寫小程序的appid
我參與的項目,商戶,小程序和公衆號都有綁定,所以會稍稍複雜一點,當時我測試用的是小程序appid和小程序下的用戶openid,大意了,應該是公衆號的
const fs = require('fs') const path = require('path') const xml2js = require('xml2js') const request = require('request') const payUtil = require('./payUtil') /** * 微信-企業付款到零錢 * * 注意的點是,企業付款到零錢,用的是公衆號下的用戶openid,所以appid要填成公衆號的,保持一致,不能填小程序的 */ exports.transfers = async (map) => { let mapInfo = {} // 商戶appid // 這裏的商戶appid是公衆號appid mapInfo.mch_appid = '' // 商戶id mapInfo.mchid = '' // 商戶密鑰 mapInfo.mchkey = '' // 隨機字符串 mapInfo.nonce_str = payUtil.getRnd32() // 商戶內部流水號 mapInfo.partner_trade_no = map.recordId // 用戶的openid mapInfo.openid = map.openid // 是否需要校驗名字 mapInfo.check_name = 'NO_CHECK' // 金額,單爲是分 mapInfo.amount = map.amount * 100 // 描述 mapInfo.desc = '企業付款到零錢' // ip,該IP同在商戶平臺設置的IP白名單中的IP沒有關聯,該IP可傳用戶端或者服務端的IP。 mapInfo.spbill_create_ip = '' // 簽名 let sign = wxpay.transfersSign(mapInfo) // 拼接xm字符串 let formData = "<xml>"; formData += "<mch_appid>" + mapInfo.mch_appid + "</mch_appid>"; formData += "<mchid>" + mapInfo.mchid + "</mchid>"; formData += "<nonce_str>" + mapInfo.nonce_str + "</nonce_str>"; formData += "<partner_trade_no>" + mapInfo.partner_trade_no + "</partner_trade_no>"; formData += "<openid>" + mapInfo.openid + "</openid>"; formData += "<check_name>" + mapInfo.check_name + "</check_name>"; formData += "<amount>" + mapInfo.amount + "</amount>"; formData += "<desc>" + mapInfo.desc + "</desc>"; formData += "<spbill_create_ip>" + mapInfo.spbill_create_ip + "</spbill_create_ip>"; formData += "<sign>" + sign + "</sign>"; formData += "</xml>"; console.log(formData) // 請求路徑 let url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers' return new Promise((resolve, reject) => { request({ url: url, // 需要證書 agentOptions: { cert: fs.readFileSync(path.resolve(`./public/config/cert/${payConfig.wx_cert}`)), key: fs.readFileSync(path.resolve(`./public/config/cert/${payConfig.wx_key}`)) }, method: 'post', body: formData, }, function (err, response, body) { if (!err && response.statusCode == 200) { // 創建一個解析xml的對象 let parser = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false }); //解析簽名結果xml轉json parser.parseString(body, (err, res) => { console.log(res) let result = {} // 判斷return_code 爲fail的時候,的錯誤信息 if (res.return_code == 'FAIL') { result.msg = res.return_msg reject(result) } // return_code是success 的話, 只代表業務已受理, 並不代表已成功 // result_code是success 的話, 纔算是付款成功, fail的話,返回錯誤信息 if (res.return_code == 'SUCCESS' && res.result_code == 'FAIL') { result.msg = res.err_code_des reject(result) } else { result.msg = '' resolve(result) } }) } reject(err) }) }) }
在其他地方調用付款到零錢transfers方法
// 微信支付方法路徑自行修改成自己的 const { transfers } = require('./wxpay') const { createOrderNumber } = require('./payUtil') let map = { // 金額 amount: 1, // 備註 desc: '提現', // 用戶openid openid: '', // 系統內部流水號 recordId: createOrderNumber(), } try { let result = await transfers(map) ctx.body = { code: 200, msg: '提現到零錢成功,提醒用戶稍後自行查看' } } catch (err) { console.log(err) // 捕捉請求微信支付的錯誤 ctx.body = { code: 500, msg: err.msg } return }
下面貼一些工具方法,自行創建一個payUtil.js,將下面代碼貼入即可
// 生成隨機隨機32 位 字符串 exports.getRnd32 = () => { let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' let result = '' for (let i = 0; i < 32; i++) { let rnd = Math.floor(Math.random() * str.length) result += str[rnd] } return result } // 生成時間戳 exports.createTimeStamp = () => { return parseInt(new Date().getTime() / 1000) + ''; } // 生成訂單編號 exports.createOrderNumber = () => { let str = '' for (let i = 0; i < 10; i++) { let num = Math.floor(Math.random() * 10) str += num } let time = new Date() let year = time.getFullYear().toString() let month = time.getMonth().toString() + 1 let day = time.getDate().toString() let hours = time.getHours().toString() let minutes = time.getMinutes().toString() let seconds = time.getSeconds().toString() let mill = time.getMilliseconds().toString() str += year += month += day += hours += minutes += seconds += mill return str; } // 按照ascll碼排序 function raw(args) { var keys = Object.keys(args); keys = keys.sort() var newArgs = {}; keys.forEach(function (key) { newArgs[key] = args[key]; }); var string = ''; for (var k in newArgs) { string += '&' + k + '=' + newArgs[k]; } string = string.substr(1); return string; } /** * 企業付款到零錢簽名 */ exports.transfersSign = (map) => { let ret = { mch_appid: map.mch_appid, mchid: map.mchid, nonce_str: map.nonce_str, partner_trade_no: map.partner_trade_no, openid: map.openid, check_name: map.check_name, amount: map.amount, desc: map.desc, spbill_create_ip: map.spbill_create_ip, } console.log(ret) var string = raw(ret); var key = map.mchkey; string = string + '&key=' + key; var crypto = require('crypto'); return crypto.createHash('md5').update(string, 'utf8').digest('hex').toUpperCase(); }