tuy

虚拟公交卡小结(支付宝容器jsBridge异步注入、WAF防火墙、地图Lbs等问题)

一:前言

支付宝和微信是现在移动端的大平台,这几年来杭州市民卡一直在微信中建设迭代传统卡的线上服务,把繁琐的线下流程直接搬到线上运前言:支付宝和微信是现在移动端的大平台,这几年来杭州市民卡一直在微信中建设迭代传统卡的线上服务,把繁琐的线下流程直接搬到线上运行,实时关注用户数突破200万,大大提高了杭州市民的用卡体验。今年六月份也在支付宝中上线了虚拟公交卡、虚拟社保卡等项目,实现了公交和医疗两大领域的无卡化目标。由于微信平台的开放比较早,开发文档也比较详细,之前完成的微信项目中,除了城市服务和微信团队有过技术对接,其它项目参考官网文档,基本能满足需求,但这次在支付宝容器中完成的虚拟公交卡项目,还是就遇到了一些小坎坷,记录下来,避免下次踩坑.   

二:过程

1、【文档比较保守】:AlipayJSBridgeReady对象API对外相对保守,所有在支付宝容器的H5页面,当window.onload以后,容器会异步初始化,产生一个全局变量AlipayJSBridge(必须注入之后再调用)。使用AlipayJSBridge的好处是不用额外引用js文件了。但是在完成整个虚拟卡开发中,以下方法是无法在对外文档中找到的(setTransparentTitle、hideBackButton、showBackButton),他们分别是设置标题透明度和显隐返回按钮。咨询支付宝相关技术后,他们坦言目前公开的都是一些基本方法,当遇到无法解决的时候可以咨询支付宝内部人员帮忙查看是否有支持的容器方法。内部方法以后也会逐步开放。

2、【组件封装异步调用】:针对常用的ui组件,toast(弱提示)、alert(强提示,需要用户手动关闭)、loading(加载中提示),直接使用AlipayJSBridgeReady的话,只要等到注入成功就能调用支付宝原生的native组件,用户体验接近原生。但是这样就会变成强依赖支付宝的应用了,到时候切到其它平台无法调用支付宝的组件又要重新修改代码。最后采用了一个兼容的方案,首先判断是否存在AlipayJSBridgeReady对象,有就唤起原生,无就采用h5自己的ui实现。代码如下:

var UI = {
	  //显示loading
      showIndicator:function(){
	 	if (typeof(AlipayJSBridge) != "undefined") {
			AlipayJSBridge.call('showLoading', {
	  			text: '加载中',
			});
			return;
		}else{
		    myApp.showIndicator();
		}     
      },
	  //隐藏loading
      hideIndicator:function(){
	 	if (typeof(AlipayJSBridge) != "undefined") {
			AlipayJSBridge.call('hideLoading');
			return;
		}else{
		    myApp.hideIndicator();
		}     
      },
      //弹框alert
      alert:function(message,callback){
	 	if (typeof(AlipayJSBridge) != "undefined") {
			AlipayJSBridge.call('alert', {
			  message: message,
			}, function(e){
			  callback && callback();
			});
			return;
		}else{
		    myApp.alert(message,callback);
		}     	
      },
      //alert
      toast:function(content,duration,type){
	 	if (typeof(AlipayJSBridge) != "undefined") {
			AlipayJSBridge.call('toast', {
			  content: content,
			  type: type || 'none',//none / success / fail / exception
			  duration: duration || 2000
			});
			return;
		}else{
		    showTips(content,duration);
		}     	
      }
	};

一直没有出现问题,直到有个用户反应他的华为手机,支付宝版本是10.0.2,一直处于加载中状态,无法领取虚拟卡。通过他的截图看到的loading是我们h5自己ui的效果,说明他第一时间jsBridge没有注入成功,导致进入了H5的ui路由,当它内容加载完,准备hide掉loading的时候,jsBridge又恰好注入成功,此时我们执行了AlipayJSBridge.call('hideLoading');因此只是隐藏了支付宝native的loading,没有隐藏我们H5的loading,导致一直处于加载中。后来在hide方法中加入隐藏H5的loading方法,避免jsBridge注入时序出错时,依然能全部隐藏。最终的hideIndicator方法修改如下:

      hideIndicator:function(){
	 	if (typeof(AlipayJSBridge) != "undefined") {
			AlipayJSBridge.call('hideLoading');
			myApp.hideIndicator(); //避免show调起的是我们自己UI,hide时AlipayJsBridge已经注入完成
			return;
		}else{
		    myApp.hideIndicator();
		}     
      }

3、离线包跳出来的webView实例不支持hash路由,所有的返回直接关闭webView】:这个问题和支付宝前端沟通之后,他们前期让我们先用容器的pushWindow接口做路由,后期他们升级支付宝钱包版本来支持hash路由。

4、【切换支付宝的登录账号,不会清空localStorage】:这对应用离线数据的存储更新带来了一个大坑,本来我可以用auth_base静默授权只拿一次alipayId,然后一直存储在localStorage里面。每次进应用只要判断localStorage是否有alipayId,有的话直接可以用它去查询是否开过卡(未开卡路由引导至开卡页面;已开卡路由引导至卡详情页面),没有的话就再次调用auth_base获取它。按照这个逻辑的话,我就可以对用户信息接口做到最小频率请求了,除非用户切换账号,否则只要请求一次就可以了,但是是否开卡还是要保持每次请求,因为用户的卡状态会变化。由于切换账号不会清空,导致每次都要去请求alipayId,对比新旧的值,如果有变(切换支付宝账号),就更新,大大增加了这个接口的访问频率。当然这个问题在旧版本的ios微信中也存在,最新版的微信已经修正了该bug。

