WWDC Quick Look 💓 By SwiftGGTeam
What’s new with in-app purchase

What’s new with in-app purchase

观看原视频

Highlight

Apple 在 WWDC 2020 为内购补上退款通知、DID_RENEW 续订通知、家庭共享交易、watchOS StoreKit、SKOverlay 和 SKAdNetwork 2.0,让开发者能用服务器通知与 StoreKit API 管理购买、订阅和推广链路。


核心内容

内购最难处理的情况,往往发生在购买完成之后。用户买了 100 个宝石,又向 Apple 申请退款;用户订阅升级、降级、续费失败、重新恢复;家庭成员打开 App,希望立刻拿到共享权益。开发者如果只在客户端看一次购买结果,很快就会丢掉真实状态。

这场 session 先从服务端补洞。Apple 新增退款通知,让 consumable(消耗型项目)、non-consumable(非消耗型项目)和 non-renewing subscription(非续期订阅)的退款进入 App Store Server Notifications。订阅侧也新增 DID_RENEW,让每一次自动续期成功都有服务器事件。

然后 StoreKit 把这些状态带回设备端。Family Sharing 会为购买者和每个家庭成员生成交易;watchOS 6.2 可以直接发起内购;订阅涨价同意弹窗可以由 App 控制展示时机;SKOverlay 让 App Clip 或完整 App 用系统浮层推广另一个 App。

最后是广告归因。SKAdNetwork 2.0 增加 source app id、redownload 和 conversion value。广告 App 可以在首次启动后更新一个 0 到 63 的转化值,广告网络收到的 postback 仍由 Apple 的公钥验证,并在隐私约束允许时带上更多上下文。


详细内容

退款通知把内容撤销变成服务端事件

01:04)开场的例子是游戏里的 100 Gems。用户购买后向 Apple 申请退款,如果开发者不知道这笔交易已退款,就无法判断是保留内容、扣回余额,还是给用户展示解释消息。

03:24)Apple 的做法是新增 Refund notification。它是 App Store Server Notifications 的新事件,会在退款发出后通过 JSON POST 通知开发者;如果端点没有返回 HTTP OK,Apple 会最多重试三次。

refund notification:
  bid
  password
  unified_receipt.latest_receipt_info[].original_transaction_id
  unified_receipt.latest_receipt_info[].cancellation_date
  unified_receipt.latest_receipt_info[].cancellation_reason
  unified_receipt.latest_receipt_info[].product_id

关键点:

  • original_transaction_id 用来定位被退款的原始交易。
  • cancellation_date 说明 Apple 什么时候完成退款。
  • cancellation_reason 的值为 0 或 1;值为 1 时,Apple 说明用户因为 App 内问题申请退款。
  • bidproduct_id 用来确认通知属于哪个 App、哪个商品。
  • password 是 App 的 shared secret,用来验证 payload 来自 Apple。

09:11)收到退款事件后,开发者可以做退款监控、App 内消息、限制已退款内容访问。对 consumable,还可以扣减 App 内货币余额。session 明确把取舍交给开发者:动作要和社区健康、经济系统公平性匹配。

订阅状态从轮询转向通知驱动

11:52)订阅生命周期包含首次订阅、自动续期成功或失败、关闭或开启自动续期、升级、降级、取消等状态。Apple 推荐用 App Store Server Notifications 接收状态更新,而不是频繁轮询 verify receipt。

subscription notification:
  auto_renew_product_id
  notification_type
  password
  bid
  unified_receipt.latest_receipt
  unified_receipt.latest_receipt_info[].original_transaction_id
  pending_renewal_info[]

关键点:

  • notification_type 标记事件类型,例如 INITIAL_BUY、INTERACTIVE_RENEWAL、DID_FAIL_TO_RENEW、DID_RECOVER。
  • auto_renew_product_id 指向计划自动续期的商品。
  • latest_receipt_info 包含最近 100 条交易,可以用 original_transaction_id 找到活跃订阅。
  • pending_renewal_info 存放下一次续期信息。

16:05)2020 年新增 DID_RENEW。它会在每一次自动续期成功后发送,payload 里有 unified receipt、原始交易 ID、新订阅周期的 transaction ID、过期时间和 auto-renew product id。

17:14)Apple 还把 promotional offer id 加入 pending renewal info。用户决定在下一次续期使用订阅优惠时,开发者可以提前知道这件事。

18:00)verify receipt 仍然保留三个位置:服务器故障后的恢复模式、用户上线时立即判断 entitlement(权益)、以及自动续期后的双重确认。推荐做法是先订阅 DID_RENEW,再在订阅过期时间后安排一次 verify receipt 调用;如果 DID_RENEW 正常到达,就取消这次任务。

家庭共享会为家庭成员生成交易

19:36)Family Sharing 支持 auto-renewable subscriptions(自动续期订阅)和 non-consumable in-app purchases(非消耗型内购),最多让五个额外家庭成员共享一次购买。开发者需要在 App Store Connect 为具体产品开启,开启后不能关闭。

21:27)共享不是简单复用购买者的 receipt。Apple 会为购买者和每个家庭成员创建交易。家庭成员打开 App 时,transaction queue 里会出现对应交易,receipt 的 latest receipt info 里也会包含共享交易。

Family Sharing APIs and signals:
  SKProduct.isFamilySharable
  transaction queue entries for family members
  unique receipt for each family member
  SKPaymentTransactionObserver.paymentQueue(didRevokeEntitlementsForProductIdentifiers:)

