wx.request 向服务器请求支付参数
服务器根据客户端的 sessionId , 找到 它的 openId, 将它和商家自己的 appId, appSecret, PAY_API_KEY和商品的一系列等信息加密, 去请求微信的服务器
微信的服务器向商家服务器返回这次交易需要的数据
商家服务器处理上述数据, 返回给用户端
用户端接收到数据, 调用 wx.requestPayment 接口
微信服务器主动向商家的服务器发送本次支付的信息(是否成功等)
二次签名和重要,对于第一次开发支付的童靴们来说,是一个很大的坑,千万要注意二次签名。
var express = require('express');
var router = express.Router();
var request = require('request');
const axios = require('axios')
const md5 = require('md5')
const xml2js = require('xml2js')
const xmlParser = new xml2js.Parser()
const appId = '你的appid'
const appSecret = "你的appSecret"
// 商户号
const mchId = 'xxxxxx'
// 支付的 key
const PAY_API_KEY = 'fernTpqRaPDjuW1qRBqZaly7Bh6DMFJH'
var app = express();
//解决跨域问题
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
var nonce_str;//在统一下单时生成的唯一的随机字符串
var out_trade_no;//在统一下单时生成的唯一的随机订单号
// var sign=querySign(appid,mch_id,nonce_str,out_trade_no);//查询签名,传参需要注意
router.post('/secc', function (req, res, next) {
var query = req.body;
console.log("post请求:参数", query);
var formData = "";
formData += ""+appId+""; //appid
formData += ""+mchId+""; //商户号
formData += ""+nonce_str+""; //随机字符串,不长于32位。
formData += ""+out_trade_no+"";
formData += ""+sign+"";
formData += "";
var sign=querySign(appid,mch_id,nonce_str,out_trade_no);//查询签名,传参需要注意
var regUrl= "https://api.mch.weixin.qq.com/pay/orderquery";
axios.post(regUrl, formData).then(wxResponse => {
// 微信返回的数据也是 xml, 使用 xmlParser 将它转换成 js 的对象
console.log(wxResponse);
}).catch(err => {
console.log('post wx err', err)
res.send('bbbbbbbb');
})
});
router.get('/', function (req, res, next) {
// attach 是一个任意的字符串, 会原样返回, 可以用作一个标记
const attach = 'GJS-ORG'
const productIntro = '预见FAN-会员中心'
const notifyUrl = '支付完成回调url'
// 一个随机字符串
const nonceStr = getNonceStr()
nonce_str = nonceStr
// 用户的 openId
const openId = req.query.openid
// 生成商家内部自定义的订单号, 商家内部的系统用的, 不用 attach 加入也是可以的
const tradeId = getTradeId(attach)
out_trade_no = tradeId
// console.log();
// 这里是在 express 获取用户的 ip, 因为使用了 nginx 的反向代理, 所以这样获取
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
ip = ip.match(/\d+\.\d+\.\d+\.\d+/)[0]
const price = 1
// 生成签名
const sign = getPrePaySign(appId, attach, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price)
// 将微信需要的数据拼成 xml 发送出去
const sendData = wxSendData(appId, attach, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price, sign)
// res.send('respond with a resource');
// 使用 axios 发送数据带微信支付服务器, 没错, 后端也可以使用 axios
axios.post('https://api.mch.weixin.qq.com/pay/unifiedorder', sendData).then(wxResponse => {
// 微信返回的数据也是 xml, 使用 xmlParser 将它转换成 js 的对象
console.log(tradeId);
xmlParser.parseString(wxResponse.data, (err, success) => {
// res.send(success);
if (err) {
console.log('parser xml error ', err)
res.send('aaaaaaa');
} else {
if (success.xml.return_code[0] === 'SUCCESS') {
const prepayId = success.xml.prepay_id[0]
const payParamsObj = getPayParams(prepayId, tradeId)
// 返回给前端, 这里是 express 的写法
res.json(payParamsObj)
console.log(payParamsObj);
} else {
if (err) {
console.log('axios post error', err)
res.sendStatus(502)
} else if (success.xml.return_code[0] !== 'SUCCESS') {
res.sendStatus(403)
}
}
}
})
}).catch(err => {
console.log('post wx err', err)
res.send('bbbbbbbb');
})
});
// 预定义的一些工具函数
function getNonceStr() {
var text = ""
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for (var i = 0; i < 16; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
return text
}
function getTradeId(attach) {
var date = new Date().getTime().toString()
var text = ""
var possible = "0123456789"
for (var i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
var tradeId = 'ty_' + attach + '_' + date + text
return tradeId
}
function getPrePaySign(appId, attach, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price) {
var stringA = 'appid=' + appId +
'&attach=' + attach +
'&body=' + productIntro +
'&mch_id=' + mchId +
'&nonce_str=' + nonceStr +
'¬ify_url=' + notifyUrl +
'&openid=' + openId +
'&out_trade_no=' + tradeId +
'&spbill_create_ip=' + ip +
'&total_fee=' + price +
'&trade_type=JSAPI'
var stringSignTemp = stringA + '&key=' + PAY_API_KEY
// console.log(stringSignTemp+"一次签名");
var sign = md5(stringSignTemp).toUpperCase()
// console.log(sign+"第一次生成签名");
return sign
}
function getPaySign(appId, nonceStr, package,signType,timeStamp) {
var stringA = 'appId=' + appId +
'&nonceStr='+nonceStr+
'&package=' + package+
'&signType=' + signType+
'&timeStamp=' + timeStamp
// console.log(stringA);
var stringSignTemp = stringA + '&key=' + PAY_API_KEY
// console.log(stringSignTemp+"二次签名");
var sign = md5(stringSignTemp).toUpperCase()
// console.log(sign+"第二次生成签名");
return sign
}
// 查单签名
function querySign(appid,mch_id,nonce_str,out_trade_no) {
var stringA = 'appid=' + appid +
'&mch_id='+mch_id+
'&nonce_str=' + nonce_str+
'&out_trade_no=' + out_trade_no
// console.log(stringA);
var stringSignTemp = stringA + '&key=' + PAY_API_KEY
// console.log(stringSignTemp+"二次签名");
var sign = md5(stringSignTemp).toUpperCase()
// console.log(sign+"第二次生成签名");
return sign
}
function wxSendData(appId, attach, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price, sign) {
const sendData = '' +
'' + appId + '' +
'' + attach + '' +
'' + productIntro + '' +
'' + mchId + '' +
'' + nonceStr + '' +
'' + notifyUrl + '' +
'' + openId + '' +
'' + tradeId + '' +
'' + ip + '' +
'' + price + '' +
'JSAPI' +
'' + sign + '' +
''
return sendData
}
function getPayParams(prepayId, tradeId) {
const nonceStr = getNonceStr()
const timeStamp = new Date().getTime().toString()
const package = 'prepay_id=' + prepayId
const signType = 'MD5'
const paySign = getPaySign(appId, nonceStr, package,signType,timeStamp,)
// 前端需要的所有数据, 都从这里返回过去
const payParamsObj = {
nonceStr: nonceStr,
timeStamp: timeStamp,
package: package,
paySign: paySign,
signType: 'MD5',
tradeId: tradeId,
}
return payParamsObj
}
module.exports = router;