nodejs版微信支付之JSAPI统一下单接口调用

公司项目要用到微信支付的jsapi统一下单接口调用,由于官方缺乏nodejs版的代码范例,而网上查到的也是五花八门的代码,却没有一个能用,也没有一个能讲清楚的,害得我花了好大的劲,各方求证并积极联系微信支付的技术支持,试了N种方法、手段,终于调通了,现在无偿提供给大家参考:

1. 支付模块wxpay.js

var xmlreader = require("xmlreader");
var request   = require('request');
var crypto    = require('crypto');

var params {
    appid: 'xxxxxxxxxx',       //应用程序编号
    mch_id: 'xxxxxxxxxx',      //商户编号
    pay_key: 'xxxxxxxxxx',     //微信商户平台-->账户设置-->API安全-->密钥设置
    server_ip: 'xxxxxxxxxx',   //你的nodejs后端程序运行的服务器的公网IP
    //由你的nodejs服务提供的公网(可外网访问)接口,微信服务在支付完成后, 回调该接口通知支付状态
    notify_url: 'http://123.123.123.123/wxpay/notify', // 本数据仅为示例,实际根据需要填写
    trade_type:  "JSAPI", //统一下单服务类型,小程序对应"JSAPI", 如果是app应用,则为"APP"
    unified_url: "https://api.mch.weixin.qq.com/pay/unifiedorder" //统一下单接口,固定值
}

/**
 * 根据传入的json对象和格式要求,返回格式化后的结果
 * @param {*} obj 
 * @param {*} format 
 */
function raw(obj, format) {
    var str = '', keys = Object.keys(obj).sort();
    keys.forEach(function (k) {
        str += format == 'str' ? ('&' + k + '=' + obj[k])  : format == 'xml' ? ('<' + k + '>' + obj[k] + '</' + k + '>') : '';
    });     
    return format == 'str' ? str.substr(1) : format == 'xml' ? ('<xml>' + str + '</xml>') : '';
};

/**
 * 根据传入的json对象和支付key,进行签名并返回结果
 * @param {*} dt 
 * @param {*} key 
 */
function paySign(dt, key) {    
    var str = raw(dt, 'str') + '&key=' + key;
    return crypto.createHash('md5').update(str, 'utf8').digest('hex').toUpperCase();
};

//微信支付函数
exports.pay = function (req, res, callback) {
    var dt = {// 注意, 这里的对象属性不能缺,名称也不能改,切记!
        appid: params.appid,
        attach: '测试商品',                //支付说明,非固定值,自己根据实际情况写
        body: req.body.type,              //商品说明等内容
        mch_id: params.mch_id,
        nonce_str: Math.random().toString(36).substr(2, 15), //随机字符串
        notify_url: params.notify_url,
        openid: req.body.openID,          //当前付款微信账号的唯一标识
        out_trade_no: req.body.orderCode, //订单编号
        spbill_create_ip: params.server_ip,
        total_fee: req.body.amount,       //订单金额,不建议从前端传进来
        trade_type: params.trade_type
    };
    dt.sign = paySign(dt, params.pay_key);       //生成下单签名
    var formData = raw(dt, 'xml');
    request({
        url : params.unified_url,
        method : 'POST',
        body : formData
     }, function (err, response, body){
        if (!err && response.statusCode == 200){
            xmlreader.read(body.toString("utf-8"), function (errors, response) {
                if (errors) {
                    res.json({
                        code: 100,
                        message: errors
                    })
                }else{
                    var rt = {// 注意, 这里的对象属性不能缺,名称也不能改,切记!
                        appid: response.xml.appid.text(),
                        noncestr: response.xml.nonce_str.text(),
                        package: 'prepay_id=' + response.xml.prepay_id.text(),
                        signType: 'MD5',
                        timeStamp: parseInt(new Date().getTime() / 1000, 10)
                    };
                    // 再次签名, 生成前端调起支付页面需要的参数对象
                    rt.timeStamp = rt.timeStamp + ''; //必须字符串,否则前端调不起支付页面
                    rt.sign = paySign(rt, params.pay_key);
                    callback(rt);
                }
            });
        }else{
            res.json({
                code: 100,
                message: err
            })
        }
    });
} 

今天往正式环境部署服务时,发生了一件无法理解的事情,就是统一下单参数out_trade_no赋值有变化了。。。。。真无语啊。。。。。

我们测试服务器和正式服务器,都是阿里云服务器,本质上没有任何区别,但时,同样的代码,在测试服务器上,这个参数,只需要传正常的的订单号就行了。但到了正式服务器上,这个参数,就必须用out_trade_no = trade_type + orderNo的方式传值,也就是说,必须带上商户应用类型。。。。否则,就会报【201商户订单号重复】的错误。。。。为什么?微信支付的技术人员,你们做了什么????

2. 小程序下单接口与回调接口:

const wxpay = require('../service/wxpay');
const xmlreader = require("xmlreader");

// 前端小程序调用的统一下单接口
Router.post('/wxpay', (req, res)=>{
  wxpay.pay(req, res);
})

// 微信支付回调方法
Router.post('/wxpay_notify', (req, res) => {
    req.setEncoding("utf8");
    req.on("data", function (xml, call) {
        xmlreader.read(xml, (err, result) => {
            if (err || result.xml.return_code.text() != "SUCCESS") {
                res.json({   
                  "code": "ERROR",
                  "message": err || result.xml.return_msg.text()
                })
            }else {
                // 提取回调数据
                var dt = {
                    nonce_str: result.xml.nonce_str.text(),
                    openid: result.xml.openid.text(),
                    total_fee: result.xml.total_fee.text(),
                    transaction_id: result.xml.transaction_id.text(),
                    out_trade_no: result.xml.out_trade_no.text(),
                    time_end: formatTimeStr(result.xml.time_end.text())
                }
                // do something here ...

                res.json({   
                    "code": "SUCCESS",
                    "message": "成功"
                })
            }
        });
    });
    req.on("end", function (db) {
        db;
    });
})

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值