- 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
微信和支付宝的支付应用场景
- APP(Android & IOS)
- 浏览器
- PC端
- Wap端
- 应用浏览器:Chrome、Firefox、Opera……
- 微信内置浏览器
在这里,我们姑且分为:PC端、移动端、微信端,方便分类说明
前端实现原理
-
APP方面,需处理手机系统跳转方式兼容问题。
-
浏览器方面,在PC端和wap端非微信内置浏览器上进行支付宝和微信支付时,前端都只需根据后端接口返回来的路径(get方式URL跳转)或form标签字符串(post方式submit提交)进行跳转即可;
-
而在微信内置浏览器上,则需前端进行特殊处理,因为有PC端微信内置浏览器和移动端微信内置浏览器。
众所周知,不同平台都会对“潜在危险网址”链接进行屏蔽,不允许跳转,比如在微信点击支付宝支付链接,是无法跳转的,反之亦然,所以在前端页面我们需要去做一个中间提示页。
最近,针对“互联网围墙”现象,工信部提出的“拆墙行动”,很明显这场发生在互联网世界中的一场美丽“雪崩”,在座的各位都会是其中或大或小的片片雪花。
支付宝支付
-
浏览器平台
-
PC端:
alipay.trade.page.pay
- 实现效果:跳转官网平台,显示二维码扫码支付
- 参考文档:支付API > 统一收单下单并支付页面接口
- 示例URL
https://openapi.alipay.com/gateway.do?method=alipay.trade.page.pay&app_id=202100210460……
-
Wap端:
alipay.trade.wap.pay
- 实现效果:唤起支付宝 App - 参考文档:支付API > 手机网站支付接口2.0 - 示例URLhttps://openapi.alipay.com/gateway.do?method=alipay.trade.wap.pay&app_id=2021002104604……
-
微信端:
微信内置浏览器中调用支付宝支付时,微信无法直接唤醒支付宝,需要一个引导页作为中间页,提示用户在浏览器中打开,再进行唤醒支付宝应用进行支付
注意,PC端微信内置浏览器
alipay.trade.page.pay
和移动端微信内置浏览器alipay.trade.wap.pay
,支付宝接口的Method是不同的,确保换取支付宝支付路径时提交的参数正确- 微信中使用支付宝支付方案的实现步骤:
- 新建引导页
APGuide.vue
- 添加引导页路由
/apGuide
,并将其设置到不重定向白名单whiteList中,避免登录跳转影响 - 选择支付宝支付的确认页跳转引导页时,先将支付宝跳转路径进行
encodeURIComponent
双重编码或escape
加密,拼接在URL后面作为参数,再用location.href
方式跳转引导页 - 引导页判断是否在微信内置浏览器上,还是已通过微信菜单跳转到浏览器
- 在微信上,则显示引导页内容
- 在PC或Wap浏览器上,则处理URL参数,取出并将支付宝跳转路径
decodeURIComponent
双重解密或unescape
解密,location.href
方式跳转支付宝
- vue示例核心代码:
// RechargeInfo.vue 支付确认页 window.location.href = `${location.origin}/apGuide?url=${encodeURIComponent(encodeURIComponent((res.data.pay_url)))}` // APGuide.vue 引导页 // template <template> <div class="ap-guide-dialog"> <div class="ap-guide-dialog-content"> <div class="J-weixin-tip weixin-tip" ref="weixinTip"> <div class="weixin-tip-content"> 请在菜单中选择在浏览器中打开,<br/> 以完成支付 </div> </div> <div class="J-weixin-tip-img weixin-tip-img" ref="weixinTipImg"></div> </div> </div> </template> // script mounted(){ if (location.hash.indexOf('error') != -1) { alert('参数错误,请检查'); } else { var ua = navigator.userAgent.toLowerCase(); var tip = this.$refs.weixinTip; var tipImg = this.$refs.weixinTipImg; if (ua.indexOf('micromessenger') != -1) { tip.style.display = 'block'; tipImg.style.display = 'block'; if (ua.indexOf('iphone') != -1 || ua.indexOf('ipad') != -1 || ua.indexOf('ipod') != -1) { tipImg.className = 'J-weixin-tip-img weixin-tip-img iphone' } else { tipImg.className = 'J-weixin-tip-img weixin-tip-img android' } } else { let APurl = decodeURIComponent(decodeURIComponent((location.search.split('?')[1].substr(location.search.split('?')[1].indexOf('=')+1)))); window.location.href = APurl; } } },
decodeURIComponent(decodeURIComponent())
两次编码原因: 两次编码传到后台之后,服务器收到参数第一次进行解码之后变为一次utf-8编码的字符串,所以再次调用解码一次就变为中文。
所以,中文参数携带中文需要两次编码,一次编码不能得到正确结果 - 新建引导页
- 微信中使用支付宝支付方案的实现步骤:
-
-
APP:
alipay.trade.app.pay
- 实现方式:APP内嵌H5页面,使用技术与浏览器端一直,主要考虑APP正常跳转返回问题
- 实现效果:唤起支付宝 App
- 参考文档:支付API > app支付接口2.0
- 示例URL
https://openapi.alipay.com/gateway.do?method=alipay.trade.app.pay&app_id=202100210460……
-
知识延伸:
escape
、encodeURI
和encodeURIComponent
的区别:- 作用:
-
escape()
不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值。比如"春节"的返回结果是%u6625%u8282
,escape()
不对+
编码。主要用于汉字编码,现在已不提倡使用。
-
encodeURI()
是JS中真正用来对URL编码的函数。 编码整个url地址,但对特殊含义的符号; / ? : @ & = + $ , #
,也不进行编码。对应的解码函数是:
decodeURI()
。 -
encodeURIComponent()
能编码; / ? : @ & = + $ , #
这些特殊字符。对应的解码函数是decodeURIComponent()
。如果要传递带
&
符号的网址,建议用encodeURIComponent()
。
-
- 应用场景:
- 如果只是编码字符串,与URL无关,那么用
escape
。 - 如果你需要编码整个URL,然后需要使用这个URL,那么用
encodeURI
。 - 当你需要编码URL中的参数的时候,那么
encodeURIComponent
是最好方法。
- 如果只是编码字符串,与URL无关,那么用
- 作用:
微信支付
- 浏览器平台
-
PC端:
- 实现效果:由后端返回二维码或前端实现路径转二维码,从而显示二维码图片扫码支付 - 参考文档:Native下单API
- 示例URL
weixin://wxpay/bizpayurl?pr=OaEfZ……
-
Wap端:
- 实现效果:用
window.location.href
直接跳转后端返回路径,从而唤起微信 App进行支付 - 参考文档:H5微信支付
- 示例URL
https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx081520……&package=31101……
- 实现效果:用
-
微信端:
- 实现效果:微信授权登录后调起支付
- 参考文档: JSAPI调起微信支付
- vue示例代码
created() { // 微信端支付 if (isWx() && !this.params('is_success_auth')) { this.$confirm('请先允许微信授权?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) .then(() => { // 跳转微信授权 this.wxLogin(); }) .catch(() => {}); return; } }, methods: { // v1.6 微信授权 由后端去获取用户openId wxLogin: function () { // 授权后返回的地址 let callbackUrl = location.href; // 授权地址和所带参数 let url = getreqinfo.getreqinfo().apiurl + '/user/wechat/oauth' + '?callback=' + escape(callbackUrl) + '&token=' + window.tools.cache.get('token') + '&service=user'; window.location.href = url; }, // 确认充值 confirmPay() { let _this = this; let ajaxData = { pay_type: Number(this.payType), money: this.rechragePrice, method: 'api.pay.userRecharge', api_trade_type: '', // api交易类型 }; // api_trade_type string 否 api交易类型 h5支付宝或微信移动端浏览器 jsapi微信浏览器, 默认web端 if (isWx()) { // 微信端 if (this.payType == 2) { // alipay // 移动端微信内置浏览器 & PC端微信内置浏览器 ajaxData.api_trade_type = isWapWx() ? 'h5' : ''; } else if (this.payType == 3) { // wx // 移动端微信内置浏览器 & PC端微信内置浏览器 ajaxData.api_trade_type = isWapWx() ? 'jsapi' : ''; } else { ajaxData.api_trade_type = ''; } } else { // 手机端 & pc端 ajaxData.api_trade_type = isAndroid() || isIOS() ? 'h5' : ''; } this.$myAjax({ ajaxData: params, type: 'POST', successFun: res => { this.payRelated.out_trade_no = res.data.out_trade_no; if (isWx() && this.params('is_success_auth')) { WeixinJSBridge.invoke( 'getBrandWCPayRequest', { appId: res.data.js_params.appId, //公众号名称,由商户传入 timeStamp: res.data.js_params.timeStamp, //时间戳,自1970年以来的秒数 nonceStr: res.data.js_params.nonceStr, //随机串 package: res.data.js_params.package, signType: res.data.js_params.signType, //微信签名方式: paySign: res.data.js_params.paySign, //微信签名 }, function (res) { if (res.err_msg == 'get_brand_wcpay_request:ok') { // H.$toast('支付成功'); // 轮询支付结果 // …… } else if (res.err_msg == 'get_brand_wcpay_request:cancel') { window.tools.alert.message('取消支付'); } else { window.tools.alert.error('支付失败'); } } ); return; } // 其他支付类型处理 // …… }, }); }, },
注意支付过程中,轮询订单支付状态,避免出错
- 微信注意问题:
- 如果是调用原生支付,而非如乐刷聚合等第三方支付平台的话,记得在微信公众平台配置js接口安全域名,并下载相应
MP_verify_***
文件,放到项目根目录下。 - 由于微信升级,现在调用微信支付确认后,会自动关闭网站回到微信主界面,想要停留在当前网站,需要开通微信的“点金计划”才行。
- 除非是有跟微信合作从而开放接口的合作开发商外,普通开发者无法通过
weixin://
调起微信后直接跳转网站页面,微信合作伙伴才有weixin://dl/business/?ticket=***
这样的功能。
- 如果是调用原生支付,而非如乐刷聚合等第三方支付平台的话,记得在微信公众平台配置js接口安全域名,并下载相应
-
js页面跳转方式
- form表单提交(post)
- 如果后台接口返回来的是form标签字符串,则用该方式提交跳转
- 示例demo
// 后台接口返回来的是form标签字符串 let _str = ` <form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do' method='POST'> <input type='hidden' name='app_id' value='201805106……'/> <input type='hidden' name='method' value='alipay.trade.page.pay'/> <input type='hidden' name='format' value='JSON'/> // …… <input type='submit' value='ok' style='display:none;''> </form> <script>document.forms['alipaysubmit'].submit();</script> `; _str = _str.replace(/form/, 'form target="_blank"'); div.innerHTML = _str; document.body.appendChild(div); document.forms.alipaysubmit.submit();
- window.location.href
- window.open
- a标签触发click事件
注意:用该方式在安卓和PC端跳转支付都可以,但iOS却无法执行,不建议使用该方式
- ……