nodejs实现微信支付,koa框架的接入流程,此文档通过测试,在小程序/微信公众号的后端代码中使用, 已经用于生产环境, 如有需要,请放心复制.
根据官方微信支付的文档
和流程图
支付过程可以分为后端流程和前端流程
后端流程
后端分为2步
- 根据用户的下单请求调用微信统一下单api拿到返回的关键数据
prepay_id
export const prepay = async ({openid,orderId,desc,totalPrice,spbill_create_ip})=> {
// 通过查阅文档,调用统一下单有10个参数是必须的
let obj = {
appid,
mch_id,
nonce_str: get_nonce_str(32),
body: desc,
out_trade_no: orderId,
total_fee: parseInt(totalPrice * 100),
spbill_create_ip,
notify_url,
trade_type:'JSAPI',
openid
}
// js的默认排序即为ASCII的从小到大进行排序(字典排序)
let arr = Object.keys(obj).sort().map(item => {
return `${item}=${obj[item]}`;
});
// 这里拼接签名字符串的时候一定要注意: 商户的key是要单独拿出来拼在最后面的
let str = arr.join('&') + '&key=' + key;
// appid=wxf8600b***b5dfb&body=德胜村&mch_id=1490909372&nonce_str=plfbp2bhr0id1z6aktmndfot94hkewcv¬ify_url=https://server.***.cn/wechat/pay_notify&openid=oFm4h0WvnQWB4ocFmdPzsWywlE8c&out_trade_no=20150806125346&spbill_create_ip=127.0.0.1&total_fee=56600&trade_type=JSAPI&key=Lzy1234567890111***5161718192
obj.sign = getSign(str);
let res;
try{
// 调用微信统一下单接口拿到 prepay_id
res = await wechatPay(obj);
let {prepay_id} = res;
if(prepay_id){
res = getClientPayConfig(prepay_id)
}
// console.log(res);
}catch(e){
res = e;
console.log(e);
}
return res;
}
/**
* 统一下单 prepay_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
* @param {Object} obj 调用统一下单的必须参数
*/
const wechatPay = (obj)=>{
let xml = json2xml(obj);
console.log(xml)
return new Promise((resolve,reject)=>{
// 这里用了reques库,不熟悉的同学可以看看相关文档 https://github.com/request/request
// 总之就是向微信的统一下单接口提交一个xml
request({method:'POST',url: prepay_url,body: xml},(err,res, body)=>{
if(err){
reject(err);
}else{
//如果成功即可得到微信返回参数
console.log(body);
let obj = parseXml(body).xml;
resolve(obj);
}
});
});
}
/**
* 对指定字符串进行md5加密
* @param {String} str
*/
const getSign = (str)=>{
console.log(str)
let hash = crypto.createHash('md5').update(str,'utf8');
return hash.digest('hex').toUpperCase();
}
/**
* 转化xml用了xml2js库
https://github.com/Leonidas-from-XIV/node-xml2js
* @param {Object} obj
*/
const json2xml = (obj)=>{
let builder = new xml2js.Builder({
headless:true,
allowSurrogateChars: true,
rootName:'xml',
cdata:true
});
var xml = builder.buildObject(obj);
return xml;
}
const parseXml = (xml)=>{
let {parseString} = xml2js;
let res;
parseString(xml, {
trim: true,
explicitArray: false
}, function (err, result) {
res = result;
});
return res;
}
/**
* 生成指定长度的随机数
* @param {*int} len
*/
const get_nonce_str = (len)=>{
let str = '';
while(str.length < len){
str += Math.random().toString(36).slice(2);
}
return str.slice(-len);
}
- 通过
prepay_id
生成前端调启微信支付界面的必要参数
官方文档
/**
* 生成前端调启支付界面的必要参数
* @param {String} prepay_id
*/
const getClientPayConfig = (prepay_id)=>{
let obj = {
appId: appid,
timeStamp: String(Math.floor(Date.now()/1000)),,
nonceStr: get_nonce_str(32),
package: 'prepay_id=' + prepay_id,
signType: 'MD5'
}
let arr = Object.keys(obj).sort().map(item => {
return `${item}=${obj[item]}`;
});
let str = arr.join('&') + '&key=' + key;
obj.paySign = getSign(str);
return obj;
}
前端流程
前端分为2步 官方文档
- 向后台提交支付订单的请求
- 拿到后台返回参数, 调启支付页面
$api({
method:'POST',
url:'/order',
data: obj,
success:(data)=>{
console.log(data);
/**
* {
appId:"wxf860****03b5dfb"
nonceStr:"o5any3uj14tyfjr8wa249n5s0nnp9rkl"
package:"prepay_id=wx20171104151803201b17a3100900948881"
paySign:"D2632F71E4CB7E9E18D329460FDF5EB0"
signType:"MD5"
timeStamp:"1509779883"
}
*/
let obj = Object.assign({
'success':function(res){
wx.showModal({
title: '提示',
content: '支付成功',
showCancel: false
});
},
'fail':function(res){
wx.showModal({
title: '提示',
content: '取消支付',
showCancel: false
});
}
},data)
//调用小程序支付api,若为网页支付,查看相应文档即可. (若是spa网页支付,有一个支付目录的坑.最粗暴的方式:刷新进入支付页面)
wx.requestPayment(obj)
}
})
签名错误
根据经验 签名错误是xml的加密出错了
这里贴出一个提交统一下单的原始xml
这里说明一下: 经过亲测 spbill_create_ip
, notify_url
这两个参数即使是写死的也不是导致签名错误
的原因
wxf8600b48303b5dfb
德胜村
1490909372
t7z9yb0wa8e5zhcwaw4ovjlrzj39t2xh
https://server.**.cn/wechat/pay_notify
oFm4h0WvnQWB4ocFmdPzsWywlE8c
20150806125346
127.0.0.1
56600
JSAPI
39FD69074F0B184D10CC5E826914785A
遇到签名错误,不要着急,进行以下2步排查,定能解决问题
- 到官方调试界面,输入自己的参数,看看最终的签名是否和自己生成的一致
- 如果签名没错,那肯定是商户信息的问题了(本人此处被坑了很久,老板给了我mch_id和key都是正确的,结果未安装操作证书,导致我调试了很久找不到原因,心中一万匹草泥马)
检查商户信息,也就是商户号mch_id和商户的key(这里需要注意key
,是申请微信支付成功后,腾讯发给申请者邮件里面的秘钥,要想此秘钥生效还需要安装操作证书)