*只有前端部分
1.首先data需要储存几个值:
payUrl: "", // 订单支付跳转链接
isFetchCode: false, //正在生成订单
payStatus: "", // 订单状态,用于判断是否还需要继续请求订单支付结果接口
userDown: false, // 用户点击已支付完成按钮
timer: null, // 轮询计时器
goZfbSrc: null, // 用于解决ios系统下safari浏览器不支持window新开窗口的计时器
以及其他订单进行中弹窗、支付成功弹窗等状态管理
2.用户点击支付后,请求后端接口,拿到支付宝跳转链接:
// 点击购买,获取商品支付地址async showQrCode() {if (this.isFetchCode) return;this.isFetchCode = true;try {const { code, msg, data } = await api.createOrder({...传参});if (code === 0 && data) {const { jump_url, orderno, create_dt } = data;// 获取订单编号及下单时间this.orderno = orderno;this.createTime = create_dt;this.payUrl = jump_url;// 关闭下单弹窗this.payVisible = false;// 打开订单进行中弹窗this.codeVisible = true;// 支付宝支付if (this.payType === "alipay_mobile") {// 微信浏览器中选择支付宝支付,弹窗复制支付链接,提示用户在外部浏览器打开进行支付const browser = window.navigator.userAgent.toLowerCase();if (browser.match(/MicroMessenger/i) == "micromessenger") {this.copyVisible = true;} else {// 跳转支付宝this.payStatus = "waiting";this.pollOrder();this.goZfbSrc = setTimeout(() => {window.open(jump_url, "_blank");clearTimeout(this.goZfbSrc);});// window.location.replace(jump_url);// const a = document.createElement("a");// a.setAttribute("href", jump_url);// a.setAttribute("target", "支付");// document.body.appendChild(a);// a.click();// document.body.removeChild(a);// const form = document.createElement("form");// form.action = jump_url;// form.target = "_blank";// form.method = "POST";// document.body.appendChild(form);// form.submit();// const tempPage = window.open("_blank");// tempPage.location = jump_url;// window.open(jump_url, "_ blank");}} else {// 微信支付this.$toast("微信支付");// this.initiatePayment();}} else if (code === 87) {// 限购,打开限购弹窗this.$refs.fail.visible = true;} else if (code === 83) {this.$toast("订单生成失败,请重新选择商品购买,如有疑问请咨询客服查询");} else {this.$toast(msg || "获取失败");}} catch (err) {//} finally {this.isFetchCode = false;}},
肉眼可见为了这个该死的sarari尝试了多少种方案= =无论是创建a标签手动触发点击事件,还是通过创建表单通过提交表单的方式拉起,还是直接使用window.open开新窗口,都不可以,最终是利用了setTimeout这个利器才解决这个问题。
而safari不允许的原因也很简单,它默认开启了阻止弹出窗口的策略,禁用了通过代码调用超链接在新标签打开页面的功能,避免window.open被滥用。但这个阻止弹出窗口的操作,并不是完全禁止,而是会根据用户的行为来判断这次window.open()是否属于流氓操作。
所以,聪明的程序员利用setTimeout运行于主线程的这个特点,把打开链接的操作放到setTimeout里运行即可,这样就不会被浏览器认定为代码操作,避免了被拦截。
谢谢你,大佬。(虎目含泪)
2.实现拉起app之后,接着就是页面轮询了:
// 间隔刷新订单支付状态pollOrder() {this.timer = setInterval(() => {if (this.payStatus === "waiting" && this.codeVisible) {// 未完成且订单进行中的弹窗未关闭,则继续请求this.queryPayStatus();} else {// 已成功完成/关闭弹窗,清空计时器clearInterval(this.timer);this.timer = null;}}, 3000);},
// 查询支付状态async queryPayStatus() {try {const { code } = await api.orderPolling({orderno: this.orderno,});if (code === 0) {this.payStatus = "success";this.codeVisible = false;this.sucVisible = true;clearInterval(this.timer);} else if (code === 22) {this.payStatus = "";this.$toast("订单已失效,请重新购买");this.codeVisible = false;clearInterval(this.timer);} else {// 如果是用户手动查询,弹出状态提示if (this.userDown) {this.$toast("订单未完成支付,请确认支付状态");this.userDown = false;}}} catch (err) {//} finally {//}},
3.以及手动点击查询:
// 用户点击刷新支付状态userSearchStatus() {this.userDown = true;this.queryPayStatus();},
那么简单的h5页面拉起支付宝支付就完成鸟~