Apple IAP 二三事

6,838 阅读11分钟

一、概述

1、简介

  • IAP (In-App Purchase)即应用内购买,是App内购买「虚拟数字产品」的支付方式;AndroidiOS都有IAP支付;判断APP是否需要IAP的标准:用户付费购买的商品/服务是否为「虚拟数字产品」;

  • 更通俗的解释:看商品/服务的实际消费场景是在 App 内,还是 App外;如果在 App内即可完成消费的,苹果要求使用 IAP,如电子书、充值类虚拟货币、游戏/直播中道具等;在App外完成消费,可以使用第三方支付;典型如线下电影票、实物电商、打车、外卖等。

  • Apple对IAP态度严苛,很大一部分原因是:Apple可以从IAP支付流水中,抽成30%;很多App不愿意使用IAP支付,通过欺骗审核方式绕开IAP支付,其实这会给App带来非常大的风险,严重可能导致App被下架。

2、IAP商品种类

  • 消耗型项目:只可使用一次的产品,使用之后即失效,必须再次购买,如:游戏币、一次性虚拟道具等

  • 非消耗型项目:只需购买一次,不会过期或随着使用而减少的产品。如:电子书

  • 自动续期订阅:允许用户在固定时间段内购买动态内容的产品。除非用户选择取消,否则此类订阅会自动续期,如:Apple Music这类按月订阅的商品

  • 非续期订阅:允许用户购买有时限性服务的产品,此 App 内购买项目的内容可以是静态的。此类订阅不会自动续期

    IAP商品交易成功后,Apple收取30%的手续费,如果商品是30元,你只能得到21元;不过自动续订的商品第二年开始只成15%

3、Pre Develop

二、支付流程对比

1、微信/支付宝の支付流程

  • 目前,微信/支付宝绝对是国内最主流的第三方支付,和IAP相比,他们都提供了非常好的技术支持;(虽无缘于支付宝,但必须给支付宝点赞!)

  • App内,微信/支付宝的支付流程简述如下:

    • App内发起支付请求;(调起微信/支付宝支付有两种,SDK方式 和 schema方式,建议前者)
    • App跳到支付宝/微信客户端,引导用户完成支付;
    • 用户完成支付的同时,微信/支付宝服务器回调App的服务器,告知支付结果;App中也会收到微信/支付宝SDK中的支付回调结果(业务上,一般不以SDK回调结果做最终结果);
    • App的服务器收到回调后,完成订单验证和发货操作;
    • 从微信/支付宝返回第三方App,App去自己的服务器查询商品支付结果;
  • 在整个支付流程中,如果发生用户支付成功,但是的Server回调出错了(可能是网络,也可能是App服务器异常),到账用户没有收到货物,这会对支付体验造成非常大的打击;

  • 但是,微信/支付宝的服务器非常靠谱:检测回调失败后会重试,能最大程度(错误重试机制)保证将结果同步给第三方App服务器;

2、IAPの支付流程

  • App内,IAP的支付流程,简述如下:

    • App内,通过IAP API(StoreKit),发起支付请求;
    • 用户在App内完成支付;(获取商品信息->创建交易加入到交易队列->用户支付->支付完成)
    • 用户完成支付的同时,IAP 服务器回调APP,通知购买成功,并把票据(receipt)写入到 APP 沙盒中
    • App需要将票据(receipt) 上传给App服务器;
    • App服务器再将票据(receipt) 发给Apple服务器去验证;只有验证成功,App服务器才能去发货。
  • 在整个支付流程中,用户在端上扣款成功,只是开始;后续的支付验证至少需要保证App上传票据成功App服务器将票据交给Apple服务器成功App服务器获取订单验证结果成功;否则,掉单是必然出现的事情;简单来说:用户扣款成功后,IAP支付的严峻挑战开始了;

  • Apple将每次IAP支付行为被抽象成一个事务(SKPaymentTransaction),只有事务被正常结束(finishTransaction:),该次支付行为才算完成。即使支付中途被中断,但是这次事务并没有丢失。eg: 支付未完成,App Crash了,下次App重启(需addTransactionObserver:),之前被中断的事务会接着进行。

    StoreKit框架主要提供三部分功能:In-App Purchase(IAP,应用程序内购买)、Recommendations and reviews(建议和评论) 和 Apple Music(苹果音乐,国内几乎不用);IAP的API在StoreKit框架。

3、总结

  • 对比第三方支付和IAP支付流程发现:

    • 微信/支付宝的扣款后,交易验证工作是在服务器之间通讯完成的;只要用户产生了交易,他们的服务器会回调App服务器,交易的可靠性由支付宝/微信服务保证的;
    • 而IAP扣款后,交易验证是App驱动App服务器完成的;但是移动设备所处的网络环境远比服务端复杂;扣款成功后,后续的下发票据 和 App上传票据都面临严峻的考验(网络异常、App服务器异常、Apple服务器异常等)。
  • 简单来说:Apple预期将支付流程中最重要的验证环节交给了App开发者;和国内的微信/支付宝套路完全不同;

  • 此外,虽然Apple为保障交易验证完成,提供了事务机制,但是事务机制最大的问题是:如果某一个事务在当次App生命周期内未能正常结束,只能在下次App重启后,中断的事务才能恢复;这其实是对用户伤害比较大:扣了款,可能很长时间收不到货。

三、优化IAP验证

1、思路

  • 完善重试机制;尽可能多的重试,保证用户扣款后能比较及时收到货物;

  • 建立业务订单和IAP订单映射机制;Apple只负责告诉一个交易事务成功or失败,不关心是否映射到业务订单好;

