TL;DR

​ 一个用户流失的可能性有很多种,不过我相信你最不愿意的就是他在支付、购买的过程中流失。一方面,付费的用户往往粘性更高,停留时间更长,这对产品的指标是有帮助的;而另一方面,既然用户发起了支付,那么就说明他有购买的意愿,也证明了你的想法是成功的,可商业化的。但是现实往往并不那么让人满意,实际上在整个支付的链路上,有相当一部分的用户由于各种原因流失了,本文主要阐述我是如何分析以及优化这些指标的。

一、建立支付转化漏斗

曝光转化率

要降低支付链路上的折损,第一步首先要清楚折损的点是从哪里来的,采用转化漏斗是一种比较常见的方式。另一个方面,也需要考虑是要度量的流程从何开始,到什么时候结束。举例来说,产品同学通常计算支付转化率,是以下面这个公式来计算的:

1
支付转化率 = 支付成功人数/营销页面曝光数

但,当整个支付转化漏斗还没有建立完整的时候,是不建议以页面曝光为支付转化流程中的起点的,原因是往往营销策略决定了用户支付的意愿,从而不同营销策略带来的支付转化率相差较大,研发同学是无从优化的。

支付转化率

​ 考虑到上面这个问题,可以采用下面这个公式:

1
支付转化率 = 支付成功人次/发起支付的次数

这样的好处在于,发起支付到支付成功,衡量的是支付能力的成功率,发起支付等于用户已经有了购买意愿,所以这就与营销策略无关了。

要建立这个漏斗,可以按照下面几个步骤来实施:

  1. 确定整个过程中涉及到的角色:以苹果支付举例,一次支付过程中涉及到用户、下单页、支付SDK、支付中台、发货后台、苹果后台这几个角色;

  2. 找出这几个角色之前的相互作用关系:

    支付作用关系图

  3. 根据相关关系中间的步骤建立支付转化漏斗:

    支付转化漏斗

    从这个漏斗可以看出,主要的损耗在苹果支付这一步。

二、优化支付链路上的异常

启动应用 —— 预拉取苹果商品

​ 支付流程的第一步并不是下单,通常是初始化支付SDK,或者是跟苹果拉取商品信息。如果等到支付时再来做,用户可能需要等待更长的时间,在启动的时候就初始化支付SDK,可以更快的发起iAP支付。这背后的原因是苹果支付第一步是通过SKProductRequests 去请求商品列表,但是这个过程并不像一个Http请求这么简单,背后还有较为复杂以及不透明的步骤,此处援引公司内一位专门负责iAP的同学整理的信息,步骤如下:

  1. 根据 AppleID + AppID(由开发者证书 SeedID + 应用包名 BundleID 组成)+ ProductID 寻找内存缓存,若缓存为空,发起网络请求并建立内存缓存。

  2. 若命中缓存,判断缓存是否过期。

  3. 若缓存过期或没有命中缓存,则使用 AppID+ ProductID 进行物品查找。

  4. 如果有在 Xcode 编译运行时指定使用 StoreKit Configuration File,会尝试在指定的文件中查找物品,若文件中不包含该 ProductID 相关的信息,则会直接回调 InvalidProductID。

  5. 如果没有指定上述文件,则苹果会前往 App Store Connect 下,确认该 AppID 下是否存在该 ProductID 对应的 App 内购买项目。

  6. 接下来,苹果需要判断当前应用是否从 App Store 下载(若是则为苹果现网包),若为 App Store 包,请求现网环境域名获取已审核物品;若为沙盒包,则过滤掉缺少关键配置(如价格,描述)等信息的无效 App 内购买项目。

  7. 最后,苹果需要根据 AppleID 的注册区域,决定该物品应该使用的币种及本地描述信息后,对其使用 SKProduct 进行包装后返回至成功回调。

–来源:公众号:生活攻城狮

从上面的步骤来看,如果第二步开始就能够命中缓存,那么后面的步骤就都能省略了。

发起下单 —— 检测非法请求

​ 平均每周约有3.46%的请求未能成功下单,造成这个损耗的原因,我统称它们为非法请求。非法请求的来源主要分为两类,第一类属于可以前置拦截类,汇总有下面几种类型:

