笔者近期的需求中有微信支付的流程,遇到了不少坑,所以做个记录,避免以后再踩坑。
1. 知己知彼,微信支付种类
当产品经理说“其他团队也支持微信支付,我们也支持吧。”微信H5支付也被列在了需求单上。
这时候你就要注意需求单上的付款场景是否考虑得不够周全。
微信支付常见类型:
这次笔者的要求是H5支付,客户端也内嵌了H5支付,IOS客户端是苹果原生的IAP支付。
开发前未考虑
2. 开发前的准备
以下重点请参考微信支付开发文档。
开发前你需要有一个商户账号,可以在微信公众平台注册并通过认证,同时还需要一个微信公众号,用于微信内支付。
通过认证后,即可在微信公众平台申请开通微信支付。
公司一般已经完成以上两个步骤,只要找到对应的人进行联系即可。
开通H5支付权限
进入微信支付商户平台->产品中心->开发配置->H5支付,设置一般10分钟内生效,开通后在“支付配置”中添加H5支付域名,注意:域名必须通过ICP备案,域名填写格式不包含或。
激活支付权限
支付依赖微信公众号,需要先进行企业认证再进行网页授权,微信公众号需在微信公众平台单独登录和设置。
进入微信支付商户平台->产品中心->开发配置->支付,设置一般在5分钟内生效。
4.1 设置支付目录
4.2 设置网页授权域名
如果看不到网页授权,请检查公众号是否开通了企业认证,服务号必须通过微信认证。
图中“下载文件”必须放在线上服务器的网站根目录下,否则无法添加域名。
以上基本配置一定要配置好,不然很容易出一些问题。特别是网页授权这一步,经常看不到这个配置。首先公众号一定要企业认证。然后域名一定要写完全正确,最好自己审核一下再让别人添加,很容易出错。授权域名数量一般是有限制的,我们这次开发过程中就遇到了需要换域名,数量刚好达到上限,只能把原来的删掉,再添加新的域名,然后就遇到各种奇怪的事情了。请大家仔细看完以上内容。
3.基础工具开发调试遇到的问题
注意:微信更新到7.0后,抓包公众号会出现证书问题,抓包小程序无法直接打开
你不用到处找,也不用怀疑人生,你没问题,没问题,没问题,就是因为微信更新了,不再从本地手机获取证书了。
7.0以下版本,无论微信版本如何,都会信任系统提供的证书
版本7.0以上,微信版本7.0以下,微信会信任系统提供的证书
.0以上版本、微信7.0以上版本,微信只信任自身配置的证书列表
上面的规则都是抄别人的,但亲身经历确实如此,让我怀疑人生了。主要原因是前一天在Win7上设置没问题,第二天就换了系统,微信刚好自动更新,突然抓包不了了。我各种怀疑(这家伙被夹在交火中了),人生各种怀疑找不到原因。只想问你怕不怕,被骗了...
也就是说如果你想抓包调试,必须使用 7以下的手机,或者微信7.0以下的版本。
开发环境,安卓手机微信无法打开网站链接(测试小姐姐说她的测试机可以,我的不可以),微信开发者工具可以打开,但是链接无法跳转,按照上面的教程,微信降级到7.0以下版本,测试无效;安卓7.0以下版本没试过。
开发环境微信里打不开网站链接,没法调试支付。问了后台大佬,他也不知道,说只能在线上调试(他对在线调试表示很担心)。怎么办?怎么办?我急得头发都掉了好多。安卓测试机连不上,下意识以为IOS也不行。
解决
无意中在iOS上尝试打开,发现在测试环境下确实可以打开链接,不过有个条件,就是手机只能连接DNS,不能连接代理(可用于抓包工具)。
因此在开发调试的时候,只能借用一台IOS测试机。
4.H5支付
官网提醒H5支付不建议在APP端使用,但在这个需求中,却用在了安卓APP端,开发中遇到了不少坑,体验大打折扣。
需要
本次的需求是点击付费商品的购买按钮,跳转到支付订单页,在支付订单页发起微信支付。成功则返回支付订单页,提示购买成功,1秒后跳转到商品详情页,改变页面风格;失败则返回支付订单页,提示支付失败;取消支付则返回支付订单页,提示取消支付。(支付也是一样,不过需求单里没写,这次购买也包含积分购买,所以考虑的点比较多,还有账户内支付,这次没来得及做。)
开发文档亮点
参考开发文档,以下是重点:
我们先来了解一下H5支付的回调页面,正常情况下用户支付完成后会返回到发起支付的页面,如果需要返回到指定的页面,可以添加参数指定回调页面。
注意:
上述的跳出操作,区分了5秒内的操作和5秒后的操作,表现效果是完全不一样的,如果没有提示框提示支付是否成功的话,订单查询操作很难稳定进行。
需求PK(一把苦泪)
官方文档建议你返回页面,让用户点击一个按钮来触发订单查询操作。
但是我的请求表单是直接在返回后提示结果的,这样页面跳转回来时就无法准确知道支付流程结果了。
在开发过程中我被5秒内和5秒后不同的表现折磨着,经过漫长的开发过程依然达不到想要的效果,导致开发过程中出现各种问题。
元旦那天我加班了两天,有情有义的劝说产品团队,付款后一定要增加付款确认页面,不然无法付款。产品团队最终因为我的不断加班才更改了这个要求。于是,我终于在元旦最后一天加班(晚上10点以后)完成了这个支付流程的开发。
再次感谢产品小姐。
发展
这个需求的难点在于如何监控微信支付成功或者取消支付之后返回到支付订单页面,从而起到一些提示的效果。
(一),
首先想到的是这个原生事件,参考 MDN,当其标签内容变为可见或隐藏时,会在文档上触发 ( ) 事件。有一个库 vue-- 可以使用。
但它存在一些问题:
原生的事件兼容性不太好,没法使用。即使用了vue--,效果也好不到哪里去。还有,在客户端上,如果5秒内微信操作完成,是能看到触发效果的,但是5秒之后就看不到了,需要借助客户端的帮助告诉网页才能出现。即便借助,在第一次进入支付订单页的时候也能看到触发弹窗。在页面中主要看网页的加载状态,如果加载慢就看不到(还没加载完就已经结束了),如果加载快就看到不想看到的提示了。
除了从微信支付页面返回时可以监控到页面之外,从后台切换回来时页面依然可以监控到,所以需要考虑的情况相对比较多。因为没有支付确认弹框,支付成功5秒内返回页面和5秒后取消支付的表现是不一样的。考虑到和IOS的环境,以及多终端不同浏览器的兼容性,奇怪的现象数不胜数。
最终放弃了这个计划。
(二),。
二维码的支付是在支付订单页进行累计,支付订单页是单独一个页面,进入微信页面再返回支付页面还会继续累计,因此大家可以自行在微信上做文章。(以下简称微信)
从商品进入支付订单页,由于支付订单页是新页面,所以值为1,进入微信页面,加1,最后返回订单页。
所以理论上只要判断变成2,就会弹出确认付款的弹框。
理想很丰满,现实却很骨感,奇怪的事情还是会发生,下面是手机浏览器的测试表现。
由于不同浏览器的表现不同,无法预测未经测试的浏览器的表现如何,所以这个解决方案也被通过了。
(三)参数标志
H5支付回调页面,正常情况下,用户支付完成后会返回发起支付的页面,如果需要返回指定页面,可以添加参数指定回调页面。
如果没有支付确认弹窗,支付返回时页面表现会不一样,所以需要一个支付确认弹窗来发送查单操作判断是否支付完成。触发弹窗的操作放在生命周期里,不指定的话会回到最开始发起支付的页面,因为是老页面,所以钩子不能再触发,所以可以指定,并且加上参数=1,表示从微信页面返回后跳转到新的页面,这样通过判断=1来触发钩子打开支付确认弹窗。
computed:{ // 从微信支付成功,取消返回 isRedirectFromWechat() { const is_redirect = location.search.match(/is_redirect=(\d+)/); return !!(is_redirect && is_redirect[1] === "1"); } }, // 从微信支付页面返回,因为通过redirect_url返回,所以会是一个新页面,能够触发created钩子 created() { if(this.isRedirectFromWechat) { this.$modal.show(); // TOOD } }
此解决方案可以稳定打开付款确认弹窗。
但也有一些副作用:
因此,我们可以针对付款确认弹出框采取一些措施。
点击“未支付”,取消确认弹框,依然停留在订单支付页面。然后只需要 .go(-2); 即可完美返回原支付订单页面,没有任何副作用。点击“已支付”,发送订单查询请求,成功则提示“支付成功”,1秒后返回商品详情页,失败则提示“支付失败”,依然停留在支付订单页面。“支付失败”操作和步骤1一样,成功则延时一定时间(给时间),然后关闭当前页面。
// 延迟跳转到商品详情页 async delayToPage(options) { await sleep(TIMEOUT_DELAY); closeCurrentPage(options); }
关闭当前页面的代码可以一起封装起来。
// 关闭浏览器页面 const closeWindow = () => { if (window.close) { window.close(); return; } window.opener = null; const t = window.open('', '_self', ''); t.close(); } // 关闭当前页面 const closeCurrentPage = ({ isSuccess } = {}) => { // 客户端 if (isPlatform() ) { // 现金支付成功,直接关闭支付订单页,返回商品详情页 if (isSuccess) { window.location.href = 'schema://close_webview'; } /** * 安卓微信取消支付后的效果,微信支付成功返回后则不处理 * 安卓的支付订单页是一个webview,最下面是订单页,中间是微信支付页,上面是跳转回来的订单页 * 现金操作跳转到微信支付 `window.location.href = mweb_url;` history 加1; * 跳转到微信页面,history再加1; * 取消微信支付,重定向到支付页面,history再加1,则总的为3; * 通过history.go(-2)能跳转到最初的支付页面。 */ else { history.go(-2); } return; } // h5 浏览器 支付成功,直接关闭支付订单页,返回商品详情页 if (isSuccess) { closeWindow(); return; } // 其他乱七八糟的浏览器,直接回退两步 history.go(-2); };
至此微信h5支付关键代码已经完成。
(四)、
我在做这件事的时候,同事建议我也记录一下状态的变化。我还没有测试过,但有机会我会试试的。
5.付款
微信内支付依赖微信公众号,配置请参考第二部分。
开发步骤如下:
引导用户进入授权页面同意授权并获取code
在钩子函数中,触发。
const getCode = (url) => { // 获取code const redirect_uri = url || window.location.href; let state = parseInt(Math.random() * 1000); // APP_ID 公众号中以wx开头的那一串代号 const path = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`; window.location.replace(path); };
网页授权交换码
将上一步获取到的code值(URL参数中获取)传递给后端处理,前端就会发生跨域。
获取代码后,请求以下链接:/sns//...
经过一系列的处理之后,后端得到签名涉及的参数:,,,,,。
然后前端调用下面的方法在微信浏览器中调用支付。
function onBridgeReady(){ // WeixinJSBridge 为微信的内置对象,可在微信浏览器中直接使用 WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入 "timeStamp":"1395712654", //时间戳,自1970年以来的秒数 "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串 "package":"prepay_id=u802345jgfjsdfgsdg888", "signType":"MD5", //微信签名方式: "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 }, function(res){ // get_brand_wcpay_request:cancel 支付过程中用户取消 // get_brand_wcpay_request:fail 支付失败 if(res.err_msg == "get_brand_wcpay_request:ok" ){ // 使用以上方式判断前端返回,微信团队郑重提示: //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。 } }); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); }
支付有回调函数,所以支付的效果非常可控。微信支付成功后,只需 .go(-1); 即可返回到商品详情页。如果取消或者失败,则继续停留在当前页面。
6. 开发过程中遇到的问题
问题类型和原因可以查看官方文档,遇到的问题无非就是配置问题,请参考第二点,还有一些是配置错误导致的问题,其他问题请直接联系后端开发小伙伴。
只要第二步配置正确,开发就很顺利了。否则,你遇到的大部分问题都是由于配置不正确造成的。