zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    VC 常见问题百问
    python windows 环境变量
    Check server headers and verify HTTP Status Codes
    Where are the AES 256bit cipher suites? Please someone help
    outlook 如何预订会议和会议室
    安装Axis2的eclipse插件后,未出现界面
    windows 环境变量
    python 时间日期处理汇集
    openldap学习笔记(使用openldap2.3.32)
    set p4 environment in windows
  • 原文地址:https://www.cnblogs.com/naturl/p/15393789.html
Copyright © 2011-2022 走看看