非法请求 占比 解决办法
web传参数不对 未统计 有一例解决一例
越狱机请求 0.007% 前置判断
非AppStore版本 0.0035% 前置判断
未开启iAP支付开关 0.133% 前置给出提示,并跳转到设置页

对于这一类的请求,如果用户依据给出的引导提示去安装AppStroe版本或者是打开对应的开关,最终仍然是可以购买成功的。而第二类属于下单请求已经提交到支付中台,或者苹果服务,由后台返回的下单错误,汇总有:

失败原因 占比
外币支付、非中国大陆区的账号 1.63%
第三方代充 0.025%
命中风控打击 0.14%
错误码"4-1" , “4_-1234”,invalid productid 0.4%

对于这部分的非法请求,处理起来比较棘手。支付中台拦截这些请求的原因主要是处于安全的考虑,比如来自境外的信用卡黑卡,或者是利用苹果小额支付漏洞,俗称苹果36技术来刷单。对于这部分请求,其实是应该被打击的,这部分折损可以算作固定折损,不计入优化的范围。另一部分是请求通过了支付中台,但是苹果后台返回4_-1 或者是 4_-1234 错误码,对于这部分的损耗,苹果给出了下列原因:

render

但是事实上只有极小部分的用户命中了上述的7个问题,首先,回到第一节启动应用部分所述的7个步骤来分析,从上报数据来看,用户发起购买的的时候,当前购买环境满足下面两点

  1. 此时用户购买的是正确的并且已经在苹果商店上架的商品
  2. 此时并没有新的iAP商品在审核中

所以讲道理是完全不应该购买失败的,所以可疑的点就只有一种可能,用户是越狱机,或者用户使用爱思助手等第三方渠道重新签名App的安装包,苹果认为这部分的流量并不是正常的,所以报错。针对这个问题,传统的手段是通过结合ipa包本身签名是否正确来校验,这个步骤并不能完全规避这种情况,此处不展开。另一个方案是采用苹果的ATTest技术来验证流量是否合法:

render

ATTest是苹果为iOS 14以上的操作系统提供的一项可以用来检测用户请求的流量是否是来自于合法的技术能力,使用ATTest时,它会关联用户的硬件信息、App信息,生成一个密钥对,然后由客户端将这个密钥发送给苹果服务器做校验,如果这个请求来自一个可信的安装包(或者说一个真实的用户),那么苹果服务器会返回给客户端一个 attestation object,客户端再将这个回包的信息发送给自己的服务器,服务器收到这个attestation object后,会做下面几件事情:

  1. 解析attestation object
  2. 对比解析出来的appid 跟服务器存储的是否一致
  3. 验证证书的有效性
  4. 验证密钥的一致性

iap

相比于本地校验证书,交给苹果服务器来校验安装包本身是否真实合法应该更可靠。基于这个想法,其实可以在用户发生 4_-1 以及 4_-1234 时去检测这部分用户是否真的是盗版包/越狱机,如果是的话,那么是否可以针对这部分用户直接采用微信支付的方式来开通对应的服务,成功率则要高的多。但是ATTest能力上线之后,真实数据显示大部分的用户并不是真正的盗版包,从下面的数据可以看出,仅有1.35%的用户是真正的非法流量 ,这也就是说,大部分导致这个问题的原因,应该仍是网络问题导致的。

日均检测数量 盗版包个数 占比
1.35k 26 1.35%

苹果支付 —— 降低异常与增加挽留措施

在整个支付链路上,这个步骤是损耗最大的,达到57%以上,其中拆分下来又分三个步骤

  • 支付错误 —— 通常是网络错误导致,苹果在境内的服务器并不稳定,支付失败概率较大

  • 取消支付 —— 价格贵了、用户不想要了,为什么不可以用微信支付、支付宝支付等原因

  • 杀进程 —— 由于网络的原因导致用户等待时间过长,所以用户等待不了杀进程

render

针对这部分的损耗,核心在于如何降低异常增加挽留措施

支付错误

