前端H5微信支付宝支付实现
- 以uniapp项目为例
- 支付宝的
- 微信的
- 先说外部浏览器的情况,在立即支付之后去判断一下浏览器是什么,返回false就走下面代码,调接口就完了
- 接下来是微信内置浏览器支付,首先需要一个(jweixin-module),下载这个依赖npm、yarn都行
以uniapp项目为例
支付宝的
首先是一个支付类型选择页面,在选择支付宝支付后,跳转到一个空白页,用于支付宝支付的中转页面。
在点击立即支付之后,直接跳转至自行设置好的空白页就好,并把你生成订单所需要的数据一并带过去。
uni.navigateTo({url: `/pages/alipaying/alipay" />${this.orderNum}&price=${this.orderinfo.price}`,});
接下来就是重点操作了(其实非常简单),在跳转这个页面之后直接调用后端接口,将数据传给后端生成订单,并且还需要两个地址,一个是取消支付的回调地址(quitUrl),一个是支付完之后的回调地址(returnUrl),后端会根据这些信息生成一个form表单返回给你,我们只需要将这个表单渲染到这个空白页面,并进行提交表单的操作,就会跳转至支付宝的页面去进行支付操作,在取消或支付完成会跳转至之前传给后端规定好的地址,在那里去调后端接口查询支付结果或者重新支付等其他操作就行了。
空白页面内容如下:
<template><!-- #ifdef H5 --><div v-html="html"></div><!-- #endif --></template><script>import api from "../../utils/api";//这个utils是对一些东西做了处理,其实没必要,utils.getStorage直接用uni.getStorage一样的import utils from "../../utils";export default {onLoad(options) {this.orderNum=options.orderNumthis.price=options.pricethis.couponId = options.couponId},mounted(){const result = utils.getStorage("weixinCallback");if(result!=3){//这是弄了个变量来判断是否已经进入了支付,防止用户直接左滑的返回一直在触发支付this.alipay()}},data() {return {orderNum:"",price:"",html:"",couponId:""};},methods: {async alipay(){//这里是获取支付宝返回的表单用的接口,那两个地址我在api里面写了默认值,所以这个地方就不传了,接口详细内容就和普通调接口一样,该传传,该接接const res = await api.alipayOrder(this.orderNum,this.price,this.couponId)//将表单渲染进页面document.querySelector('body').innerHTML = res;//在渲染完立即提交表单,就会进入支付宝支付的界面this.$nextTick(()=>{utils.setStorage("weixinCallback", 3);window.document.forms[0].submit()})}},};</script>
不过这里的实现支持正常浏览器h5,但是微信内置浏览器的话会提示在浏览器打开该链接,非常的繁琐,查了一下午解决方案,就是没有解决方案(可能也是我没找到),索性直接微信内置浏览器的话就不显示支付宝支付,反正需求很奇怪,都打开微信了还要支付宝干啥,对吧
微信的
微信支付比支付宝支付稍微麻烦一点点,但也不难,对于前端来说调接口就完事了。
微信的话有两种,一种外部浏览器,一种微信内置浏览器
所以首先需要一个方法来判断浏览器的类型
isWeiXin(){let ua = window.navigator.userAgent.toLowerCase();if (ua.match(/MicroMessenger/i) == 'micromessenger') {this.payway.forEach(item=>{if(item.value==1){item.flag = false}})return true;} else {return false;}},
是微信内置就返回true,不是返回false
先说外部浏览器的情况,在立即支付之后去判断一下浏览器是什么,返回false就走下面代码,调接口就完了
//把生成订单需要的数据传给后端,然后后端会返回一个地址const res2 = await api.weixinPayOrder(this.orderNum,this.couponId);const name = "_self";//因为微信支付没有传什么回调地址,在支付完之后还会回到这个页面,所以防止重复触发这里设置一个值来控制utils.setStorage("weixinCallback", 1);//打开后端返回的地址,就可以去微信支付了window.open(res2.mweb_url, name);
在页面展示的时候onShow钩子,判断weixinCallback这个值来执行不同的操作,如果是1说明已经发起过支付,那么需要一个执行另一个方法来调用查询支付结果的接口来跳转页面,下面是查询结果的方法
getOrderDetail() {clearTimeout(this.timer);this.timer = setTimeout(() => {let initTime = +new Date();let loop = () => {//这里调用后端给的查询支付结果的接口,api.weixinPayOrderBack(this.orderNum).then((res) => {if (res.status == 1) {//支付成功跳转uni.navigateTo({url: `/pages/payWeixinSuccess/payWeixinSuccess?orderNum=${this.orderNum}&result=yes`,});} else {let now = +new Date();if (now - initTime < 45000) {loop();} else {//支付失败跳转uni.navigateTo({url: `/pages/payWeixinSuccess/payWeixinSuccess?orderNum=${this.orderNum}&result=no`,});}}});};loop();}, 2000);},
这样其实就完成了外部浏览器的微信支付前端部分,可能还有不足或bug,至少现在我没发现。
接下来是微信内置浏览器支付,首先需要一个(jweixin-module),下载这个依赖npm、yarn都行
在调用isWeiXin()为true时,表示是微信内置浏览器
首先,调用微信官方提供的链接获取用户授权信息code
//应用的appidconst appId = "wxxxxxxxxxxxx";//重定向的地址,重定向回当前页面const local = window.location.href//打开微信官方提供的链接,传入appid和回调地址,在用户确认授权后会在回调地址后面拼接上codewindow.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+appId+'&redirect_uri=' + encodeURIComponent(local) + '&response_type=code&scope=snsapi_base&state=1#wechat_redirect';
在授权后回到这个页面看看地址上有没有code,有则进行下一步
写一个方法取出地址里的code
getUrlParam(name) {var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')let url = window.location.hreflet search = url.split('?')[1]if (search) {var r = search.substr(0).match(reg)if (r !== null)return unescape(r[2])return null} elsereturn null},
判断当前地址有没有code,有则准备进行支付操作,没有则去到让用户授权
const code = this.getUrlParam("code")if(code==null || code == ""){//这里走让用户授权的操作}else{//这里走支付的操作,下面详写}
在else中,使用前面下载的依赖(jweixin-module)来走支付流程
//这里是将code传给后端来获取openIdconst res2 = await api.sendCode(code);//然后将后端给的openId和订单信息继续给后端,在后端返回支付所需的一些参数在依赖中需要配置//这两个接口后端可以合并成一个的吧,我不太理解为啥分成两个,繁琐const res3 = await api.weixinPayOrder(this.orderNum,res2.data);//下面就是使用这个依赖包了,具体配置什么意思网上复制来的,一查一大堆jweixin.config({//初始化配置debug: false, // 是否开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: appId, // 必填,公众号的唯一标识timestamp: res3.timeStamp, // 必填,生成签名的时间戳nonceStr: res3.nonceStr, // 必填,生成签名的随机串signature:res3.paySign, // 必填,签名,jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表});jweixin.ready(function() {jweixin.chooseWXPay({//下面这些参数都是在上面接口返回给我的timestamp: res3.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符nonceStr: res3.nonceStr, // 支付签名随机串,不长于 32 位package: res3.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)signType: res3.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'paySign: res3.paySign, // 支付签名success: function(res) {// 支付成功后的回调函数uni.showToast({icon: 'none',title: '支付成功',duration: 4000});uni.navigateTo({url: `/pages/payWeixinSuccess/payWeixinSuccess?orderNum=${this.orderNum}&result=yes`,});},cancel: function(r) {},fail: function(res) {console.log('payfail')}});});jweixin.error(function(res) {uni.showToast({icon: 'none',title: '支付失败了',duration: 4000});uni.navigateTo({url: `/pages/payWeixinSuccess/payWeixinSuccess?orderNum=${this.orderNum}&result=no`,});});
走到这里h5的微信支付也完成了,其实大部分逻辑在后端,前端只管调接口就完事了,不明白接口什么意思可以去问问后端就行,是不是非常简单,如果看官方文档那样详细的流程图其实很多流程在后端走了,想要更多的学习的话那就去掌握后端知识咯