實現小程序的支付,首先需要去微信官網先了解一下微信小程序支付相關接口文檔: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:
1,
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 ==
200) {
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',
})
}
})