
在微信开发中,最复杂的莫过于微信支付流程了,前端、服务端、微信平台要通过一系列的交互才能成功支付,官方 文档也不是很友好,下面这张图就是来自官方文档的,是不是感觉特别复杂。

看了半天,绕来绕去的,虽然很规范,但却不容易理解,下面给大家上一张简单粗爆一点的。

什么?你还是看不懂,那我只能上代码了。
首先是客户端页面代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>微信支付功能</title>
<!-- 引入JQuery -->
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<!-- 引入JS-SDK -->
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<!-- 引入手机调试工具 -->
<script src="http://wechatfe.github.io/vconsole/lib/vconsole.min.js?v=3.2.0"></script>
<script>
// 全局域名地址
var URL = 'http://www.xiaoduanfa.com'
// 手机调试工具
var vConsole = new VConsole();
// 获取code的方法
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return decodeURI(r[2]);
return null;
};
// 获取code
var code = getQueryString('code');
// 获取openid
$(function () {
// 获取openid
$.get(URL + '/getOpenId?code=' + code, function (response) {
var openid = response.openid;
wxInit(openid)
})
});
// 获取配置信息初始化JS-SDK
function wxInit(openid) {
var param = { 'cmd': 'get_js_config', 'url': kk };
// 发送请求获取config
$.ajax({
url: URL + '/wechat',
type: 'POST',
data: param,
datatype: 'json',
success: function (data) {
// 配置JS-SDK
wx.config(data);
// 微信JS-SDK加载成功
wx.ready(function () {
// 点击扫码
document.getElementById('#scan').onclick = function () {
wx.scanQRCode({
// 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
needResult: 1,
// 可以指定扫二维码还是一维码,默认二者都有
scanType: ["qrCode", "barCode"],
success: function (res) {
var result = res.resultStr;
alert(result);
}, fail: function (err) {
alert(JSON.stringify(err))
}
});
};
// 点击支付
document.querySelector('#pay').onclick = function () {
// 根据openid获取支付参数
$.get(URL + '/order/?openid=' + openid, function (response) {
wx.chooseWXPay({
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。
// 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
"timestamp": response.timeStamp,
"nonceStr": response.nonceStr,
"package": response.package,
"signType": response.signType,
"paySign": response.paySign,
success: function (res) {
// 支付成功后的回调函数
alert(JSON.stringify(res));
}
});
// 支付的安全域名配置错误 choosewxpay:fail
// 出现此错误以后,用IOS手机调试,这时候会告诉你具体错误
})
};
});
// 微信JS-SDK加载失败
wx.error(function (res) {
alert(JSON.stringify(res));
// config信息验证失败会执行error函数,
// 如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,
// 也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
}
})
}
</script>
</head>
<body>
<button id="scan">扫一扫</button>
<button id="pay">支付</button>
</body>
</html>接下来是服务端的代码。
// 安装wechat-api
// cnpm install wechat-api --save
// 引入封装的全局配置信息
var Config = require('./module/config');
// 引入封装支付模块
var wechatPay = require('./module/wechatPay');
// 引入wechat-api
var API = require('wechat-api');
var express = require('express');
var bodyParser = require('body-parser');
// 引入express-xml-bodyparser让node.js可以接收xml格式请求
var xmlparser = require('express-xml-bodyparser');
// 实例化配置
var api = new API(Config.wxappid, Config.wxappsecret)
var app = new express();
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');
// 生成config的参数
app.post('/wechat', function (req, res, next) {
var param = {
debug: false,
jsApiList: [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'onMenuShareQZone',
'hideMenuItems',
'showMenuItems',
'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem',
'translateVoice',
'startRecord',
'stopRecord',
'onVoiceRecordEnd',
'playVoice',
'onVoicePlayEnd',
'pauseVoice',
'stopVoice',
'uploadVoice',
'downloadVoice',
'chooseImage',
'previewImage',
'uploadImage',
'downloadImage',
'getNetworkType',
'openLocation',
'getLocation',
'hideOptionMenu',
'showOptionMenu',
'closeWindow',
'scanQRCode',
'chooseWXPay',
'openProductSpecificView',
'addCard',
'chooseCard',
'openCard'
],
url: req.body.url
};
api.getJsConfig(param, function (err, result) {
res.send(result);
});
});
// 获取首页路由
app.get('/', function (req, res) {
res.render('index');
});
// 获取openid返回客户端
app.get('/getOpenId', function (req, res) {
var code = req.query.code;
var pay = new wechatPay();
pay.getAccessToken(code, function (err, data) {
res.json(data);
})
});
// 生成订单
app.get('/order', function (req, res) {
var openid = req.query.openid;
var pay = new wechatPay();
pay.createOrder({
openid: openid,
// 微信支付完成后的回调
notify_url: Config.url + '/notifyUrl',
// 订单号
out_trade_no: new Date().getTime(),
attach: '名称',
body: '购买信息',
// 此处的额度为分
total_fee: '1',
// 处理node.js的异常情况
spbill_create_ip: req.connection.remoteAddress.replace(/::ffff:/, ''),
}, function (error, responseData) {
if (error) {
console.log(error);
};
res.json(responseData);
});
})
// 获取微信支付的签名
app.post('/notifyUrl', function (req, res) {
var pay = new wechatPay();
var notifyObj = req.body.xml;
var signObj = {};
for (var attr in notifyObj) {
if (attr != 'sign') {
signObj[attr] = notifyObj[attr][0]
}
};
var obj = pay.getSign(signObj);
res.json(obj);
})
app.listen(8002); 在上面的代码中我,我引入了两个封装的模块,一个是全局配置文件,另一个是支付一系列的API模块。
以下是引入config.js代码。
var config={
url:'http://www.xiaoduanfa.com',
mch_id: '1502539541',
wxappid: "wx7bf3787c783116e4",
wxappsecret:'cca407db4d8459c907d2c59274ca6d17',
wxpaykey: 'zhongyuantengitying6666666666666'
};
module.exports=config;以下是封装的微信支付相关的API,大家可以复制粘贴拿去复用,如有问题,后果自负,哈哈!
var queryString = require('querystring');
var crypto = require('crypto');
var request = require('request');
var xml2jsparseString = require('xml2js').parseString;
// 引入项目的配置信息
var config = require('./config.js');
// wechat 支付类
class WechatPay {
constructor() {
};
// 获取微信统一下单参数
getUnifiedorderXmlParams(obj) {
var body = '<xml> ' +
'<appid>' + config.wxappid + '</appid> ' +
'<attach>' + obj.attach + '</attach> ' +
'<body>' + obj.body + '</body> ' +
'<mch_id>' + 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: config.wxappid,
attach: obj.attach,
body: obj.body,
mch_id: config.mch_id,
nonce_str: this.createNonceStr(),
// 微信付款后的回调地址
notify_url: obj.notify_url,
openid: obj.openid,
out_trade_no: obj.out_trade_no,
spbill_create_ip: obj.spbill_create_ip,
total_fee: obj.total_fee,
trade_type: 'JSAPI',
// 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) {
prepay_id = result.xml.prepay_id[0];
// 放回数组的第一个元素
resolve(prepay_id);
});
} else {
reject(body);
}
});
})
};
// 获取微信支付的签名
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=' + config.wxpaykey;
// 生成签名
return crypto.createHash('md5').update(queryString.unescape(string), 'utf8').digest("hex").toUpperCase();
};
// 获取微信支付的所有参数
getBrandWCPayParams(obj, callback) {
var that = this;
var prepay_id_promise = that.getPrepayId(obj);
prepay_id_promise.then(function (prepay_id) {
var prepay_id = prepay_id;
var wcPayParams = {
// 公众号名称,由商户传入
"appId": config.wxappid,
// 时间戳,自1970年以来的秒数
"timeStamp": parseInt(new Date().getTime() / 1000).toString(),
// 随机串
"nonceStr": that.createNonceStr(),
// 通过统一下单接口获取
"package": "prepay_id=" + 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 getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + config.wxappid + "&secret=" + 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;