nodejs+koa2 实现微信支付请求
在开发之前我们先看一下小程序向微信请求支付的业务流程
?第一步,小程序先发起支付请求
小程序首先先向“商家系统后台”发起请求支付,也就是我们自己的后端服务,生成平台订单和签x
第二步,我们的nodejs服务接收到请求之后,处理支付请求,签名后强微信请求支付
let xmlData = await WXPlay.requestWXPay(wxPayInfo);? ->这里是向微信支付服务器请求支付的地方
router.post('/xxxxPay',async (ctx) => {
const v = await new PayFeeValidator().validate(ctx);
let body = ctx.request.body;
let payAmount = Math.floor(parseFloat(body.payAmount))*100);
let orderType = body.orderType;
let content = body.content)?body.content):'支付费用';
let wxPayInfo = {
userId: ctx.auth.uid,
entriesId: body.entriesId,
openId: header.openid,
payAmount: payAmount,
money: body.payAmount,
ip: host,
productIntro: content,
orderType: orderType
}
let xmlData = await WXPlay.requestWXPay(wxPayInfo);
let response = xmlData.xml
if (response.return_code === 'SUCCESS') {
if (response.result_code === 'SUCCESS') {
let timeStamp = createTimeStamp();
let nonceStr = response.nonce_str;
let prepay_id = response.prepay_id;
let appid = global.config.violinWx.appid;
let package = 'prepay_id=' + prepay_id;
let apiKey = global.config.violinWx.payApiKey;
let paySign = getPaySignJs(appid, timeStamp, nonceStr, package, apiKey);
const order = await Order.getOrderDetailByPer(prepay_id);
ctx.body = {
code: 0,
msg: 'SUCCESS',
data: {
timeStamp: timeStamp,
nonceStr: nonceStr,
package: package,
paySign: paySign,
orderNo: order.orderNo
}
}
} else {
ctx.body = {
code: response.err_code,
msg: response.err_code_des
}
}
} else {
ctx.body = {
code: 1,
msg: response.return_msg
}
}
})
我们的服务端拿到微信的支付响应之后,返回给小程序以下参数
timeStamp: 时间戳
nonceStr: 随机字符串
package: 订单详情扩展字符串
paySign: 签名
orderNo: 我们系统的订单号
?signType:?签名方式
小程序拿到这些信心后才开始真正调起支付,代码如下
wx.requestPayment({
timeStamp: res.data.timeStamp.toString(),
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: 'MD5',
paySign: res.data.paySign,
success (result) {
wx.showToast({
title: `支付成功`,
icon: 'none',
duration: 2000
})
// 下面做支付成功的事情
},
fail (result) {
wx.showToast({
title: `您的支付请求失败,请重新支付`,
icon: 'none',
duration: 2000
})
}
})
微信支付成功后下面需要处理的就是微信收到付款后的回调,这里的支付回调接口一定要在微信支付平台配置正确
router.post('/XXXPayNotice', async (ctx) => {
let result = 1;
const wxResponse = ctx.body.xml;
if (wxResponse && wxResponse.return_code[0] === 'SUCCESS') {
if (wxResponse.result_code[0] === 'SUCCESS') {
let timeEnd = wxResponse.time_end[0];
let pattern = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/;
let formateDate = timeEnd.replace(pattern, '$1/$2/$3 $4:$5:$6');
let order = {
orderStatus: 1,
confirmTime: new Date(),
arrivalTime: formateDate,
confirmFlag: 1,
transactionNo: wxResponse.transaction_id[0],
orderNo: wxResponse.out_trade_no[0]
}
const orderR = await Order.getOrderByOrderNo(order.orderNo);
if (orderR) {
let payAmount = Math.floor(parseFloat(orderR.payAmount)*100);
if (payAmount === parseInt(wxResponse.total_fee[0])) {
await Order.updateOrderByWxResponse(order);
result = 1;
} else {
result = 2;
}
if (orderR.orderType === 0) {
let entries = {
entriesStage: 2,
payStatus: 1,
orderNo: order.orderNo,
entriesId: orderR.entriesId
}
if( result === 1) {
await V_Entries.updateEntriesPayInfo(entries);
// 修改用户标记为已支付用户
await V_User.updateIsEnteriesUser(1,orderR.userId);
}
}
if (orderR.orderType === 1) {
// 处理大师班报名
let payInfo = {
payStatus: 1,
orderNo: order.orderNo
}
await T_Student_Class.updatePayInfoByOrderNo(payInfo);
}
} else {
result = 3;
}
} else {
let timeEnd = wxResponse.time_end[0];
let pattern = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/;
let formateDate = timeEnd.replace(pattern, '$1/$2/$3 $4:$5:$6');
let order = {
orderStatus: 2,
confirmTime: new Date(),
arrivalTime: formateDate,
confirmFlag: 1,
transactionNo: wxResponse.transaction_id[0],
orderNo: wxResponse.out_trade_no[0]
}
await Order.updateOrderByWxResponse(order)
}
}
if (result === 2) {
ctx.type = 'application/xml';
ctx.headers[{
'Content-Type': 'text/xml'
}];
ctx.body = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[未找到订单]]></return_msg></xml>';
} else if (result === 3) {
ctx.type = 'application/xml';
ctx.headers[{
'Content-Type': 'text/xml'
}];
ctx.body = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[金额校验失败]]></return_msg></xml>';
} else {
ctx.type = 'application/xml';
ctx.headers[{
'Content-Type': 'text/xml'
}];
ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
})
微信支付成功的通知参数消息体
let wxResponse = {
? ? "appid": ["XXXXX"],
? ? "attach": ["XXXX"],
? ? "bank_type": ["OTHERS"],
? ? "cash_fee": ["1"],
? ? "fee_type": ["CNY"],
? ? "is_subscribe": ["N"],
? ? "mch_id": ["XXXX"],商户号
? ? "nonce_str": ["uNE3NFA0nGPAU7iiOGsuDRkC6qNcNQc8"],
? ? "openid": [""], 支付用户的openID
? ? "out_trade_no": [""],
? ? "result_code": ["SUCCESS"],
? ? "return_code": ["SUCCESS"],
? ? "sign": ["C82D1DE210366E280DBBE6F7D9A3A63A"],
? ? "time_end": ["20200628212320"],
? ? "total_fee": ["1"],
? ? "trade_type": ["JSAPI"],
? ? "transaction_id": [""]
}
?*/
里面用的签名和具体的支付请求,查看这里:签名预支付请求
参数说明,参考微信支付-开发者文档
?
未完待续?
欢迎交流,分享不易,谢谢点赞!
|