在以往,每一个App只能知道最外层的错误码,大部分返回信息都是SKErrorDomain Code=0 "发生未知错误" 根据外层错误码聚类,可以看出大部分问题属于未知错误,或者是属于网络错误导致的:

支付失败错误码 占比
0 (未知错误) 94.474%
-1001 (网络错误-TimedOut) 3.226%
4097 (苹果内部XPC通信异常) 1.017%
-1005 (网络错误-NetworkConnectionLost) 0.598%
-1009 (网络错误-NotConnectedToInternet) 0.205%
-1200 (网络错误-SSL errors) 0.117%
-1003 (网络错误-Cannt find host) 0.135%

假设,未知错误也属于网络错误,那么就不由得让人萌发一个单纯又美好的愿望,是不是可以在应用本地起一个代理,劫持所有的网络流量重定向一个稳定的后台服务上,由这个后台服务作为中间人跟苹果后台交互,这样不就解决了用户直连苹果服务器经常链接失败的问题了吗?但是很可惜,苹果应该有一种机制去识别代理发出的请求,如果走代理就会支付失败。既然这条路走不通,那是否可以进一步分析错误码本身?苹果在最近几个版本,开始支持第三层错误码的提示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 // 原错误信息
 支付失败: error: Error Domain=SKErrorDomain Code=0 "发生未知错误" 
 UserInfo={NSLocalizedDescription=发生未知错误, NSUnderlyingError=0x282528150 {Error Domain=ASDServerErrorDomain Code=3532 "您目前已订阅此项目" UserInfo={NSLocalizedDescription=您目前已订阅此项目}}},
 
 // 内层错误码
 errorInfo: {
    resultCode = 4;
    resultInnerCode = 0;
    resultMsg = "Error Domain=SKErrorDomain Code=0 \"\U53d1\U751f\U672a\U77e5\U9519\U8bef\" UserInfo={NSLocalizedDescription=\U53d1\U751f\U672a\U77e5\U9519\U8bef, NSUnderlyingError=0x282528150 {Error Domain=ASDServerErrorDomain Code=3532 \"\U60a8\U76ee\U524d\U5df2\U8ba2\U9605\U6b64\U9879\U76ee\" UserInfo={NSLocalizedDescription=\U60a8\U76ee\U524d\U5df2\U8ba2\U9605\U6b64\U9879\U76ee}}}";
    workingStep = 6;
}

所以iAP支付类的错误,我们就可以获取到

  • resultCode = 4
  • resultInnerCode = 0
  • resultMsg->NSErrorDomainCode = 3532
  • NSLocalizedDescription = (您目前已订阅此项目)\U60a8\U76ee\U524d\U5df2\U8ba2\U9605\U6b64\U9879\U76ee

相比之前的发生未知错误,更清晰了,那么针对这部分的错误码,我们可以进一步的给出用户对应的提示,或者给出苹果官方的解决方案:

code 错误原因 解决方案 占比
0 未知错误 无法解决 74.521%
3029 需要付款信息 https://support.apple.com/en-us/HT201266 20.128%
2024 需要验证 https://macreports.com/app-store-says-verification-required-for-free-apps-how-to-fix/ 2.648%
2005 无法完成付款 https://support.apple.com/en-us/HT203005 2.537%
3014 商店信用余额不足 https://discussions.apple.com/thread/251238288 0.066%
2022 你以前某次购物时存在帐单问题。 https://discussions.apple.com/thread/7828949 0.048%
3038 Apple 媒体服务条款与条件已更改。 https://discussions.apple.com/thread/253556197 0.046%
3750 Add a new payment method https://support.apple.com/en-us/HT201266 0.009%
3751 新增付款方式 https://support.apple.com/en-us/HT201266 0.002%
2006 付款方式被拒 https://support.apple.com/en-us/HT203005 0.001%
3705 要求准许 https://macreports.com/app-store-says-verification-required-for-free-apps-how-to-fix/ 0.001%
2011 Payment information is required to continue purchasing. https://support.apple.com/en-us/HT201266 0.000%

