微信小程序 + nodeJs(loopback) 实现支付

实现小程序的支付,首先需要去微信官网先了解一下微信小程序支付相关接口文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1

微信支付首先需要调用微信的统一下单接口,返回微信支付接口需要数据。具体参数参考:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1

在后端服务中,提供一个小程序前端调用的订单接口,生成预定单并访问微信统一下单接口返回微信接口数据,具体服务端代码如下:

module.exports = (req, res) => {
// TODO replace the codes bellow and add your own codes here
const wx_app_id = server.get('wx-app-id');
let pay_info = {
[wx_app_id]: { // 定义能支付的商户,可能存在多个
mch_id: server.get('wx-mch-id'),
pay_key: server.get('wx-pay-key')
}
}if(!(req.body.appid in pay_info)) {
res.send({
successed: false,
message: '商户不支持支付',
datalist: '暂不支持支付的appid'
});
return
}
let sendtowx = {
appid: req.body.appid,
mch_id: pay_info[req.body.appid].mch_id,
nonce_str: moment().valueOf(),
body: req.body.body,
// detail: req.body.detail,
// attach: req.body.attach,
out_trade_no: getOrderNo(), //订单在同一商家不能重复
total_fee: req.body.total_fee,
spbill_create_ip: req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress,
notify_url: req.body.notify_url, // 订单支付反馈通知接口,一般做修改订单完成状态用,微信支付成功后会在后台调用
trade_type: 'JSAPI',
openid: req.body.openid
}
sendtowx.sign = wx_sign(sendtowx, pay_info[req.body.appid].pay_key); //生成MD5加密字符,传入字段不能有undefined。否则会报签名错误 let request_str = '<xml>'
for (let item in sendtowx) {
request_str = request_str + '<' + item + '>'
request_str = request_str + sendtowx[item]
request_str = request_str + '</' + item + '>'
}
request_str = request_str + '</xml>'
console.log('request_str:', request_str);
const AppOrder = server.models.AppOrder;
AppOrder.findOrCreate({where:req.body.where},{ // 首先创建系统订单,此时状态为待支付
status:"0",
pay_price: sendtowx.total_fee/100,
order_no: sendtowx.out_trade_no
}, function(err, data, created) {
console.log(data,created,'s;s/');
if (err) {
console.log(err);
} else if(created){
console.log("创建预付订单成功",data);
request.post( // 调用微信支付统一下单接口,返回微信预订单数据
{
url : 'https://api.mch.weixin.qq.com/pay/unifiedorder',
// headers: {
// 'Content-Type':'text/xml; charset=utf-8',
// 'Content-Length': data.length
// },
body: request_str,
rejectUnauthorized: false
}, function(err, httpResponse, body){
// 请求完成之后的回调函数
console.log(body)
parseString(body, {explicitArray:false}, function (err, result) {
let res_body = { }
if(result.xml.return_code == 'SUCCESS') {
res_body.successed = true
res_body.message = 'ok'
let temp = {
appId: result.xml.appid,
timeStamp: moment().format('X'),
nonceStr: moment().format('x'),
package: 'prepay_id='+result.xml.prepay_id,
signType: "MD5"
}
temp.paySign = wx_sign(temp, pay_info[result.xml.appid].pay_key)
result.xml = temp
}
else {
res_body.successed = false
res_body.message = '支付失败'
}
if(res_body.message) {
res_body.datalist = result.xml
res.send(res_body);
}
});
})
}
else {
res.send({
code: "applied",
successed: false,
message: '不能重复提交订单!'
});
}
}); }

经常遇到的问题是会报签名错误问题,排查方法:

1.排查微信支付申请的pay-key是否正确,或写成了appid、app-key;

2.排查在上传MD5签名时是否传入了undefined的字段;

3.如果都没问题,建议在微信商户管理后面中重新设置pay-key。

统一下单结束后,小程序前端调用统一下单接口,开始微信支付:

