微信支付前的准备工作
准备工作
- 准备工作:个体工商户、企业、政府及事业单位。
需要获取内容
- appid:应用 APPID(必须配置,开户邮件中可查看)
- MCHID:微信支付商户号(必须配置,开户邮件中可查看)
- 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
把url转成二维码
手机扫码支付
egg微信支付(真实)流程
- 调用统一下单接口生成预支付交易,获取 code_url
- 用 code_url 生成二维码
- 支付成功后监听服务器的异步通知,然后处理订单
扫码支付前
微信支付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转成二维码
扫码支付后
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;
}
回调信息
支付后跳转
微信支付后,支付宝支付后
微信支付后不会自动跳转
流程
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);