微信支付【 WeChatPay
】
WeixinJSBridge.invoke
、wx.chooseWXPay
业务需求:
现在涉及到微信相关的项目有很多,主要涉及到的功能有
【微信自定义分享、微信支付等】
。开发微信公众号H5页面时,需要使用微信支付进行金融支付。当微信支付完成【支付成功 点击“完成”】
时,需要出发相应的业务逻辑;比如:支付成功时跳转到订单页面;支付失败时跳转到下订单页面;支付取消时保持当前订单页面不变等。
前期开发微信H5页面时,大多考虑的就是【支付成功】
之后如何处理,因为是刚接触微信支付,大多都是采用刷新页面获取新数据的方式来更新支付状态。这种方式可以解决当时面临的问题,但是从业务逻辑来看,处理的方式不是很合理。
支付场景越来越多,对微信支付流程有了进一步的了解和认识,现在回过头来对过去项目中使用【微信支付】
的业务场景进行梳理和完善,进一步优化支付流程以及对支付后的不同状态做相应的处理和优化,并结合实际情况做相应的业务逻辑分析。
支付方式:
方式一:微信支付【微信公众号】<——点击进入微信公众号官方文档
此支付方式需要在HTML页面中引入JS文件,即【 jweixin-1.6.0.js 】
;
此JS文件链接支持http
和https
两种形式,引用时需要匹配当前项目的请求-响应协议(即http、https)
;
该JS文件支持使用AMD/CMD
标准模块加载方式加载。
let WeChatPay = function() {
// 2、引入js后、获取公众号校验信息
let timestamp = '',
nonceStr = '',
signature = '';
let v = {
// 用于换取微信校验信息的参数:要求不可以包含 “#” 号
url: location.split('#')[0]
};
// 3、通过config接口注入权限验证配置(需要同步进行,在获取到校验信息后方可注入config,否则校验失败!)
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '', // 必填,签名
jsApiList: ["checkJsApi", "chooseWXPay", "updateAppMessageShareData", "updateTimelineShareData"] // 必填,需要使用的JS接口列表
});
axios.post('/wx/pay/orderPay_XXXX', data).then(res => {
// 支付成功状态
if (res.code == 200) {
// 获取支付必备的参数
let {
nonceStr,
package,
signType,
paySign
} = res.data;
// 4、通过ready接口处理成功验证
wx.ready(function() {
/* 微信支付 */
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: nonceStr, // 支付签名随机串,不长于 32 位
package: package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: paySign, // 支付签名
success: function(res) {
// 前端判断返回方式,微信团队郑重提示:不保证绝对可靠,切记!
if (res.errMsg == 'chooseWXPay:ok') {
// 【支付成功】
} else if (res.errMsg == 'chooseWXPay:cancel') {
// 【支付取消】:用户取消支付不会进入这个判断,而是进入complate和cancel函数
} else {
}
},
complete: function(res) {
// 接口调用完成时执行的回调函数,无论成功或失败都会执行
if (res.errMsg == 'chooseWXPay:ok') {
// 【支付成功】:支付成功提示页面,点击完成按钮之后
wx.closeWindow(); /* 关闭微信窗口,调用时需要在config中进行校验 */
} else if (res.errMsg == 'chooseWXPay:cancel') {
// 【支付取消】
} else {
}
/**
* iOS和Android支付成功点击“完成”后都会进入success和complete函数,都返回'chooseWXPay:ok'
* (也有人说Android支付成功不进入success函数,)
* 原因是【iOS和Android返回数据不同。支付成功后Android返回 {"errMsg":"getBrandWCPayRequest:ok"},iOS返回{"err_Info":"success","errMsg":"chooseWXPay:ok"},故Android找不到success方法,导致失败】
* */
},
fail: function(err) {
// 接口调用失败
},
cancel: function(err) {
// 用户点击取消时的回调函数:用户取消支付后实际上进入cancel 和 complate函数
}
});
});
}
}).catch(err => {
console.log('支付失败:', err);
});
}
接口说明:【不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。】
success
:接口调用成功时执行的回调函数。fail
:接口调用失败时执行的回调函数。complete
:接口调用完成时执行的回调函数,无论成功或失败都会执行。cancel
:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。trigger
: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
方式二:微信支付【 JSAPI 支付
】(常用支付方式)
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": "appId", //公众号名称,由商户传入
"timeStamp": "timeStamp", //时间戳,自1970年以来的秒数
"nonceStr": "nonceStr", //随机串
"package": "package",
"signType": "MD5", //微信签名方式:
"paySign": "paySign" //微信签名
},
function(res) {
// 支付成功
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
// 支付过程中用户取消
if (res.err_msg == "get_brand_wcpay_request:cancel") {
}
// 支付失败
if (res.err_msg == "get_brand_wcpay_request:fail") {
}
/**
* 其它
* 1、请检查预支付会话标识prepay_id是否已失效
* 2、请求的appid与下单接口的appid是否一致
* */
if (res.err_msg == "调用支付JSAPI缺少参数:total_fee") {
}
});
}
// 检测支付环境中的 WeixinJSBridge
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
JSAPI调起支付注意事项:
在微信浏览器里打开H5网页中执行JS调起支付;
WeixinJSBridge
是微信浏览器内置对象,在其他浏览器中无效
;
调用支付传递的参数注意区分大小写
。
WeixinJSBridge.invoke 、 wx.chooseWXPay 的区别
WeixinJSBridge.invoke()
出现的版本更早无需引用jssdk 无需wx.config方法注入
需要参数appId 使用回调 有详细的说明- 而
wx.choosewxpay()
出现的版本比较晚 需要jssdk注入 不需要参数appId 使用回调 只有SUCCESS 和 FAIL没有具体的说明 WeixinJSBridge.invoke()
是微信浏览器的内置方法 其实wx.choosewxpay()
在引用的微信jssdk文件中 也调用了WeixinJSBridge.invoke()
是对WeixinJSBridge.invoke()
的再次封装
综上所诉 这是微信前后设计的不同方法的支付 还是weixinjsbridge更方便一些 有具体的失败回调
uniapp案例
<template>
<view class="donation_amount">
<view class="donation_bg">
<u-navbar title="充值中心" @leftClick="leftClick" :titleStyle="titleStyle" placeholder fixed
bgColor="transparent"></u-navbar>
<view class="recharge-btn"><u-button type="primary" text="立即充值"
@click="$u.debounce(rechargePay, 500)"></u-button>
</view>
<u-modal width="580rpx" :show="payYejShow" @cancel="paygoconfirm" @confirm="paygoconfirm"
:showCancelButton="true" cancelText="取消" confirmText="已完成支付">
<view class=""
style="display: flex;flex-direction: column;align-items: center;justify-content: center;">
<u-loading-icon size="40"></u-loading-icon>
<view class="" style="text-align: center;font-size: 28rpx;color: #666;margin-top: 28rpx;">
请确认微信支付是否已完成
</view>
</view>
</u-modal>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import {
createRechargeOrder,
ipGoodsPay
} from '@/common/api.js';
import {
mapState
} from 'vuex';
export default {
data() {
return {
titleStyle: {
color: '#666'
},
amountList: [],
ratingTageIndex: 0,
value: '微信支付',
amount: '',
rechargeId: null,
rechargeOrderId: '',
wxShow: {},
payYejShow: false,
paySkipShow: false
};
},
computed: {
...mapState([''])
},
onLoad(option) {
if (!!option.paySkipShow) {
this.paySkipShow = option.paySkipShow;
}
},
onShow() {
this.wxShow = window.navigator.userAgent.toLowerCase();
if (window.location.href.indexOf('#reloaded') == -1 && JSON.parse(this.paySkipShow)) {
this.payYejShow = true;
const url = location.href + '#reloaded';
window.history.replaceState(null, '', url);
// this.$router.go(0);
} else {
}
},
methods: {
paygoconfirm() {
this.payYejShow = false;
},
leftClick() {
uni.$u.route({
type: 'redirect',
url: '/pages/IpModule/catch-doll'
});
},
// 创建订单
createRechargeOrderApi() {
if (uni.$u.test.isEmpty(this.rechargeId)) {
this.rechargeId = this.amountList[this.ratingTageIndex].id
}
createRechargeOrder({
id: this.rechargeId
}).then(res => {
if (res.code == 0) {
this.rechargeOrderId = res.result.orderId;
this.toPay();
}
})
.finally(() => {});
},
rechargePay() {
this.createRechargeOrderApi()
},
toPay() {
if (this.wxShow.search(/MicroMessenger/i) > -1) {
this.ipGoodsPayApi('jsapi');
} else {
this.ipGoodsPayApi('h5');
}
},
wxpay({
appId,
timestamp,
nonceStr,
signType,
paySign,
orderdetail
}) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
appId, //公众号名称,由商户传入
timeStamp: timestamp, //时间戳,自1970年以来的秒数
nonceStr, //随机串
package: orderdetail,
signType, //微信签名方式:
paySign //微信签名
},
res => {
if (res.err_msg == 'get_brand_wcpay_request:cancel') {
// timer = null;
this.$refs.uToast.show({
message: '支付失败',
type: 'error',
duration: '2000'
});
}
if (res.err_msg == 'get_brand_wcpay_request:ok') {
// this.$u.vuex('$dollOrderId', this.dollOrderId);
}
}
);
},
ipGoodsPayApi(payMethod) {
uni.showLoading({
title: '支付中...',
mask: true
});
ipGoodsPay({
orderId: this.rechargeOrderId,
orderSource: 8,
payMethod: payMethod,
})
.then(res => {
if (res.code == 0) {
if (this.wxShow.search(/MicroMessenger/i) > -1) {
let wxJsApiParam = res.result.jsApi;
this.wxpay(wxJsApiParam);
} else {
let tourl = '';
const hrefUrl = window.location.href;
hrefUrl.indexOf('test') != -1 ?
(tourl = encodeURIComponent(
`https://xxx.xxx.xxx.com/xxx/xxx?orderId=${this.rechargeOrderId}&paySkipShow=true`
)) :
(tourl = encodeURIComponent(
`https://xxx.xxx.xxx.com/xxx/xxx?orderId=${this.rechargeOrderId}&paySkipShow=true`
));
this.$u.vuex('$payjump', {
url: '/pages/secondary/vouche-center'
});
if (res.code == 0) {
window.location.href = `${res.result.payUrl}&redirect_url=${tourl}`;
// this.$u.vuex('$dollOrderId', this.rechargeOrderId);
} else {
this.$refs.uToast.show({
message: res.resultMsg,
type: 'error',
duration: '2000'
});
// uni.$u.route({
// type: 'redirect',
// url: '/pages/secondary/exchange-record'
// });
}
}
}
})
.finally(() => {
uni.hideLoading();
});
},
}
};
</script>
<style lang="scss" scoped>
::v-deep .u-button--primary {
background: #F75348 !important;
border: none !important;
border-radius: 16rpx;
}
.donation_bg {
width: 100%;
background: #FFCFCC;
position: relative;
}
.donation_amount {
.recharge-btn {
width: 100%;
height: 120rpx;
position: fixed;
bottom: 0;
left: 0;
box-sizing: border-box;
padding: 0 30rpx;
}
.recharge-remark {
margin: 20px;
// margin-top: 520rpx;
font-size: 24rpx;
color: #999999;
.title {
font-size: 28rpx;
}
}
}
</style>