关键点:

  • SKProduct.isFamilySharable 让 App 在请求商品信息后判断产品是否支持家庭共享。
  • 新购买会为家庭成员放入类似 restored purchase 的交易,已有恢复逻辑可以复用。
  • 旧的 non-consumable 购买需要 restore purchases,家庭成员才能拿到内容。
  • paymentQueue(didRevokeEntitlementsForProductIdentifiers:) 会在关闭家庭共享、离开家庭组、退款后被调用。
  • 收到撤销回调后要重新验证 receipt,不能只看 product identifiers 数组就直接撤销所有重叠权益。

StoreKit 扩展到 watchOS、涨价弹窗和 SKOverlay

27:40)watchOS 6.2 开始支持 StoreKit 和内购。watchOS App 可以观察 payment queue、请求 products、把 payment 加入队列。服务端 receipt validation 仍然可用;本地验证时,取设备标识要用 WKInterfaceDevice,而不是 UIDevice。

30:02)订阅涨价同意流程也变了。App Store 会在用户打开 App 时展示系统同意弹窗;如果当前时机不合适,App 可以在 SKPaymentQueue delegate 方法里返回 false,之后再调用 SKPaymentQueue.showPriceConsentIfNeeded 展示。

31:33)SKOverlay 是新的系统浮层,用来展示和推广 App。它比 SKStoreProductViewController 更贴近当前界面,也可以帮助 App Clip 把用户引导到完整 App。

SKOverlay flow:
  create configuration
  initialize overlay
  set delegate
  present in UIWindowScene
  dismiss from UIWindowScene
  animate alongside SKOverlay.TransitionContext

关键点:

  • AppClipConfiguration 用于从 App Clip 展示对应完整 App。
  • AppConfiguration 可以展示任意 App,并提供 app identifier。
  • campaign token 和 provider token 可用于 App Analytics。
  • position 可以选择 bottom 或 bottomRaised,后者适合带 tab bar 的界面。
  • delegate 的展示和消失回调都在 main queue,可配合 transition context 同步移动自己的 UI。

SKAdNetwork 2.0 给广告归因增加可选上下文

37:43)SKAdNetwork 涉及三方:广告网络、source app 和 advertising app。广告网络把签名后的广告数据放进 source app;用户安装并打开 advertising app 后,advertising app 初始化 postback;设备在计时器结束后把 postback 发给广告网络。

SKAdNetwork 2.0 additions:
  version: 2.0
  source app id
  redownload
  conversion value: 0...63
  updateConversionValue

关键点:

  • 广告展示数据在 2.0 里需要包含 version 和 source app id。
  • advertising app 首次启动时初始化 postback;后续调用不会重复启动流程。
  • updateConversionValue 可以写入 0 到 63 的 6-bit 值,用于表达购买、试用注册、完成关卡等事件。
  • 转化值只能升高,低于当前值的更新会被忽略。
  • postback 里的 source app id 和 conversion value 是可选项,Apple 会在隐私计算允许时返回。

核心启发

  1. 退款事件台账

    • 做什么:为每个 original_transaction_id 建一条退款处理记录,记录退款时间、原因、处理动作和用户可见消息。
    • 为什么值得做:Refund notification 会在 Apple 完成退款后推送,开发者可以及时处理内容和 App 内经济系统。
    • 怎么开始:接入 App Store Server Notifications,保存 cancellation_datecancellation_reasonproduct_id,再把处理结果同步到 App。
  2. 订阅续期校准任务

    • 做什么:用 DID_RENEW 更新订阅周期,同时在过期时间后安排 verify receipt 兜底任务。
    • 为什么值得做:session 建议用通知减少轮询,但在服务器故障或通知延迟时仍保留恢复路径。
    • 怎么开始:收到 DID_RENEW 就取消对应校准任务;任务触发时调用 verify receipt 并按 original_transaction_id 更新 entitlement。
  3. 家庭共享权益页

    • 做什么:在购买页标出哪些产品支持家庭共享,并在家庭成员获得或失去共享权益时刷新 UI。
    • 为什么值得做:StoreKit 会为家庭成员生成交易,也会在共享关闭、离开家庭组或退款时触发撤销回调。
    • 怎么开始:读取 SKProduct.isFamilySharable,复用 restored purchase 逻辑处理家庭成员交易,收到 revoke 回调后重新验证 receipt。
  4. App Clip 到完整 App 的升级入口

    • 做什么:在 App Clip 关键任务完成后展示 SKOverlay,引导用户安装完整 App。
    • 为什么值得做:SKOverlay 专门支持从 App Clip 过渡到完整 App,浮层能和当前界面一起动画。
    • 怎么开始:创建 AppClipConfiguration,设置 overlay delegate,在合适的 window scene 中 present,并用 transition context 调整底部 UI。
  5. 隐私友好的广告转化分层

    • 做什么:把安装后的关键行为映射为 0 到 63 的转化值,例如注册、试用、购买、完成首个关卡。
    • 为什么值得做:SKAdNetwork 2.0 允许 advertising app 更新 conversion value,广告网络可在保护隐私的前提下评估质量。
    • 怎么开始:为事件定义递增等级,只在新等级高于旧等级时调用 updateConversionValue,服务器处理 postback 时接受缺失 source app id 或 conversion value 的情况。

关联 Session

  • Introducing StoreKit Testing in Xcode — 本地测试内购、退款、订阅场景,是验证这篇 session 中服务端状态处理的配套工具。
  • Architecting for subscriptions — 深入讲 entitlement 设计,接上 DID_RENEW、verify receipt 和订阅生命周期处理。
  • Streamline your App Clip — 讲 App Clip 的交易体验和 SKOverlay 使用场景,可补充本文的 SKOverlay 部分。
  • Explore App Clips — 先建立 App Clip 的设计和发现机制背景,再理解 SKOverlay 为什么适合完整 App 引导。

评论

GitHub Issues · utterances