不过,从上文仍然可以看出,未知错误,也就是网络原因导致的错误占比仍然是最大的,这仍需要苹果进一步的优化自己的服务器才能解决了。

既然此路不通,那就另寻他法

苹果在22年更新的Apple Review Guide Lines 增加了一条

  • **3.1.3 其他购买方式:**以下 App 可以使用 App 内购买项目以外的购买方式。除非符合 3.1.3(a) 中规定的情况,否则此部分中的 App 不得在 App 内鼓励用户使用 App 内购买项目以外的购买方式。开发者可以在 App 之外向其用户群发送宣传材料,以介绍 App 内购买项目以外的购买方式。

    Apple Review Guide Lines 3.2.1

在App外部提供购买方式的渠道有下面几种,下面说一下这几种方式的利弊

  • 邮件,国内不常用,国内大部分用户也没有绑定邮箱
  • 短信,需要用户绑定手机号,并且发短信有一些成本,单条短信成本在2分-1毛之间
  • 微信/QQ 公众号,需要用户关注,并且登录态绑定

取消支付

通常针对取消支付的都会采用一个弹窗询问用户是否真正要取消,这个弹窗的效果通常一般,转化率也比较低。有部分的App如抖音,采用了短信的方式推送端外支付方式,这样则是把取消支付认为是支付异常的一部分。另一种是类似于曹操专车,会在用户取消的时候询问用户为什么取消,通过收集用户的反馈来不断改善用户体验。我目前采用的是第二种方式,也确实从中收集到一些问题,用户的主要反馈分布如下:

render

从用户的反馈,也可以看出一些优化的方法,比如提供折扣优惠,或者支持第三方的支付渠道。

杀进程

在分析数据的过程中,最开始总有一部分缺失的数据:

render

经过分析发现,除了上面的成功、失败、取消三种状态,还有一部分会杀进程导致App没有收到底层支付SDK的回调。这背后的原因要回到几年前,刚刚设计支付体验的时候,大部分的App都会在用户发起支付的时候,拉起一个全屏的蒙层,或者是菊花图在屏幕中间转啊转,但是此时用户没有办法终止这个流程,只能等待苹果的支付回调回来,才能决定是否继续支付,还是取消。但是还是由于苹果服务器的原因,这个过程有时候会相当的长,不愿意等待的用户会尝试通过杀进程的方式来中断这个支付流程。这部分的用户占比大概有5%左右,可以说是相当多了。要优化这个,可以把全屏的阻断交互去掉。这部分的提升可以这样估算:

1
提升比例 = 总转化率 - 杀进程后当日仍然购买成功用户比例

三、增加流量

要优化支付链路,降低损耗是可以增加利润,但是如果目标更高,还需要通过进一步的提升天花板,笔者之前参与的一个企业内部培训分享为这个问题提供了三种解决办法:

  1. 客户端打法:
    • 卖给更多用户——比如通过铺量、降价来提高口碑。如果反应到会员业务,就可以看到会有比如首充优惠,升级优惠等策略;
    • 卖给用户更多——如通过交叉销售,精准推荐,从而挖掘潜在的需求。比如现在很多平台都提供的联合会员策略,或者是更多的增加会员的权益;
    • 卖给更好的用户——通过细分用户群体,来采用不同的营销策略。
  2. 市场端打法:
    • 走出去,扩大市场,寻找新的空间
    • 扎下去,消费下沉,比如拼多多从一二线到四五线
  3. 新业务:
    • 相关的多元化,研究上下游的商机,增加品类
    • 非相关的多元化,研究主业之外的业务

用户流失率

对于技术侧而言,增加流量的还是要从降低损耗开始着手。以下面这张图举例:

render

  1. 图3用户点击立即开通按钮,到图4用户支付开通会员,是我们上述的支付转化。
  2. 如果以图3的页面曝光为起点,到图4用户支付成功,则是通常产品同学采用的曝光转化。

但是如果在图1加载到图2的过程中,用户退出了页面,那么这部分的损耗就统计不到了。从真实的情况来看,这部分的损耗并不低,在某些页面可以达到13%。如何提升页面打开的次数,提升曝光,也同样是增加流量的措施之一。从前文说道的曝光转化率公式也可以变形一下:

