WWDC 2019 Session 302: In-App Purchases and Using Server-to-Server Notifications

1. Overview

本章节包括以下内容:

  • 使用新属性 SKStorefront 来为不同地区的用户提供对应的商品
  • 应用收据中将添加应用预购的信息(watchOS 6.0 也将支持预购)
  • 使用服务端对服务端的通知来管理用户订阅状态
  • 订阅生命周期的讲解
  • 订阅服务新特性:账单缓冲期

其中,最重要的就是服务端对服务端通知的新类型,其他特性业界基本都有手动实现的方案,现在只不过更方便成本更低。而服务端对服务端通知新类型的添加,则对用户订阅状态的管理方案带来了实质性进步,因此苹果也为此处分配了很长的篇幅。其次是账单缓冲期,可以为我们带来额外的收益。

2. What's new in StoreKit

2.1 Subscription Offers(订阅优惠)

从 iOS 12.2、tvOS 12.2、macOS 10.14.4 开始,为了帮助应用保持订阅数量,挽回订阅流失,苹果为应用内订阅服务提供了订阅优惠的支持,包括一定时长的免费试用、一定时长的低价订阅等十种类型的优惠方案。开发者可以在 iTunes Connect 里为订阅类型的商品设置促销,促销对新用户和曾经订阅过的老用户有效。苹果会自动判断当前用户是否符合优惠条件,从而在调起系统的内购面板时,自动展示促销文案或者正常价格。

2.2 SKStorefront

SKStorefront 用于描述当前用户 App Store 的店面信息,目前它只有一个 countryCode 属性可用,这是一个三字符的国家地区编码,表示用户的应用商店所设置的国家或者地区。苹果为 SKPaymentQueue 对象添加了一个SKStorefront类型的属性,名为 storefront。 未来应该会有更多的属性被添加进来。

苹果给出了示例,在进行内购商品展示前,主动获取 SKStorefrontcountryCode, 然后对内购商品一一核对,确定该商品在当前国家是否要提供。这样一来,内购商品的展示逻辑就放在了客户端。很多公司出于灵活性等考虑,商品列表的数据是放在服务端的,这样可以随时配置线上的商品,而对于不同国家展示不同商品这个需求,之前也有很容易实现的方案,举个例子:服务端根据请求的 ip 判断所处地区,动态返回对应地区的商品列表。

paymentQueueDidChangeStorefront

由于在程序运行期间,SKStorefront 也可能发生变化(比如用户更改了自己 App Store 的设置),所以苹果在 SKPaymentTransactionObserver 协议添加了一个新方法:- paymentQueueDidChangeStorefront:,用来通知应用 SKStorefront 发生了变化。在此方法中,可以重新对商品的地区信息进行核对,确保显示的是正确商品。

SKPaymentQueueDelegate

如果在一次内购进行的过程中 SKStorefront 发生了变化,应用可能会对本次交易的内容做出调整,比如用户把 App Store 切换到另一个国家,你就不想让他买这个商品了。对此,苹果添加了一个新的协议:SKPaymentQueueDelegate,用于对交易过程中 storefront 信息变化时作出响应。

该协议中目前只有一个方法: - paymentQueue:shouldContinueTransaction:inStorefront:,当内购进行期间 storefront 发生变化的时候被调用,你可以通过返回值告知系统是否希望此种情况下交易继续进行。如果你为这个方法返回了 true,用户的购买行为正常继续。如果返回了 false,那么此次购买就会终止。

需要强调的一点是,出于用户体验的考虑,这个方法需要尽快返回。因此,诸如网络请求询问服务端的处理方式可能会造成延迟,苹果建议事先把验证需要的相关信息缓存在本地,然后这部分的验证可以直接在应用中进行。

当你返回了false后,- paymentQueue:updatedTransactions: 方法被调用,你会收到一个被标记为失败的 transaction,错误码是 storeProductNotAvailable。这时购买行为也会被中断,你可给出一个弹窗之类的,告诉用户发生了什么。

2.3 App Pre-Orders

从 iOS 11.2、tvOS 11.2、macOS 10.13.2 开始,苹果推出预购应用的服务。开启预定的应用,在上架之前就可以上线产品介绍页,用户可以提前购买或者免费获取该应用。在最新版 App Store 中,“游戏” tab 会经常出现一个 “现可预定”分类,里面就是可预定中的游戏。

游戏行业有个运营的惯例是提前预约的用户在公测或正式上线后可以获得特殊奖励,提高玩家预定的积极性,从而在上线前造势。针对这类需求,苹果准备在应用的收据中添加预购相关的信息,用来判断当前用户是否预购应用,在旧版本的系统也将适用。

此外,在 watchOS 6.0 的手表应用商店中,也将支持应用预约。

3. Server-to-Server Notifications

