博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
egg(110,111,112)--egg之微信支付
阅读量:6809 次
发布时间:2019-06-26

本文共 13767 字,大约阅读时间需要 45 分钟。

微信支付前的准备工作

准备工作

  1. 准备工作:个体工商户、企业、政府及事业单位。

需要获取内容

  1. appid:应用 APPID(必须配置,开户邮件中可查看)
  2. MCHID:微信支付商户号(必须配置,开户邮件中可查看)
  3. KEY:API 密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)

express支付(测试)

向微信发送带金额和标题参数的请求

//引入统一下单的apivar wechatPay = require('./module/wechatPay');var express = require('express');var bodyParser = require('body-parser');var xmlparser = require('express-xml-bodyparser');var app = new express();//xmlparserapp.use(xmlparser());app.use(express.static('./public'));//使用中间件body-parser获取post参数  app.use(bodyParser.urlencoded({ extended: false }));app.use(bodyParser.json());app.set('view engine', 'ejs');app.get('/order', function(req, res) {    var openid = '';    var config = {        mch_id: '1502539541',        wxappid: "wx7bf3787c783116e4",        wxpaykey: 'zhongyuantengitying6666666666666'    }    var pay = new wechatPay(config);    pay.createOrder({        openid: openid,        notify_url: 'http://118.123.14.36:8000/notifyUrl', //微信支付完成后的回调        out_trade_no: new Date().getTime(), //订单号        attach: '名称',        body: '购买信息',        total_fee: '1', // 此处的额度为分        spbill_create_ip: req.connection.remoteAddress.replace(/::ffff:/, '')    }, function(error, responseData) {        console.log('11111111');        console.log(responseData);        if (error) {            console.log(error);        }        res.json(responseData); /*签名字段*/    });})app.listen(8000, function() {    console.log('port 8000 is running!');});

回调里有支付url

clipboard.png

把url转成二维码

clipboard.png

手机扫码支付

支付页面有金额和标题信息

clipboard.png

egg微信支付(真实)流程

  1. 调用统一下单接口生成预支付交易,获取 code_url
  2. 用 code_url 生成二维码
  3. 支付成功后监听服务器的异步通知,然后处理订单

扫码支付前

微信支付js封装包

