node微信扫码支付(一)

YClimb 2019-06-25

版权声明:本文为博主原创文章,未经博主允许不得转载。

概述

目前微信官方的微信支付demo没有node版本,这几天一直折腾微信支付,当做笔记记录下来;
参考资料微信支付官方文档

签名

微信支付的签名采用MD5加密,下面微信支付的签名算法,及统一下单:
首先先建立一个wechatPay.js 文件(在该文件中包括签名算法,统一下单等)

/**
** 引入依赖包
**/
var config = require('../config');
var request = require("request");
var moment = require('moment'),
    _ = require('underscore'),
    https = require('https'),
    fs = require('fs'),
    URL = require('url'),
    CryptoJS = require("crypto-js"),
    jsontoxml = require('jsontoxml'),
    xml2js = require('xml2js');
var crypto = require('crypto');

/**
** 签名算法
**/
var generateSign = exports.genrateSign = function(obj) {

    var tmpObj = {};  // 取非空的key
    _.each(obj, function(value, key){
        if(value) {
            tmpObj[key] = value;
        }
    });
    var keys = _.keys(tmpObj); // key 字典排序
    keys = keys.sort();

    var tmpArr = [];
    _.each(keys, function(key) {
        tmpArr.push(key + "=" + tmpObj[key])
    })
    tmpArr.push("key=" + config.wxmcn.mcnsecret);  // //key为在微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
    var tmpStr = tmpArr.join('&'); // 拼接字符串
    var sign = crypto.createHash('md5').update(tmpStr, 'utf8').digest('hex');
    return sign.toUpperCase();
}

//统一下单,获取code_url后期生成二维码
var paysign = exports.paysign = function(params,callback) {
    if(_.isEmpty(params)) {
        callback('参数不能为空');
    }

    var currentTime = moment().valueOf().toString();
    var nonce_str = [
        config.wxmcn.mcnid,
        currentTime
    ].join('');   //  随机字符串
    var mch_billno = [
        config.wxmcn.mcnid,
        moment().format("YYYYMMDD"),
        moment().unix()
    ].join('');   // 订单号


    var ret = {
        appid: config.H5.appid,
        body: params.body,  // 商品描述
        attach: params.attach,  // 用户的身份intid
        mch_id: config.wxmcn.mcnid,  // 商户号
        nonce_str: nonce_str,  // 随机字符串
        notify_url:params.notify_url,  // 通知地址
        out_trade_no:mch_billno,  // 商户订单号
        spbill_create_ip:params.spbill_create_ip,  //  终端ip
        total_fee:params.total_fee,  // 标价金额
        trade_type:params.trade_type  // 交易类型
    };
    if(!_.isEmpty(params.openid)){
        _.extend(ret,{openid: params.openid});
    }

    var sign = generateSign(ret);
    _.extend(ret,{sign: sign});

    var body = jsontoxml(ret);
    body = '<xml>' + body + '</xml>';
    request({
        url:'https://api.mch.weixin.qq.com/pay/unifiedorder',
        method: 'POST',
        body: body
    },function (err, response, body)
    {
        if (!err && response.statusCode == 200)
        {
           // xml2js将返回xml 转换成json,但是后面都是都数组
            xml2js.parseString(body, function(err, json) {
                if (err) {
                    callback(new Error("解析xml报错"));
                } else {
                    var result = formMessage(json.xml); // 转换成正常的json 数据
                    if(result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
                        callback(null,result);
                    }else {
                        callback(new Error('wechat err'))
                    }
                }
            })

        }
    });
};

// 将xml2js 转换成json数据进一步转换
var formMessage = function(result) {
    var message = {};
    if (typeof  result === 'object') {
        var keys = Object.keys(result);

        for (var i = 0; i < keys.length; i++) {
            var item = result[keys[i]];
            var key = keys[i];
            if (!(item instanceof Array) || item.length === 0) {
                continue;
            }
            if (item.length === 1) {
                var val = item[0];
                if (typeof  val === 'object') {
                    message[key] = formMessage(val);
                } else {
                    message[key] = (val || '').trim();
                }
            } else {
                message[key] = [];
                for (var j = 0, k = item.length; j < k; j++) {
                    message[key].push(formMessage(itemp[j]));
                }
            }
        }
    }
    return message;
}

在router 调用

本次开发用的express框架,创建一个wxpayRouter.js(包括获取微信支付的二维码等)

/*
** 引入依赖
*/
var express = require('express');
var wxPayRouter = express.Router();
var mongoose = require('mongoose'),
    ObjectId = mongoose.Schema.Types.ObjectId;
var wechatPay = require('./wechatpay')

var URL = require('url'),
    async = require('async'),
    _ = require('underscore'),
    qr = require('qr-image'),
    jsontoxml = require('jsontoxml'),
    moment = require('moment');
   

wxPayRouter.get('/order',function(req, res, next){
    var params = URL.parse(req.url, true);
    var queryParams = req.query;
          total_fee = queryParams.total_fee;  // 前端金额
       

    var status = 400,
        errmsg = "";

    if(_.isEmpty(total_fee)) {
        errmsg = 'total_fee is empty';
    }
    if(errmsg){
        return res.end({status: status, msg: errmsg});
    }


    async.auto({
       
        getCodeUrl: function(callback) {
            var condition = {body: '订单名称',attach:'附加信息'};  //  attach 会在支付成功的后的原封不动的返回
            if(!_.isEmpty(total_fee)) {
                _.extend(condition, {total_fee: total_fee});
            }
            _.extend(condition, {spbill_create_ip: tools.getLocalIP(),trade_type: 'NATIVE'});
            // notify_url 为微信回调的地址,将支付成功的信息以数据流的形式返回
            _.extend(condition,{notify_url: 'http://49727c15.ngrok.io/wxpay/orderpay'});

            wechatPay.paysign(condition, function(err, result){
               if(err) {
                   return callback(new Error('connect wechat service is fail'));
               }
               callback(null,result );

            });
        }
    }, function(err, results) {

        if(err) {
            return res.end({status: status, msg: err.message});
        }
        var data = results.getCodeUrl;

        if(_.isEmpty(data)){
            status = 404;
        }else{
            status = 200;
        }

        res.end({status: status, data: data});
    })
});
module.exports = wxPayRouter;

后台微信的支付接口已经完成,前端调用order接口同时需要将支付金额一并传递过来;然后后台返回相关信息,code_url通过第三方工具生成二维码,用户扫码即可完成支付。

相关推荐