在用户为订阅的第一个周期付款后,可能发生什么事情呢?用户可能在 App Store 中取消掉自动订阅,可能在用户支持中申请了退款,可能把自动续费的开关关了又开,诸如此类的所有事件,我们是通通无法获知的,因为它们都是用户和苹果的交互,并非发生在 app 内部。除非我们定时对所有的订阅用户轮询,把验证收据接口都调一遍(通过验证收据可以获知齐全的订阅状态信息),这显然是不可能的,轮询频率过高负担太重,频率过低又失去了时效性。但是,它们对于用户订阅状态的维护又必不可少,因此,全面理解服务端对服务端通知这套机制,同时正确实现处理的逻辑,对于提供了订阅服务的产品十分重要

3.1 What Are Server-to-Server Notifications?

以前也被称为 statusUpdateNotifications,简单来说,就是你需要维护一台服务器,在 iTunes Connect 上填写好这台服务器的地址,并保持其稳定可用。当用户订阅的特定事件发生时,苹果就会把事件信息通过 HTTP POST JSON 的方式主动通知这台服务器。你需要为苹果的通知返回 200 的响应作为确认回执,异常情况下苹果会重发最多三次,同时,针对这些事件进行对应的业务逻辑处理。

3.2 Setting Up Your Server

苹果对接收通知的服务器有以下几点的要求:

  1. 请求的连接需要符合 ATS (App Transport Security);
  2. 使用受信任证书颁发机构颁发的证书;
  3. 支持 TLS 1.2;
  4. 使用 AES-128 或 AES-256 对称加密;
  5. 使用 SHA-256 或更高级算法进行认证;

3.3 Unified Receipt

在之前,获取用户订阅信息只能通过/verifyReceipt收据验证接口获取。在以后的通知中,苹果对通知字段的结构进行了调整,添加了新字段.unified_receipt,包含内购的历史记录等与收据验证接口相同的信息。需要注意的是,不同于客户端从 bundle 中取出的收据,服务端对服务端通知里带的收据并不和某次应用的安装对应,因此,你只应该在服务端存储、处理通知中的收据。此字段将包含以下几个信息:

  • lastest_receipt:收据的唯一码,可用于收据验证接口。
  • lastest_receipt_info:一个包含订阅信息和订阅者订阅情况的数组。
  • pending_renewal_info:关于即将续订的信息。
  • status:账单状态
  • environment:账单创建的环境

最多只包含最近100条记录,更多记录可以通过数据验证接口获取。

3.4 Notification Types

在此之前,苹果提供了四种类型的通知:INITIAL_BUYINTERACTIVE_RENEWALDID_CHANGE_RENEWAL_PREF 以及 CANCEL。它们发送的时机和字面意思一致,其中 DID_CHANGE_RENEWAL_PREF 是指自动续费订阅的级别发生变化(同一项订阅服务,可以分级别,例如基础版、高级版等)。对于用户订阅状态的维护,其实这四种通知已经可以满足需求,我们可以知晓用户初次订阅、用户更改了自动续费项目的级别、用户续费了、用户取消了订阅。 本次 WWDC 之后,苹果会再增加四种通知类型。分别是 DID_CHANGE_RENEWAL_STATUSDID_FAIL_TO_RENEWDID_RECOVERPRICE_INCREASE_CONSENT,分别对应用户自动续费开关的更改、系统第一次自动续费失败、自动订阅失败过后再重试订阅成功以及用户进入了涨价流程。其中PRICE_INCREASE_CONSENT还包含了一个字段price_increase_effective_date,用于说明用户同意订阅价格变化的最后限期。

3.5 Handling Notifications

这部分苹果举例了一个用户订阅的场景,过了一遍八种通知发出的时机,在此过程中详细讲解了每种通知你需要重点关注的字段,以及该如何处理。其中,original_transaction_id大家非常熟悉,也被演讲者频繁提到,它是一个用户订阅最初的交易 id,在每种通知中,你都可以使用这个字段去追溯这个交易。

需要注意的是,如果用户升级了一个订阅项目的级别,那么会收到对旧级别的 CANCEL 通知,以及对新级别的 INTERACTIVE_RENEWAL 通知。如果是自然流失之后再次订阅,则只会收到 INTERACTIVE_RENEWAL 通知。未来 DID_RECOVER 会替代RENEWAL 通知,但目前它们会同时收到,这是为了给你的服务一定的调整适应时间。

3.6 利用新通知提升用户体验

用户在 App Store 对订阅项目进行的操作,以及在苹果用户支持系统中发起的退款等行为,都是用户和苹果进行的交互,我们的应用和后台服务无法及时感知,显得非常愚钝,因此这套通知系统的存在十分有必要。 总体来看,之前的四种通知,让这套通知系统达到了“可用”的水准,通过这四种通知类型,开发者可以方便地管理用户的订阅状态,然而,也仅仅是“可用”的水准。有了的新的四种通知类型,开发者可以关注到用户订阅行为更加详实的细节,对于用户的每一个操作,都能够及时感知,并且在应用内做出应对。举个例子,系统第一次自动续费失败,可能是用户无意间的操作导致付款方式失效。此刻我们的服务端收到了苹果的通知,然后下发一个提醒指令到客户端内,客户端在合适的时机用合适的方式提醒用户去检查一下付款方式,从而避免无意间的用户流失。可以看到,有了新的四种通知,我们可以把用户订阅的流程做到更好,提升了用户体验,也避免了不必要的流失。

