egg(110,111,112)--egg之微信支付

微信支付前的準備工做

準備工做

  1. 準備工做:個體工商戶、企業、政府及事業單位。

須要獲取內容

  1. appid:應用 APPID(必須配置,開戶郵件中可查看)
  2. MCHID:微信支付商戶號(必須配置,開戶郵件中可查看)
  3. KEY:API 密鑰,參考開戶郵件設置(必須配置,登陸商戶平臺自行設置)

express支付(測試)

向微信發送帶金額和標題參數的請求

//引入統一下單的api
var wechatPay = require('./module/wechatPay');

var express = require('express');
var bodyParser = require('body-parser');
var xmlparser = require('express-xml-bodyparser');

var app = new express();

//xmlparser
app.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.pnghtml

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 = '<xml> ' +
            '<appid>' + this.config.wxappid + '</appid> ' +
            '<attach>' + obj.attach + '</attach> ' +
            '<body>' + obj.body + '</body> ' +
            '<mch_id>' + this.config.mch_id + '</mch_id> ' +
            '<nonce_str>' + obj.nonce_str + '</nonce_str> ' +
            '<notify_url>' + obj.notify_url + '</notify_url>' +
            '<openid>' + obj.openid + '</openid> ' +
            '<out_trade_no>' + obj.out_trade_no + '</out_trade_no>' +
            '<spbill_create_ip>' + obj.spbill_create_ip + '</spbill_create_ip> ' +
            '<total_fee>' + obj.total_fee + '</total_fee> ' +
            '<trade_type>' + obj.trade_type + '</trade_type> ' +
            '<sign>' + obj.sign + '</sign> ' +
            '</xml>';
        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
<div class="payment-box ">

                    <div class="payment-body">
                        <ul class="clearfix payment-list J_paymentList J_linksign-customize">
                            <li id="weixinPay"><img src="//c1.mifile.cn/f/i/16/pay/weixinpay0701.png" alt="微信支付" style="margin-left: 0;"></li>
                            <li class="J_bank" id="alipay">
                                <a target="_blank" href="/alipay/pay?id=<%=orderResult._id%>"> <img src="//s01.mifile.cn/i/banklogo/payOnline_zfb.png?ver2015" alt="支付寶" style="margin-left: 0;"></a>
                            </li>

                        </ul>
                    </div>
                </div>
<div class="modal fade" id="weixinPayModel" tabindex="-1" role="dialog">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title" id="myModalLabel">微信支付</h4>
                </div>
                <div class="modal-body">
                    <img class="lcode" src="/weixinpay/pay?id=1242141244" />
                    <img class="rphone" src="/public/default/image/phone.png" />
                </div>
            </div>
        </div>
    </div>
$("#weixinPay").click(function() {

                $('#weixinPayModel').modal('show');

            })

效果

把訂單信息經過微信支付js,轉成url,再把url轉成二維碼
clipboard.pnggit

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

支付後跳轉

微信支付後,支付寶支付後

微信支付後不會自動跳轉es6

clipboard.png

流程

5秒鐘判斷一次狀態
clipboard.pngexpress

修改支付狀態和訂單狀態爲1(成功)
clipboard.pngjson

支付狀態和訂單狀態爲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() {

        /*
    
         一、獲取訂單號
        
         二、查詢當前訂單的支付狀態
    
         三、若是支付 返回成功   若是沒有支付返回失敗信息
    
    
        */

        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);
相關文章
相關標籤/搜索