背景: 现在的项目,都需要付款,难免涉及支付宝或者微信支付。如果是支付宝支付,很多人都说,都已经2202年了,支付宝返回的还是form表单,然后,唤起支付宝的界面。
一般来说,支付宝分为前端发起支付,和后台发起支付两种:
①前端发起支付,首先向后台发送订单数据,生成订单。然后,由前端调起手机的支付进行付款;
②如果采用的是,后台发起支付的方式,得到的返回是一个 < form >< /form >的表单html结构,自带< script >里面写的是监听提交按钮事件< /script >标签;
简单来说,由后台发起的支付会返回一个form表单,调用表单中的submit方法唤起支付宝界面。
一、实现思路
首先,明确业务逻辑,支付跳转之前必然有一个提交订单信息【post】的过程,而我们的整个支付表单form是【post】之后返回发结果。返回的字符串结果如下所示:
整理一下,整个的支付流程(此流程,仅针对本公司业务,仅做参考):
①生成token,createToken;
②创建订单createOrder,生成orderCoder(ps:后面获取订单详情需要用到);
③H5通过post接口调用,返回form表单(网上大多数案例,就是指的这一步);
④H5和步骤③同时发起,获取订单详情来轮询支付结果,最后,返回支付成功or支付失败的结果(ps:这个流程,后台调用前端无法拿到支付后的回调,目前,采取轮询调取后台支付状态的一个接口)。
二、相关代码
前端相关代码:
2.1 方法一:使用document.forms[0].submit(),提交表单操作
(1)首先,把表单的内容放在一个页面中:
<div class="aliform" v-html="aliform"></div>
(2)通过document.forms[0].submit(),获取当前页面的第一个表单提交:
<script>
export default {
data() {
return {
aliform: "",
};
},
methods: {
async iosAlipay(orderCode) {
let data = await requestAlipay(orderCode);
if (data.code == 20000) {
this.aliform = data.data; //data.data就是支付宝返回给你的form,获取到的表单内容,具体样子可见上面的图片
this.$nextTick(() => {
// 获取订单详情来轮询支付结果
this.getOrderDetail();
console.log(document.forms); //跳转之前,可以先打印看看forms,确保后台数据和forms正确,否则,可能会出现一些奇奇怪怪的问题 ╮(╯▽╰)╭
document.forms[0].submit(); //重点--这个才是跳转页面的核心,获取第一个表单并提交
});
}
},
// 轮询结果
getOrderDetail() {
//轮询方法,因为支付是跳转到第三方支付宝,我们无法获知用户是否支付成功,或者用户支付成功后是否跳转回来。轮询方法,在一定时间内
clearTimeout(this.timer);
this.timer = setTimeout(() => {
let initTime = +new Date();
let loop = () => {
getOrderDetail({ orderCode: this.orderCode}).then((res) => {
if (res.code == 20000 && res.data && res.data.payStatus == 30) {
//支付成功的相关操作
} else {
let now = +new Date();
if (now - initTime < 45000) {
loop();
} else {
// 超时按照失败处理
//支付失败的结果
}
}
});
};
loop();
}, 500);
},
}
}
</script>
<style lang="less" scoped >
.aliform {
width: 1px;
height: 1px;
opacity: 0;
}
</style>
(3)题外话–了解一下js提供的document.forms方法:
(1)document.forms //表示获取当前页面的所有表单;
(2)document.forms[0] //表示获取当前页面的第一个表单;
(3)document.forms['exportServlet'] //表示获取当前页面的name="exportServlet"的表单
(4)submit() //表示提交函数
2.2 方法二:使用document.write(),就是重定向,覆盖原始页面
\\ 假设result是后端返回的from字符串
const newWindow = window.open('', '_self');
newWindow.document.write(result);
newWindow.focus();
或者,直接使用document.write(xxx),来进行支付跳转
await postUserinfo('接口入参').then(res=>{
document.write(res.data.result)
})
如果是在PC端处理,这种方式是没有问题的,但是需要注意三个问题:
①window.open()在接口回调中触发,可能会被游览器拦截;
②如果本身页面的地址是https的,如果返回的form的action是http的,游览器也会弹出安全提示;
③微信环境window.open()不生效。
2.3 方法三:动态创建div容器,将form渲染进去,js触发form提交
虽然,返回到result的form外面有html包裹,但是,可以动态的创建一个div容器,然后,将包含< html >的result渲染进去,通过js触发form表单的提交。
const div = document.createElement('formdiv');
div.innerHTML = result;
document.body.appendChild(div);
document.forms['cashierSubmit'].setAttribute('target', '_self');
document.forms['cashierSubmit'].submit();
div.remove();
对于H5来说,这种方案兼容性更好。需要注意的是:
一开始,将form的target设置成_blank,导致在ios上的游览器(微信环境,safari,UC)都无法实现form表单的提交。改成_self之后,问题解决。
或者,这样也可:
let divForm = document.getElementsByTagName('divform')
if (divForm.length) {
document.body.removeChild(divForm[0])
}
const div = document.createElement('divform')
div.innerHTML = res.data // res.data就是支付宝返回给你的form
document.body.appendChild(div)
// document.forms[0].setAttribute('target', '_blank') // 加了_blank可能出问题所以我注释了
document.getElementById('alipay_submit').submit()
又或者,试下这样:
//将接口返回的Form表单显示到页面
document.querySelector('body').innerHTML = res.data; // res.data就是支付宝返回给你的form
//调用submit 方法
document.forms[0].submit()
参考博客:
UNI-APP解析支付宝返回FORM表单,唤起支付宝界面 https://www.freesion.com/article/6241970398/
Vue完美解决支付宝返回的form表单问题,这可能是最有效的解决办法了 https://blog.zixutech.cn/archives/324
vue项目中后端返回的支付宝form表单,怎么实现支付跳转? https://blog.csdn.net/qq_45934004/article/details/126156546
Vue自动提交form表单后,自动跳转第三方页面 https://www.jianshu.com/p/e2323b4e2cf9
开发笔记之uniapp 支付宝支付返回form表单解决方案 http://blog.haiya360.com/archives/766.html
vue 支付宝返回url 新窗口打开 https://blog.csdn.net/wax9092/article/details/86631151
H5处理支付宝接口返回form https://www.jianshu.com/p/8c5375671495
vue项目中后端返回的支付宝form表单,怎么实现支付跳转? https://blog.csdn.net/qq_45934004/article/details/126156546
Vue完美解决支付宝返回的form表单问题,这可能是最有效的解决办法了 https://blog.zixutech.cn/archives/324