payNow: function (e) {
let that = this;
let data = {
openid: app.globalData.openId,
appid: app.globalData.app_id,
body: this.data.applyGame.name + "测试",
// attach: this.data.applyGame.id,//附加信息,暂时不添加
total_fee: 1,
notify_url: app.globalData.appUrl + 'sdk/wxpayvnotify', // 微信支付成功反馈通知接口
where:{ // 不能重复提交订单条件
status: orderStatus.pay,
}
};
pay(data, (res) => {
if (res.statusCode == 200) {
if (res.data.successed == true) {
let backobj = res.data.datalist; let timestamp = backobj.timeStamp;
let nonceStr = backobj.nonceStr;
let prepay_id = backobj.package;
let signType = backobj.signType;
let paySign = backobj.paySign;
wx.requestPayment({
'timeStamp': timestamp,
'nonceStr': nonceStr,
'package': prepay_id,
'signType': signType,
'paySign': paySign,
'notify_url': app.globalData.appUrl + 'sdk/wxpayverify',
success: function (res) {
wx.navigateTo({
url: "/pages/my-game/index",
})
wx.switchTab({
url: '/pages/my-game/index',
})
},
fail: function (res) {
console.log(res,'支付失败');
wx.showToast({
title: '支付失败!',
icon: 'none',
})
// wx.navigateTo({
// url: "../pay/payfail/payfail",
// })
},
})
} else {
if (res.data.code === 'applied'){
wx.showToast({
title: '已经报名',
icon: 'none',
})
}else{
wx.showToast({
title: res.data.message,
icon: 'none',
})
} }
}else{
wx.showToast({
title: '支付失败!',
icon: 'none',
})
}
})
}

支付完成。

console.log(e,'s//s/');
let that = this;
// if (!this.data.payNowBottonUse) {//防止按钮重复点击
// return;
// }
// this.setData({
// payNowBottonUse: false
// })
let data = {
openid: app.globalData.openId,
appid: app.globalData.app_id,
body: this.data.applyGame.name + "报名测试",
// body: "pppddpdp",
// detail: JSON.stringify(that.data.payOrdrInfoData.specific_project),//服务id
// attach: this.data.applyGame.id,//附加信息,暂时不添加
// //product_id: "002",//trade_type=NATIVE时(即扫码支付),此参数必传。
total_fee: ,
notify_url: app.globalData.appUrl + 'sdk/wxpayvnotify',
member_id: this.data.memberInfo.id,
user_id: app.globalData.userId,
game_id: this.data.applyGame.id,
where:{ // 不能重复提交订单条件
status: orderStatus.pay,
gameId: this.data.applyGame.id,
memberId: this.data.memberInfo.id
}
};
pay(data, (res) => {
if (res.statusCode == ) {
if (res.data.successed == true) {
let backobj = res.data.datalist;
console.log(backobj,'slslslslls');
let timestamp = backobj.timeStamp;
let nonceStr = backobj.nonceStr;
let prepay_id = backobj.package;
let signType = backobj.signType;
let paySign = backobj.paySign;
wx.requestPayment({
'timeStamp': timestamp,
'nonceStr': nonceStr,
'package': prepay_id,
'signType': signType,
'paySign': paySign,
'notify_url': app.globalData.appUrl + 'sdk/wxpayverify',
success: function (res) {
wx.navigateTo({
url: "/pages/my-game/index",
})
wx.switchTab({
url: '/pages/my-game/index',
})
},
fail: function (res) {
console.log(res,'支付失败');
wx.showToast({
title: '支付失败!',
icon: 'none',
})
// wx.navigateTo({
// url: "../pay/payfail/payfail",
// })
},
})
} else {
if (res.data.code === 'applied'){
wx.showToast({
title: '该棋手已经报名',
icon: 'none',
})
}else{
wx.showToast({
title: res.data.message,
icon: 'none',
})
}
 
}
}else{
wx.showToast({
title: '支付失败!',
icon: 'none',
})
}
})
上一篇:SQL服务器名称更改


下一篇:IIS:错误: 无法提交配置更改,因为文件已在磁盘上更改