最近接手了一个vue单页面应用的售票商城,就不可避免的要用到支付,主要讲下微信支付做的过程中遇到的哪些坑。
首先微信支付分为2部分。一个是在微信环境下的支付可以直接调用微信方法。一个是微信浏览器外的H5支付。
第一步,到支付页时判断用户当前的环境是否在微信浏览器下(用户在商品下单跳到支付页时,在支付页组件created或mounted生命周期里判断)
let ua = navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == 'micromessenger') {}
这2段代码就是用来判断微信环境的。
这2段代码也可以在用户点击的时候判断。我放在生命周期这里的原因是需要判断支付宝在微信环境中不能使用
所有业务需要,先在生命周期里判断,然后渲染dom。
第二步,区分环境之后,先说微信浏览器的支付。
调用微信WeixinJSBridge内置对象,详细可看微信官方文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
Pay (data) {
let vm = this
if (typeof WeixinJSBridge === 'undefined') {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', vm.onBridgeReady(data), false)
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', vm.onBridgeReady(data))
document.attachEvent('onWeixinJSBridgeReady', vm.onBridgeReady(data))
}
} else {
vm.onBridgeReady(data)
}
},
onBridgeReady (data) {
let self = this
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
'appId': data.appId,
'timeStamp': String(data.timeStamp), //这里必须要转换为字符串。ios跟android表现不同。后台返回的是数值,但是微信方面必须要json参数都是字符串形式,android会自动转换成字符串(当时我在这里也找了很久的博文才知道的)
'nonceStr': data.nonceStr,
'package': data.package,
'signType': data.signType,
'paySign': data.paySign
},
function (res) {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
self.$router.replace({name: 'paymentend'})
} else if (res.err_msg === 'get_brand_wcpay_request:cancel') {
self.$vux.toast.text('支付取消!', 'default')
} else {
self.$vux.toast.text('支付失败!', 'default')
}
}
)
}
这个2个方法是调起支付。再调起之前需要做2件事。
首先你需要获取用户微信授权,获得openId,当然这个过程是后台处理的。前端需要获得到code传递给后台然后返回openId给你。
if (!this.GetQueryString('code')) {
this.$vux.toast.text('微信授权中……', 'default')
let currentUrl = encodeURIComponent(window.location.href)
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx70fed7a16aebcd2e&redirect_uri=' + currentUrl + '&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'
} else {
this.code = this.GetQueryString('code')
// localStorage.setItem('user_code', this.code)
this.getWeixinOpenID({
code: this.code
})
}
GetQueryString (name) {
let url = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
let newUrl = window.location.search.substr(1).match(url)
if (newUrl != null) {
return unescape(newUrl[2])
} else {
return false
}
}
直接上代码。GetQueryString 这个方法是截取当前url上参数值(比如&code=123, GetQueryString (code)获得123)
那么什么时候获取这个code呢。我个人一开始是在支付页的时候授权获得code,后来因为业务需要。在用户登录之后进入路由跳中间页微信授权登录
router.beforeEach((to, from, next) => {
if (checkLogined()) {
let ua = navigator.userAgent.toLowerCase()
let userinfos = JSON.parse(localStorage.getItem('jwt'))
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
if (!userinfos.openId && to.path !== '/login' && to.path !== '/author') {
localStorage.setItem('beforeLoginUrl', to.fullPath)
next({path: '/author'})
return false
}
}
}
if (to.meta.checkLogin) {
if (checkLogined()) {
next()
} else {
Vue.$vux.toast.text('您还没未登录', 'default')
next({path: '/login', query: {Rurl: from.fullPath}})
}
} else {
next()
}
})
localStorage.setItem(‘beforeLoginUrl’, to.fullPath) 这个段主要保存登录之后前往的路由地址。当授权成功之后再接口跳转到这个路由下
[GETWEIXINOPENID_SUCCESS] (state, openId) {
state.openId = openId
let userinfos = JSON.parse(localStorage.getItem('jwt'))
userinfos.openId = openId
localStorage.setItem('jwt', JSON.stringify(userinfos))
router.push(localStorage.getItem('beforeLoginUrl'))
localStorage.removeItem('beforeLoginUrl')
}
我的接口都是写在vuex上的。这里获得的openId必须存到本地用户数据里。因为如果用户有授权过。登录之后有带openID的数据的。没有授权过的用户是没有的。
这样下次路由跳转的时候就不会再进行授权了
好了到了这里可以说成功了一半。接下来是支付提交,支付提交之前你需要获得微信签名,
let ua = navigator.userAgent.toLowerCase()
let orderId = JSON.parse(localStorage.getItem('orderId')).orderId
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
this.weixinFlog = false
let u = navigator.userAgent
let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
if (isiOS) {
if (sessionStorage.getItem('flag')) {
sessionStorage.removeItem('flag')
} else {
sessionStorage.setItem('flag', true)
location.reload()
}
}
this.$nextTick(() => {
this.weixinjspay({
orderId: orderId,
openId: this.userinfos.openId
})
})
}
直接在下单之后的支付页生命周期里调用接口获得签名数据,这里你需要对ios特别处理一下。 因为ios跟Android表现不同
https://blog.csdn.net/sky_beyond/article/details/54312433 具体哪里不同可以参考这里。我的处理方法是ios进入支付页时刷新一次。这里的处理方法我看到的有2种,一种是我这样的,另一种是#前加?,查了一些资料说#前加?的不太友好而且具体操作相关的没有找到。我就采取了刷新的方法。对用户而言没影响,感受不出来。,这样处理之后安卓跟ios的支付安全域名一样了。
到这里差不多就结束了,点击支付的时候调用pay方法,传入获得的签名参数就可以了。支付回调相信你们都明白的这里就不提了。如果有报什么错误。在回调里打alert(JSON.stringify(res))会弹出具体报错信息。报错信息处理可以到官方文档查阅。
然后微信浏览器外的h5支付。这里另外抱怨一下,我当初做的时候,因为接口有问题,找了很久的资料查没有成功的问题原因。结果是接口有问题。所以有时候多跟后台沟通很重要。(PS:这个项目因为是私活,所以沟通比较困难,一度奔溃)。
其实h5支付很简单。前端不需要做太多事情。
let url = '&redirect_url='+encodeURIComponent('http://wap.singlook.net/#/home/rushbuy/payment/paymentend')
window.location.href = data.mweb_url + url
这里直接贴上代码。 通过后台接口,后台会传回来一个mweb_url的地址你直接跳转就可以唤起微信并支付
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4具体看开发文档。我就不把文档里的摘出来了。
这2段代码 第一段是我的回调地址。支付成功或取消支付都会回跳这个地址。
这里回报2个常见错误。
一个是网络环境未能通过安全验证,请稍后再试,这个错误找后台就行了。后台处理,要么后台的代码问题要么是服务器代理问题。
第二个是商家参数格式有误,请联系商家解决,
这个问题是我最头疼的。官方文档的解释是:
当前调起H5支付的referer为空导致,一般是因为直接访问页面调起H5支付,请按正常流程进行页面跳转后发起支付,或自行抓包确认referer值是否为空
如果是APP里调起H5支付,需要在webview中手动设置referer,如(
Map extraHeaders = new HashMap();
extraHeaders.put(“Referer”, “商户申请H5时提交的授权域名”);//例如 http://www.baidu.com ))
因为我是手机浏览器里打开的网页,所以就看问题1,referer为空导致。当时真的是翻遍了所有的资料都没找出问题所在。最后是项目带头人在我的index.html上看到了这么一句, 我当时就是一张黑人问号脸。我什么时候写过这一句代码? 一直也没往这方面想,所以一直想不通为什么整个项目路由都没带上referer,本以为单页面应用的原因。所以大家如果碰到这个问题。首先先看下index.html上是否带有这句话,我深刻怀疑是脚手架vue-cli下过来的时候自带上的!!!!