
微信支付平台配置
在微信支付平台配置支付授权目录。
调用微信支付需要在此目录下。
配置如:https://www.xxx.com/pay/
产品中心->开发配置->支付配置
公众号后台配置
因为我们需要获得用户的openid,所以微信公众号后台也需要做对应的配置。
微信公众号设置网页授权域名,只有在设定的域名下面才能获取到得到openid的必要参数code.
设置与开发->公众号设置->功能设置->网页授权域名
配置如:wap.xxxx.cn/scan/control
在此页面下通过重定向获取code。
获取openid
获取code
在wap.xxxx.cn/scan/control 页面下进行重定向操作,url会自动添加code参数。
vue静默获取code代码
//code是自己设置的变量
//encodeURIComponent是系统自带的encode方法,因为url必须要encode才行
if (!this.code) {
window.location.href =
'https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的微信appid&redirect_uri=' +
encodeURIComponent(
'https://wap.xxx.cn/scan/control?sn=' +
this.sn +
'&name=' +
this.name
) +
'&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect'
} else {
this.code = this.$route.query.code
}
获得openid
Eggjs后台获得openid
const url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=您的appid&secret=您的secret&code=您刚刚获得的code&grant_type=authorization_code"
//发起get请求
const result = await axios(url)
//在data中获得openid
const openid = result.data.openid;
配置微信
获得配置参数
前端发起请求获得配置参数
后端安装wechat-api库
npm i wechat-api --save
const API = require('wechat-api');
const api = new API('appid', 'secret');
var param = {
debug: false,
jsApiList: ['chooseWXPay'],//选择jsapi接口
url: 'https://wap.xxx.cn/'+this.ctx.request.body.path//这里是调用页面的完整地址
};
const data = await new Promise((resolve, reject) => {
api.getJsConfig(param, function (err, result) {
resolve(result)
});
})
//data就是配置参数
初始化配置
前端获得配置参数后进行初始化操作
前端安装weixin-js-sdk库并引用
npm i weixin-js-sdk --save
const wx = require('weixin-js-sdk')
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: result.appId, // 必填,公众号的唯一标识
timestamp: result.timestamp, // 必填,生成签名的时间戳
nonceStr: result.nonceStr, // 必填,生成签名的随机串
signature: result.signature, // 必填,签名
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
})
发起支付
发起支付请求
后端生成订单的参数
这里用到一个工具 wxpay
//attach订单附加信息可以不填
//body订单详情,可以使用json字符串
//mch_id微信支付id,从微信支付后台获取
//openid前面获取到的
//bookingNo自定义的订单号
//total支付金额,以分为单位,只能是整数
//notify_url这个很重要,微信支付后会不停的像这个url发送post请求。
const arg = await new Promise((resolve, reject) => {
try {
wxpay.order(attach, body, mch_id, openid, bookingNo, total, notify_url).then(function (data, err) {
console.dir(err)
resolve(data)
}).catch(function (err) {
console.dir(err)
resolve(err)
});
} catch (error) {
resolve(null)
}
})
获取到的arg返给前端。
前端调用支付
wx.ready(function() {
wx.chooseWXPay({
appId: config.appId,
timestamp: config.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: config.nonceStr, // 支付签名随机串,不长于 32 位
package: 'prepay_id=' + config.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: config.signType, // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
paySign: config.paySign, // 支付签名
success: function(res) {
// 支付成功后的回调函数
if (res.errMsg == 'chooseWXPay:ok') {
// 成功
} else {
//失败
}
},
cancel: function(res) {
this.$notify({
type: 'warning',
message: '您已取消支付'
})
},
// 支付失败回调函数
fail: function(res) {
this.$notify({
type: 'error',
message: '支付失败'
})
}
})
})
回调处理
微信支付比较重要的部分,就是notify_url
微信支付成功后会不停的向这个接口发送post请求,直到得到回应。
回复如下内容即可
this.ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'
在post请求里面我们还能获得支付详细信息,该请求是以xml形式发送的。
eggjs获取xml数据需要进行配置才行,具体配置如下
//config.prod.js或config.default.js
config.bodyParser = {
enable: true,
encoding: 'utf8',
formLimit: '100kb',
jsonLimit: '100kb',
strict: true,
queryString: {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000,
},
enableTypes: ['json', 'form', 'text'],
extendTypes: {
text: ['text/xml', 'application/xml'],
},
};
body中的xml大概如下
// <attach><![CDATA[你好]]></attach>
// <bank_type><![CDATA[CMB_CREDIT]]></bank_type>
// <cash_fee><![CDATA[1]]></cash_fee>
// <fee_type><![CDATA[CNY]]></fee_type>
// <is_subscribe><![CDATA[Y]]></is_subscribe>
// <mch_id><![CDATA[1286586201]]></mch_id>
// <nonce_str><![CDATA[evoi9w8mwev]]></nonce_str>
// <openid><![CDATA[oohmmwJCTH7H6PjcbmayUbthUQNo]]></openid>
// <out_trade_no><![CDATA[ZMDZ_QR_RELAY_1650279755263]]></out_trade_no>
// <result_code><![CDATA[SUCCESS]]></result_code>
// <return_code><![CDATA[SUCCESS]]></return_code>
// <sign><![CDATA[FA5B529FF8497795D477C1BF4533ED3A]]></sign>
// <time_end><![CDATA[20220418190239]]></time_end>
// <total_fee>1</total_fee>
// <trade_type><![CDATA[JSAPI]]></trade_type>
// <transaction_id><![CDATA[4200001322202204181928545592]]></transaction_id>
// </xml>
从xml中提取订单号
//wxpay是上面提到的工具类
const orderStr = wxpay.getXMLNodeValue('out_trade_no', body)
let orderId = ''
if (orderStr) {
const arr = orderStr.split('![CDATA[')
orderId = arr[1].substring(0, arr[1].length - 3)
}
代码示范
前端
第一页,我在这里获取code
<template>
<div @click="goCharge()">
初始页面https://xxxx/scan/control
</div>
</template>
<script>
export default {
data() {
return {
code: '',
}
},
mounted() {
this.code = this.$route.query.code
if (!this.code) {
window.location.href =
'https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appid&redirect_uri=' +
encodeURIComponent(
'https://xxx/scan/control'
) +
'&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect'
} else {
this.code = this.$route.query.code
}
},
methods: {
goCharge() {
if (out == 0) {
this.$router.push(
'/scan/pay?code=' +
this.code
)
}
},
}
}
</script>
正式开始调用微信支付(/scan/pay)
<template>
<div @click="toPay()">
发起支付
</div>
</template>
<script>
const wx = require('weixin-js-sdk')
export default {
data() {
return {
config: null,
code: '',
}
},
mounted() {
this.code = this.$route.query.code
if (this.code) {
//初始化配置
this.initWechat()
} else {
//没有code
this.$alert('非法请求,请重新扫码', '提示', {
confirmButtonText: '确定',
callback: (action) => {
this.$router.go(-1)
}
})
}
},
methods: {
async initWechat() {
//后端获得配置参数
const result = await this.$store.dispatch('user/wechat')
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: result.appId, // 必填,公众号的唯一标识
timestamp: result.timestamp, // 必填,生成签名的时间戳
nonceStr: result.nonceStr, // 必填,生成签名的随机串
signature: result.signature, // 必填,签名
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
})
},
async toPay() {
//code去获取openid,并获得支付参数
const config = await this.$store.dispatch('pay/getWechatPayConfig', {
code: this.code
})
const _this = this
wx.ready(function() {
wx.chooseWXPay({
appId: config.appId,
timestamp: config.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: config.nonceStr, // 支付签名随机串,不长于 32 位
package: 'prepay_id=' + config.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: config.signType, // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
paySign: config.paySign, // 支付签名
success: function(res) {
// 支付成功后的回调函数
// console.dir(res)
alert(res.errMsg)
if (res.errMsg == 'chooseWXPay:ok') {
_this.$notify({
type: 'success',
message: '开始充电'
})
} else {
//失败
}
},
cancel: function(res) {
this.$notify({
type: 'warning',
message: '您已取消支付'
})
},
// 支付失败回调函数
fail: function(res) {
this.$notify({
type: 'error',
message: '支付失败'
})
}
})
})
}
}
}
</script>
后端
// 接口/user/wechat
async wechat() {
const that = this
var param = {
debug: false,
jsApiList: ['scanQRCode','chooseWXPay'],
url: 'https://xxx/scan/pay'//这里是调用页面的完整地址
};
console.dir(param)
const data = await new Promise((resolve, reject) => {
api.getJsConfig(param, function (err, result) {
resolve(result)
});
})
console.dir(data)
that.ctx.body = {
code: 0,
data
}
}
//接口/pay/getWechatPayConfig
async getWechatPayConfig() {
const code = this.ctx.request.body.code || ''
if (code) {
const url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=您的appid&secret=您的secret&code="
+ code + "&grant_type=authorization_code"
const result = await axios(url)
const openid = result.data.openid;
const attach = '无';
const body = JSON.stringify(content);
const mch_id = "微信支付后台获取";
const total = 1;
const bookingNo = "ZMDZ_QR_RELAY_" + new Date().getTime();
const notify_url = "https://xxx/api/v1/pay/notify"; //通知地址
const arg = await new Promise((resolve, reject) => {
try {
wxpay.order(attach, body, mch_id, openid, bookingNo, total, notify_url).then(function (data, err) {
console.dir(err)
resolve(data)
}).catch(function (err) {
console.dir(err)
resolve(err)
});
} catch (error) {
resolve(null)
}
})
this.ctx.body = {
code: 0,
data: arg
}
} else {
this.ctx.body = {
code: -1,
message: '无效的支付'
}
}
}
//接口/api/vi/notify
async notify() {
const body = this.ctx.request.body
//提取订单id,其他提取方式一样
const orderStr = wxpay.getXMLNodeValue('out_trade_no', body)
let orderId = ''
if (orderStr) {
const arr = orderStr.split('![CDATA[')
orderId = arr[1].substring(0, arr[1].length - 3)
}
//回复微信
this.ctx.body = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'
}
// body中的xml内容
// <attach><![CDATA[你好]]></attach>
// <bank_type><![CDATA[CMB_CREDIT]]></bank_type>
// <cash_fee><![CDATA[1]]></cash_fee>
// <fee_type><![CDATA[CNY]]></fee_type>
// <is_subscribe><![CDATA[Y]]></is_subscribe>
// <mch_id><![CDATA[1286586201]]></mch_id>
// <nonce_str><![CDATA[evoi9w8mwev]]></nonce_str>
// <openid><![CDATA[oohmmwJCTH7H6PjcbmayUbthUQNo]]></openid>
// <out_trade_no><![CDATA[ZMDZ_QR_RELAY_1650279755263]]></out_trade_no>
// <result_code><![CDATA[SUCCESS]]></result_code>
// <return_code><![CDATA[SUCCESS]]></return_code>
// <sign><![CDATA[FA5B529FF8497795D477C1BF4533ED3A]]></sign>
// <time_end><![CDATA[20220418190239]]></time_end>
// <total_fee>1</total_fee>
// <trade_type><![CDATA[JSAPI]]></trade_type>
// <transaction_id><![CDATA[4200001322202204181928545592]]></transaction_id>
// </xml>
结束
简单的微信支付对接结束