支付成功人数 = 营销页面曝光数 * 支付转化率

也可以看出,提升营销页面的曝光数,也是可以提升支付成功的人数的。另外,其实通过常识我们也知道,打开页面的速度越快,用户就越不容易流失

As per the report, the poor performance of the website will negatively affect business goals. Therefore, as per this statement by BBC, they lost around 10% of users for every additional second their website took to load. Hence, it’s advisable to leverage comprehensive JavaScript development prowess to power up your business.

how-to-improve-website-and-rendering-speed-with-javascript-code-optimization

比如上面这一篇文章引述BBC的页面每多加载1s就会额外损失10%的用户,针对这个问题,并没有实际的准则,每一个业务应该建立自己的观测指标,比如针对会员开通相关的页面计算出的流失率如下:

用户流失率 COUNT(Android_用户流失率)
用户正常打开页面 87.436%
用户正常打开错误 5.423% (主要是网络不好)
用户流失,等待时间小于1秒 0.928%
用户流失,等待时间在1 ~ 2秒 2.077%
用户流失,等待时间在2 ~ 5秒 2.599%
用户流失,等待时间在5 ~ 10秒 1.003%
用户流失,等待时间大于10秒 0.535%

从上面的表格可以发现两点:

  1. 等待时间与流失率在小于5s的阶段成正比,但是超过5s之后又成反比

    如果等待时间小于1s,也许误触导致进入的用户来不及退出,而等待时间越长的用户,是真正有耐心等待的用户。

  2. 应该减少由于网络原因导致的页面打开错误,避免用户加载失败

优化用户流失率

我所在的App的会员开通页、活动页面等目前主要技术栈仍以Web为主,Web页面的打开表现,依赖于客户端、页面、服务器三个角色互相配合。所以在优化上,我们也针对性的分析解决每一个影响页面打开速度的问题:

角色 影响页面打开速度的原因 优化措施
终端 低端机Webview的容器/内核加载时间过长,请求页面的时候网络环境差 预加载Webview容器/内核,渐进式加载、降低用户等待预期
页面 页面请求内容多,CSS体积大,图片过大… 合并请求、提及优化、按需异步,SSR…
服务器 服务器响应速度慢 cgi拆分,降低RTT

但是,虽然页面跟服务器之间的交互可以优化到一个理想的状态,也可以通过预加载把Webview内核的加载耗时降低到0(安卓高低端机的性能差距大,Webview的内核加载耗时最长时就已经超过了1s),但是由于用户的网络环境复杂,无法统一,这就导致了传统的优化方案到了瓶颈,需要用其他办法才能进一步优化。

离线包方案:Sonic

换一个思路来想,如果页面是缓存好的,就无需关注用户的网络环境了,这个方案的核心在于,当页面在加载的过程中,客户端需要拦截页面的请求,如果发现当前已经有缓存页面,那么就直接给Web去渲染,提升首屏的速度。

SSR+VasSonic流程

采用这个方案后,以安卓端为例,用户流失率从24.27%降低到了11.591%,提升了约12.6%,效果比较明显。针对Web的优化,除此之外还有比如Webview的缓存池优化、js压缩优化、缓存命中率优化等措施,由于篇幅的原因不在这里展开。

总结

moneyball

2011年有一个电影叫做MoneyBall,中文名翻译为点球成金,讲述了由布拉德·皮特主演的比利·比恩带领的奥克兰运动家队,在认识了耶鲁大学经济学硕士彼得(上图右)后,掌握了破解职业棒球联赛的金钥匙——通过数据分析来改变球队运营的策略,用数学建模的方式,选用能得分的球员而不仅仅是球探,最终,领导球队获得了历史上最长连胜的故事。

之所以分享这部电影,是想说明在整个支付链路优化的过程中,数据与指标,它们对技术优化的重要性就像电影里面破解棒球联赛的金钥匙一样重要。数据可以帮助你更深入的理解支付链路损耗背后的原因,而新定义的指标,如用户流失率,杀进程率等,可以帮助你找到新的解决办法。