4. Subscription Lifecycle

在这部分,苹果用细致又不繁琐的方式完整讲述了订阅行为的生命周期。大体上的过程如下:

  1. Acquisition,通过试用等措施来吸引用户订阅你的服务;
  2. Engagement,你需要不断更新自己服务的内容让用户继续订阅你的服务;
  3. Retention,保持用户订阅量,减少流失。

在此生命周期中,会穿插着许多账单相关的事件,例如:从试用到购买、服务升级或降级、续订或取消订阅等,通过上述八种通知类型,我们可以检测到这些事件发生,从而做出相应操作。在此session中,演讲者对各种通知对应的事件做了详细描述以及给出示例。

  1. Purchase,第一次订阅服务,应用收到此次购买的收据后,将相关信息加密传输到自己的服务器,服务器请求验证收据接口验证账单,并获取更多信息保存到数据库。服务器也会收到INITIAL_BUY通知,通知和账单信息的original_transaction_id一一对应。

  2. Renewal,续订服务时,应用在下次启动时也会收到订阅收据,如果你不想等到下次启动也可以使用第一次购买的信息,然后把账单信息发送到服务器验证。这种事件服务器不会收到通知,服务器在收到应用发来的收据信息后,直接调用接口验证账单,然后提供相应服务即可。

  3. Upgrade,升级订阅服务,服务会先收到一个旧服务的CANCEL通知,接下来会是INTERACTIVE_RENEWAL通知,服务器收到后可以去更新用户订阅信息,然后升级其服务。

  4. Cancel,取消订阅,用户关闭自动续费的开关,在之前你可能需要对所有用户调用验证收据的接口去更新订阅信息,苹果对这种情况作出改变,新增DID_CHANGE_RENEWAL_STATUS通知,当用户改变续费状态时通知服务器,从而避免轮训用户的操作。

  5. Churn,用户在订阅结束前,服务器没有收到任何关于他的通知,那么就可以把这名用户标记为流失用户。

  6. Win Back,为了挽回流失用户,可以实行订阅优惠等措施,苹果把这单独作为一个Session,Session 305: 订阅优惠最佳实践

  7. Billing Error,用户本来没有想着取消订阅,但是 App Store 无法恢复或续订,可能是用户的信用卡出现异常了,这种情况下,苹果会发出一个新的通知DID_FAIL_TO_RENEW。当你收到这个通知时,可以停止提供服务,标记流失用户,并在应用上面提醒用户订阅已经失效。

  8. Billing Retry,在上面的情况下,App Store会自动尝试续订服务,如果成功了,将会发出新的通知DID_RECOVER, 服务器可以更新用户订阅信息,并重新提供相应服务。

5. Reducing Involutary Churns(减少无意间的用户流失)

这一节主要介绍苹果在帮助我们减少无意间的用户流失所做的措施和成果,包括不断优化和更新续订失败时的重试机制等,甚至使用了机器学习的模型来提升多平台恢复订阅的可能性。通过这次措施,苹果恢复了 77.4% 因为账单问题导致的订阅流失,从而将无意间的账单流失减少至 1.6%,恢复订阅数约 4600万。据数据统计,挽回的订阅数在前 16 天可达到 80%,那么我们在这期间可以做什么?那就是下一节的账单缓冲期。

6. Billing Grace Period(账单缓冲期)

最后,介绍了一个压轴的新特性,叫做账单缓冲期(Billing Grace Period)。开发者可以自己选择是否开启这个特性,开启后,用户的自动订阅失效后会进入一定时间的缓冲期,苹果建议你在这段时间内保持对用户的服务(比如维持用户的会员状态),因为在这段时间里苹果可能还在持续地进行续费尝试。开启的步骤也很简单:

  1. 在 App Store Connect 中选择开启服务;

  2. 在收据验证接口/verifyReceipt中,会有一新字段grace_period_expires_date,说明缓冲截止期;

  3. 在截止日期前,保持相应服务不中断。

开启新账单缓冲期,可以减少对用户服务的干扰,账单可以维持在同一生命周期内,那么缓冲期也会算入长期订阅的 85/15 分成的时长中,从而给我们带来附加的收入,苹果强烈建议我们开启这个功能。

此外,你还可以利用这段时间在应用内为用户提供“最后的温存”,比如提醒用户你的会员你已经过期,但我对你的服务目前还会一如既往!用户极有可能在这段时间里被你挽回,重新安排上会员!

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容