node-koa2 微信支付-企业付款到银行卡

微信支付用的V2版本

微信支付说明文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay_yhk.php?chapter=24_2   参数详细说明请自行查看

 

提示语:

微信支付2.0还是xml传输数据,用到了解析模块xml2js

可以自行创建一个wxpay.js,将下面获取公钥和付款到银行卡的代码贴进去

特别说明一下:付款到银行卡,不仅需要证书,还需要用微信提供的公钥(下面有获取代码示例),对收款方姓名和收款银行卡号,进行RSA加密,

 

上代码,

获取微信提供的加密公钥

/**
 * 获取公钥
 * 
 * 获取企业付款到银行卡的加密公钥
 */
exports.getpublickey = async (payConfig) => {
    let mch_id = payConfig.Wechat_merchant_number
    let mchkey = payConfig.Wechat_pay_key
    let url = 'https://fraud.mch.weixin.qq.com/risk/getpublickey'
    let formData = "<xml>"

    let mapInfo = {}
    // 商户号
    mapInfo.mch_id = mch_id
    // 随机字符串
    mapInfo.nonce_str = wxpay.getRnd32()
    // 商户密钥
    mapInfo.mchkey = mchkey
    // 加密方式
    mapInfo.sign_type = "MD5"
    // 签名
    let sign = wxpay.getpublickkeySign(mapInfo)

    formData += "<mch_id>" + mch_id + "</mch_id>"
    formData += "<nonce_str>" + mapInfo.nonce_str + "</nonce_str>"
    formData += "<sign>" + sign + "</sign>"
    formData += "<sign_type>MD5</sign_type>"
    formData += "</xml>"
    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) {
                let parser = new xml2js.Parser({
                    trim: true,
                    explicitArray: false,
                    explicitRoot: false
                }); //解析签名结果xml转json
                parser.parseString(body, (err, res) => {
                    console.log(res)
                    // console.log(res)

                    if (res.return_code == 'FAIL') {
                        reject(res.return_msg)
                    }
                    // return_code是success 的话, 只代表退款业务已受理, 并不代表已退款成功
                    // result_code是success 的话, 才算是退款成功, fail的话,返回错误信息
                    if (res.return_code == 'SUCCESS' && res.result_code == 'FAIL') {
                        reject(res.err_code_des)
                    } else {
                        resolve(res.pub_key)
                    }
                })
            }
            reject(err)
        })
    })
}

 

微信支付付款到银行卡的方法封装

const fs = require('fs')
const path = require('path')
const xml2js = require('xml2js')
const request = require('request')
const crypto = require('crypto')
const NodeRSA = require('node-rsa');

/**
 * RSA加密
*
* RSA加密可以用crypto模块。也可以使用node-rsa模块,参考如下连接:https://www.cnblogs.com/huangdaozhang/p/11109417.html 
*/ let encryptRSA = (key, hash) => { return crypto.publicEncrypt(key, Buffer.from(hash)).toString('base64') } /** * 付款到银行卡 */ exports.payBank = async (map) => { let publicKey = map.publicKey let url = 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank' let mapInfo = {} // 商户号 mapInfo.mch_id = '' // 商户密钥 mapInfo.mchkey = '' // 商户付款单号 mapInfo.partner_trade_no = map.recordId // 随机字符串 mapInfo.nonce_str = payUtil.getRnd32() // 收款方开户行银行编号 mapInfo.bank_code = map.bankCode // 收款方用户名 mapInfo.enc_true_name = encryptRSA(publicKey, map.payee) // 收款方银行卡号 mapInfo.enc_bank_no = encryptRSA(publicKey, map.receivingAccount) // 付款金额 mapInfo.amount = Math.round(map.amount * 100 * 100) / 100 // 付款说明 mapInfo.desc = map.desc // 签名 let sign = payUtil.payBankSign(mapInfo) // console.log(publicKey) // const a_public_key = new NodeRSA(publicKey); let formData = "<xml>"; formData += "<amount>" + mapInfo.amount + "</amount>"; formData += "<bank_code>" + mapInfo.bank_code + "</bank_code>"; // formData += "<desc>" + mapInfo.desc + "</desc>"; formData += "<enc_bank_no>" + mapInfo.enc_bank_no + "</enc_bank_no>"; formData += "<enc_true_name>" + mapInfo.enc_true_name + "</enc_true_name>"; formData += "<mch_id>" + mapInfo.mch_id + "</mch_id>"; formData += "<nonce_str>" + mapInfo.nonce_str + "</nonce_str>"; formData += "<partner_trade_no>" + mapInfo.partner_trade_no + "</partner_trade_no>"; formData += "<sign>" + sign + "</sign>"; formData += "</xml>"; console.log(formData) 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) { let parser = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false }); //解析签名结果xml转json parser.parseString(body, (err, res) => { console.log(res) let result = { recordId: mapInfo.partner_trade_no } 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) }) }) }

 

 

1. 接下来获取加密公钥,获取成功后,可以写入文件,,本人比较懒,没有搞呢。是不会变化的这个公钥

2. 获取公钥成功后,调用payBank方法,实现付款到银行卡

// 微信支付方法路径自行修改成自己的
const { payBank } = require('./wxpay')
const { createOrderNumber } = require('./payUtil')

router.post('/', async () => {
  let map = {
// 金额
    amount: 1,
    // 备注
    desc: '提现',
    // 用户openid
    openid: '',
    // 系统内部流水号
    recordId: createOrderNumber(),
}
// 第一步先获取公钥
let publicKey;
try {
    publicKey = await getpublickey(payConfig)
} catch (err) {
    console.log(err)
    ctx.body = {
        code: 500,
        msg: err
    }
    return
}
// 获取公钥成功后,调用payBank方法,付款到银行卡
try {
    let result = await payBank(map)
    ctx.body = {
        code: 200,
        msg: '微信受理成功, T+1到账'
    }
} 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.payBankSign = (map) => {
    let ret = {
      amount: map.amount,
      bank_code: map.bank_code,
      enc_bank_no: map.enc_bank_no,
      enc_true_name: map.enc_true_name,
      mch_id: map.mch_id,
      nonce_str: map.nonce_str,
      desc: map.desc,
      partner_trade_no: map.partner_trade_no,
    }
    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();
  }

/**
 * 获取公钥进行签名
 */
 exports.getpublickkeySign = (map) => {
    let ret = {
        mch_id: map.mch_id,
        nonce_str: map.nonce_str,
        sign_type: map.sign_type
    }
    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();
}

 

 

最后提一些我测试时出现的问题,有时你在本地测试的时候,会有如下报错

error:此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置

 可以参考下面的链接:https://blog.csdn.net/yexiaomodemo/article/details/109316364

posted @ 2021-10-11 16:08  时光凉忆  阅读(223)  评论(0编辑  收藏  举报