applibwechatPay.js
/* * @Descrition : wechat 微信支付功能 */var url = require('url');var queryString = require('querystring');var crypto = require('crypto');var request = require('request');var xml2jsparseString = require('xml2js').parseString;// wechat 支付类 (使用 es6 的语法)class WechatPay {    /*     构造函数      */    constructor(config) {        this.config = config;    }    /**     * 获取微信统一下单参数     */    getUnifiedorderXmlParams(obj) {        var body = '
' + '
' + this.config.wxappid + '
' + '
' + obj.attach + '
' + '' + obj.body + ' ' + '
' + this.config.mch_id + '
' + '
' + obj.nonce_str + '
' + '
' + obj.notify_url + '
' + '
' + obj.openid + '
' + '
' + obj.out_trade_no + '
' + '
' + obj.spbill_create_ip + '
' + '
' + obj.total_fee + '
' + '
' + obj.trade_type + '
' + '
' + obj.sign + '
' + '
'; return body; } /** * 获取微信统一下单的接口数据 */ getPrepayId(obj) { var that = this; // 生成统一下单接口参数 var UnifiedorderParams = { appid: this.config.wxappid, attach: obj.attach, body: obj.body, mch_id: this.config.mch_id, nonce_str: this.createNonceStr(), notify_url: obj.notify_url, // 微信付款后的回调地址 openid: obj.openid, //改 out_trade_no: obj.out_trade_no, //new Date().getTime(), //订单号 spbill_create_ip: obj.spbill_create_ip, total_fee: obj.total_fee, // trade_type : 'JSAPI', trade_type: 'NATIVE' // sign : getSign(), }; // 返回 promise 对象 return new Promise(function(resolve, reject) { // 获取 sign 参数 UnifiedorderParams.sign = that.getSign(UnifiedorderParams); var url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; request.post({ url: url, body: JSON.stringify(that.getUnifiedorderXmlParams(UnifiedorderParams)) }, function(error, response, body) { var prepay_id = ''; if (!error && response.statusCode == 200) { // 微信返回的数据为 xml 格式, 需要装换为 json 数据, 便于使用 xml2jsparseString(body, { async: true }, function(error, result) { if (error) { console.log(error); reject(error); } else { // prepay_id = result.xml.prepay_id[0]; //小程序支付返回这个 console.log(result); var code_url = result.xml.code_url[0]; resolve(code_url); } }); } else { console.log(body); reject(body); } }); }) } /** * 获取微信支付的签名 * @param payParams */ getSign(signParams) { // 按 key 值的ascll 排序 var keys = Object.keys(signParams); keys = keys.sort(); var newArgs = {}; keys.forEach(function(val, key) { if (signParams[val]) { newArgs[val] = signParams[val]; } }) var string = queryString.stringify(newArgs) + '&key=' + this.config.wxpaykey; // 生成签名 return crypto.createHash('md5').update(queryString.unescape(string), 'utf8').digest("hex").toUpperCase(); } /** * 微信支付的所有参数 * @param req 请求的资源, 获取必要的数据 * @returns {
{appId: string, timeStamp: Number, nonceStr: *, package: string, signType: string, paySign: *}} */ getBrandWCPayParams(obj, callback) { var that = this; var prepay_id_promise = that.getPrepayId(obj); prepay_id_promise.then((prepay_id) => { var prepay_id = prepay_id; var wcPayParams = { "appId": this.config.wxappid, //公众号名称,由商户传入 "timeStamp": parseInt(new Date().getTime() / 1000).toString(), //时间戳,自1970年以来的秒数 "nonceStr": that.createNonceStr(), //随机串 // 通过统一下单接口获取 // "package" : "prepay_id="+prepay_id, //小程序支付用这个 "code_url": prepay_id, "signType": "MD5", //微信签名方式: }; wcPayParams.paySign = that.getSign(wcPayParams); //微信支付签名 callback(null, wcPayParams); }, function(error) { callback(error); }); } /** * 获取随机的NonceStr */ createNonceStr() { return Math.random().toString(36).substr(2, 15); }; //获取微信的 AccessToken openid getAccessToken(code, cb) { var that = this; var getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + this.config.wxappid + "&secret=" + this.config.wxappsecret + "&code=" + code + "&grant_type=authorization_code"; request.post({ url: getAccessTokenUrl }, function(error, response, body) { if (!error && response.statusCode == 200) { if (40029 == body.errcode) { cb(error, body); } else { body = JSON.parse(body); cb(null, body); } } else { cb(error); } }); } /** * 创建订单 */ createOrder(obj, cb) { this.getBrandWCPayParams(obj, function(error, responseData) { if (error) { cb(error); } else { cb(null, responseData); } }); }}module.exports = WechatPay;

config

configconfig.default.js
// 微信支付的配置    exports.weixinPayConfig = {        mch_id: '1502539541',        wxappid: "wx7bf3787c783116e4",        wxpaykey: 'zhongyuantengitying6666666666666'    }    exports.weixinpayBasicParams = {        //注意回调地址必须在  微信商户平台配置        notify_url: "http://video.apiying.com/weixinpay/weixinpayNotify"    }

router

router.get('/weixinpay/pay', initMiddleware, controller.default.weixinpay.pay);

controller

appcontrollerdefaultweixinpay.js
'use strict';const Controller = require('egg').Controller;class WeixinpayController extends Controller {    async pay() {        var d = new Date();        const data = {            title: '辣条111',            out_trade_no: d.getTime().toString(),            price: '0.1'        }        var code_url = await this.service.weixinpay.doPay(data);        //调用方法生成二维码        var qrImage = await this.service.weixinpay.qrImage(code_url);        this.ctx.type = 'image/png';        this.ctx.body = qrImage;    }}module.exports = WeixinpayController;

service

appserviceweixinpay.js
'use strict';var wechatPay = require('../lib/wechatPay.js');const qr = require('qr-image');const Service = require('egg').Service;class WeixinpayService extends Service {    async doPay(orderData) {        return new Promise((resove) => {            var pay = new wechatPay(this.config.weixinPayConfig);            var notify_url = this.config.weixinpayBasicParams.notify_url;            var out_trade_no = orderData.out_trade_no;            var title = orderData.title;            var price = orderData.price * 100;            var ip = this.ctx.request.ip.replace(/::ffff:/, '');            pay.createOrder({                openid: '',                notify_url: notify_url, //微信支付完成后的回调                out_trade_no: out_trade_no, //订单号                attach: title,                body: title,                total_fee: price.toString(), // 此处的额度为分                spbill_create_ip: ip            }, function(error, responseData) {                console.log(responseData);                if (error) {                    console.log(error);                }                resove(responseData.code_url)            });        })    }    async qrImage(url) {        var qrimg = qr.image(url, { type: 'png' });        return qrimg;    }}module.exports = WeixinpayService;

view

appviewdefaultconfirm.html
$("#weixinPay").click(function() {                $('#weixinPayModel').modal('show');            })

效果

把订单信息通过微信支付js,转成url,再把url转成二维码

clipboard.png

clipboard.png

clipboard.png

扫码支付后

router

//异步通知   注意关闭csrf验证    router.post('/weixinpay/weixinpayNotify', initMiddleware, xmlparseMiddleware, controller.default.weixinpay.weixinpayNotify);

config

configconfig.default.js

csrf

config.security = {        csrf: {            // 判断是否需要 ignore 的方法,请求上下文 context 作为第一个参数            ignore: ctx => {                if (ctx.request.url == '/admin/goods/goodsUploadImage' || ctx.request.url == '/admin/goods/goodsUploadPhoto' || ctx.request.url == '/pass/doLogin' || ctx.request.url == '/user/addAddress' || ctx.request.url == '/user/editAddress' || ctx.request.url == '/admin/goods/goodsUploadPhoto' || ctx.request.url == '/weixinpay/weixinpayNotify') {                    return true;                }                return false;            }        }    }

controller

appcontrollerdefaultweixinpay.js
//异步通知  async weixinpayNotify(){        let that = this;         let data = '';                   this.ctx.req.on('data',function(chunk){      data += chunk;    });        this.ctx.req.on('end',function(){        xml2js(data,{explicitArray:false}, function (err, json) {            console.log(json);//这里的json便是xml转为json的内容            var mySign=that.service.weixinpay.weixinpayNotify(json.xml);            console.log(mySign);            console.log('-------------');            console.log(json.xml.sign);        });    });    }

service

appserviceweixinpay.js
weixinpayNotify(params) {        var pay = new wechatPay(this.config.weixinPayConfig);        var notifyObj = params;        var signObj = {};        for (var attr in notifyObj) {            if (attr != 'sign') {                signObj[attr] = notifyObj[attr]            }        }        var sign = pay.getSign(signObj);        return sign;    }

回调信息

clipboard.png

支付后跳转

微信支付后,支付宝支付后

微信支付后不会自动跳转

clipboard.png

流程

5秒钟判断一次状态

clipboard.png

修改支付状态和订单状态为1(成功)

clipboard.png

支付状态和订单状态为1(成功),则跳转到订单页面

clipboard.png

router

//检测订单是否支付    router.get('/buy/getOrderPayStatus', initMiddleware, userauthMiddleware, controller.default.buy.getOrderPayStatus);    router.get(' /user/order', initMiddleware, userauthMiddleware, controller.default.user.order);

controller

appcontrollerdefaultbuy.js
async getOrderPayStatus() {        /*             1、获取订单号                 2、查询当前订单的支付状态             3、如果支付 返回成功   如果没有支付返回失败信息                */        var id = this.ctx.request.query.id;        if (id) {            try {                var orderReuslt = await this.ctx.model.Order.find({ "_id": id });                if (orderReuslt && orderReuslt[0].pay_status == 1 && orderReuslt[0].order_status == 1) {                    this.ctx.body = {                        success: true,                        message: '已支付'                    }                } else {                    this.ctx.body = {                        success: false,                        message: '未支付'                    }                }            } catch (error) {                this.ctx.body = {                    success: false,                    message: '未支付'                }            }        } else {            this.ctx.body = {                success: false,                message: '未支付'            }        }    }

view

appviewdefaultconfirm.html
setInterval(function() {                $.get('/buy/getOrderPayStatus?id=<%=id%>', function(response) {                    console.log(response);                    if (response.success) {                        location.href = '/user/order'                    }                })            }, 5000);

转载地址:http://nhqwl.baihongyu.com/

你可能感兴趣的文章
beta冲刺第六天
查看>>
Spring知识点总结
查看>>
2018年全国卷Ⅰ卷理科数学图片版
查看>>
CF915E Physical Education Lessons(珂朵莉树)
查看>>
bzoj3550: [ONTAK2010]Vacation(单纯形法+线性规划)
查看>>
nodejs.md
查看>>
图文混排的几种实现方案
查看>>
opencv(10)图像变换之边缘检测
查看>>
百度地图的简单使用
查看>>
15个简单算法题
查看>>
JSTL 核心标签库 使用(C标签)
查看>>
L2-004. 这是二叉搜索树吗?
查看>>
socket发送http请求
查看>>
Redis主从配置
查看>>
光耦工作原理
查看>>
bzoj3262
查看>>
BZOJ3925: [Zjoi2015]地震后的幻想乡
查看>>
CSS布局基础
查看>>
C++day02 学习笔记
查看>>
UICollectionView
查看>>