2、完善重试机制

  • 不依赖苹果事务机制重试;当Apple通知用户交易成功(SKPaymentTransactionStatePurchased),立刻持久化交易数据到keychain,存储成功后,finish掉交易。

    补充1:交易数据持久化到keychain好处有二:存储到keychain的数据被加密,安全可靠;即使App被卸载,keychain中数据也不会被删除;

    补充2:扣款成功后,可能苹果没有告知交易成功,可能下次甚至下下下次启动,才告知交易成功;因此,App启动后,监听交易事务队列,当收到交易事务完成通知,立刻持久化交易数据;

  • 建设交易验证队列;每笔交易数据持久化成功后,尝试订单验证,验证包含两步:上传票据 和 查询订单状态;只有两步都完成,才能算一个订单验证结束;

  • 丰富交易验证的时机

    • App冷启动后x秒,keyChain 中还有没有处理完的交易,一个个去发起校验;
    • 用户发生新购买,扣款成功,交易数据持久化成功后,发起验证操作;
    • App从后台切换到前台x秒,无网切到有网,前台停留x分钟等情况下,如果 keyChain 中有未验证完成的交易,发起验证请求;
    • ...

3、订单映射

  • 订单映射指:业务订单和IAP订单的映射,本质是将业务订单号 orderID 绑定到苹果的交易订单上;
  • 发起IAP支付后,我们给Apple的是一个SKPayment对象,最后监听到的是SKPaymentTransaction对象(有SKPayment属性对象);我们可以通过利用SKPaymentapplicationUsername字段实现订单映射;
  • 创建交易对象时,将业务订单号 orderID赋值给SKPaymentapplicationUsername;等支付成功后,通过SKPaymentTransactiond对象的payment获取到业务订单号 orderID,从而实现了业务订单和IAP订单映射绑定
  • 重要说明:利用applicationUsername透传业务订单号 orderID 有失败的概率,一些情况下会导致applicationUsername透传的值丢失(订单号丢失问题);如果不处理的话,会发生掉单;
  • 总的来说,利用applicationUsername实现订单绑定虽然有不足,但是从ROI上评估还是可行的;订单号丢失问题可以交给服务器处理:服务端只要验证交易是正确的,且该用户的确有交易记录,自动生成订单重新发货 or 最近订单(时间、金额)匹配;即便这么做,依然有case被遗漏, 但能保证大盘基本OK。

4、票据的问题

  • iOS 7后(App几乎都是iOS 9起步),从[[NSBundle mainBundle] appStoreReceiptURL]]中获得的receipt(票据)数据;App上传票据信息的话,将其中的数据一起上传
  • iOS 7后,[[NSBundle mainBundle] appStoreReceiptURL]]中的票据信息是一个receipt listin_app字段),本身带有“自动修复的特性”,如果用户某次支付没有正确完成,后续也没有被成功恢复;当他产生下一次成功支付后,[[NSBundle mainBundle] appStoreReceiptURL]]中会包含这几次支付的receipt
  • 获取不到票据情况:如监听到交易成功了,但是从[[NSBundle mainBundle] appStoreReceiptURL]]中获得的数据是空,遇到此类问题,可以打标记后续重试;
  • 票据无效,验证失败的情况也可能遇到,这时候,让用户提供了支付信息,走补偿 or 退款吧;

5、to be continued

  • 保障扣过款的IAP订单尽快得到验证,是IAP问题中首要解决的问题;本节中简单介绍了相关一些思路;我相信,做好这些能hold住大多数问题;
  • 虽然IAP总体体验被微信/支付宝吊打,但是由于iOS用户被教育了这么多年,Apple在新iOS版本上也有些不为认知的优化;总体上来说,IAP支付还可以,没有早年那么糟糕;
  • 做好以上这些事,不是终点;优化产品体验,做好用户教育工作,也能很好帮助实现总目标。

四、IAP周边建设

1、完善监控埋点

  • 支付环节中,对关键路径埋点,包括但不限于:持久化交易数据操作、上传票据操作、查询操作,结束验单操作等;
  • 监控异常的情况,包括但不限于:持久化交易数据失败、上传失败,applicationUsername获取订单号为空、查询失败等;
  • 开发阶段,尽可能多展示调试日志信息;

2、响应用户反馈

  • 再好的方案,也无法hold所有的IAP问题,建立用户反馈响应机制,及时响应用户反馈;关注用户关注的;
  • 例1:部分用户反馈无法支付,排查发现,是网络原因导致获取SKProduct失败;因此,尝试提前拉取SKProduct;
  • 例2:部分用户反馈,iOS 13上必选Crash,排查得知:iOS13把SKProductRequst的回调放到了子线程;如果在这个回调中有UI操作,必然Crash。
  • 将典型的,数量比较多的用户反馈集中集中起来,针对问题,尝试优化方案;同时,注意安抚用户的情绪;

3、其他

  • 要加强沉淀,包括但不限于:IAP优化方案、问题排查和解决记录,用户反馈和处理记录等;

  • 虽然,Apple提供了Testflight包,用户可以IAP沙盒支付;针对沙盒支付,Server要做好验证,这些非真实金钱交易,要特殊处理。

参考文章

iOS 内购(In-App Purchase)总结

谈谈苹果应用内支付(IAP)的坑

iOS IAP应用内购详细步骤和问题总结指南

苹果IAP开发中的那些坑和掉单问题

贝聊 IAP 实战之见坑填坑

贝聊 IAP 实战之订单绑定