5、【地图支持性】:之前在微信中一直用腾讯地图lbs服务开发地图相关场景,由于竞对企业,支付宝直接屏蔽了对腾讯地图的支持,无奈我只能用高德地图在支付宝容器中进行相关功能开发,开发阶段,用微信和支付宝调试地图功能都正常(此处要给腾讯一个赞,至少没在微信中屏蔽高德地图),但是预上线那晚,我发现线上的版本始终无法在支付宝中显示出网页(仅限4G环境下),当时的我真的非常诧异,内测一直好好的,部署到线上就无法打开了,我怀疑是服务器部署问题,马上拉上后台人员一起帮忙查看,因为页面提示一直是:ERR_CONTENT_LENGTH_MISMATCH:我通过论坛查找发现,有可能是Nginx 做反向代理,后端是 tomcat,chrome 浏览器访问项目时加载大文件失败导致的。后台也认可我这个观点,因为我们确实用了nginx做负载均衡,他们马上着手帮忙解决,于此同时我也开始在前端排查问题,重新审核了一遍代码排除了卡住线程的代码,甚至把自己写的业务逻辑js全部删除还是无法显示。问题一下子就棘手起来了。首先我想到的是网络问题,手机是4G,电脑是wifi,会不会是内网wifi可以访问,4G不行。开启手机热点,把电脑连接到手机4G之后也无法访问了,在fiddler中无法捕获到网页相关请求,看似直接被拦截了。后来又写了一个无js无css的纯html放到服务器上去,发现又是可以访问的。于是心里想着肯定是某个js影响了页面请求,它是影响页面请求,并不是页面渲染或者js报错,所以这个层级是在js语法错误更上层的问题,于是我把首页的js直接删除,果然可以访问了。找到问题节点后马上联系后台开发帮我看看是不是服务器因为我这个js限制了我整个网页的请求,他们又重新给我科普了一遍http请求过程,强调整张网页其实就是一个字符串,他们是不解析js的,js是我网页生成之后,在根据script中src发起二次请求,不可能屏蔽我的网页。听完我觉得他们说的是对的,回头又继续思考自己代码的问题,无奈之下只能通过一行一行人肉删除外部引用的js时,发现删除高德地图之后,网页就可以访问了。马上把信息同步给服务端人员,太好了,终于把问题缩小了。服务端人员发现确实有一个负载均衡的配置代码,会限制文件请求,但是修改之后依然无效,这时我们又陷入了无尽思索。迷茫地浏览技术论坛时,看到waf防火墙会直接屏蔽网页,脑海中闪过一个思路,会不会是公司防火墙在网络层把我屏蔽了,马上电话联系到公司waf负责人,对方回复高德地图不在屏蔽的黑名单中,只有调用支付宝等才会屏蔽,我马上接着提醒他,高德地图就是阿里巴巴的,他们是同一个企业啊,会不会存在相互调用。最后他通过查看日志,发现是因为高德地图调用了支付宝的相关服务,导致waf认为这个请求存在风险,直接把我们屏蔽了,最后联系风控部帮我们开了白名单,项目终于恢复正常展示。ps:下次再遇到此类问题的时候要继续发挥人肉定位问题的精神,因为当没有任何线索的时候,就尝试把问题定位到最小;但是也不要一味扑在业务代码层,多跳出思维定势,条件允许的情况下,提前联系网络相关同学报告情况,有可能是网络屏蔽呢。

6、【存放在https协议的网页,无法在容器中引用http的js和css资源,图片资源可以正常引用】:为了安全起见,部署上线后采用ssl加密传输,导致原先在cdn上的http静态资源无法在容器中引用,在pc端会警告,但是不影响页面打开,在支付宝容器中直接卡在进度条中,无法呈现页面。最后把cdn上的资源更新到https解决。然而在http的网页中引用https资源是完全允许的,这个不属于支付宝容器问题,这个应该是一个在移动端比较安全的一个做法。当引用了不安全的文件,直接屏蔽掉网页展示。

三:小结

(1)折中考虑——如果在微信或者支付宝中开发应用,就能集Native 和web两者之所长。一方面,Native 让开发者可以充分利用现代移动设备所提供的全部不同的特性和功能。另一方面,使用 Web 语言编写的所有代码都可以在不同的移动平台之间共享,使得开发和日常维护过程变得集中式、更简短、更经济高效。

(2)内部技能——前端都拥有Web 开发技能。在合适native解决方案的支持下,Web 开发者只要仅仅运用 HTML、CSS 和 JavaScript 等 Web 技能,就能构建应用,同时提供 Native 用户体验。

(3)考虑未来——HTML5的可用性和功能都在迅速改进。它可能会成为前端强有力的技术。如果用 HTML 来编写应用的大部分代码,一定要兼顾好Native组件的调用,比如上述的兼容UI方案,在支付宝中就用容器的组件,在支付宝外面就用h5自己的ui组件。公司就能确保我们今天的投入在明天不会变得过时,因为 HTML 功能变得更丰富,可以满足现代企业一系列更广泛的移动要求。

四:参考资料

支付宝H5文档高德地图开发接口web技术文档




码字很辛苦,转载请注明来自tuy博客《虚拟公交卡小结(支付宝容器jsBridge异步注入、WAF防火墙、地图Lbs等问题)》

评论

  1. 摩天轮 #1

    :evil: :?: 支付宝项目开发起来是不是比微信费劲?

    回复
    2017-06-23
    • admin
      admin

      不会特别费劲,都是在支付宝大楼驻场开发,其实支付宝完全可以自己完成这部分前端工作,只是考虑到我们是传统卡公司,就把卡业务这块还是交由我们实现!

      回复
      2017-06-23

点击这里取消回复。