• node-koa2 微信支付,企业付款到零钱


    微信支付用的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();
    }
  • 相关阅读:
    Lucene学习总结之二:Lucene的总体架构
    Lucene学习总结之三:Lucene的索引文件格式(1)
    android开发_SeekBar进度条
    android开发_mp3_player音乐播放器
    Lucene学习总结之一:全文检索的基本原理
    android开发_Intent_requestCode_resultCode页面之间的跳转
    android开发_Location位置定位
    .NET Remoting Basic(7)信道
    .NET Remoting Basic(1)概览
    .NET Remoting Basic(5)多服务器访问和程序集共享
  • 原文地址:https://www.cnblogs.com/naturl/p/15393459.html
Copyright © 2020-2023  润新知