Implement App Store Offers
App Store Distribution & Marketing 进阶 20m

实现 App Store 订阅优惠

Implement App Store Offers

2024年6月10日

在 Apple 官方观看视频

一句话判断

Win-back Offers 是今年订阅体系最重要的新增:Apple 替你筛选流失用户、替你分发优惠卡片,你只需要在 App Store Connect 里配规则、在应用里处理兑换逻辑。

这场 Session 讲了什么

App Store 的订阅优惠体系已经有了 introductory offers(拉新)、promotional offers(促活)和 offer codes(营销渠道分发)三种形态。今年的更新分两个方向:一是给现有 API 做了信息补全,二是推出全新的 win-back offers 用来挽回流失用户。

在 API 层面,Transaction 和 RenewalInfo 两个结构体新增了 offer 字段,把优惠 ID、类型、支付模式(免费试用 / 按期付款 / 一次性预付)统一放在一起。StoreKit Views 也新增了 subscriptionPromotionalOffer View Modifier,让你可以直接在 SubscriptionStoreView 的 UI 里展示促销优惠,不用自己画界面。macOS 15 也终于支持了 offer code redemption sheet。

但全场真正的重头戏是 win-back offers。这是一种全新的优惠类型,专门针对”订阅已过期且关闭了自动续期”的流失用户。和以往最大的不同是:Apple 替你做用户分群和渠道分发。

值得深挖的点

Win-back Offers 的核心机制

传统的 promotional offers 需要你在应用里自己判断用户是否满足优惠条件,自己生成签名,自己决定展示时机。Win-back offers 把这套流程大幅简化。

用户分群规则在 App Store Connect 里配置,支持三个维度:付费订阅累计时长(paid subscription duration)、距离上次订阅的时间(time since last subscribed)、以及两次优惠之间的间隔(wait between offers)。你不需要维护任何服务端逻辑来追踪这些状态,Apple 已经有你所有订阅用户的历史数据。

更关键的是分发渠道。Win-back offers 会自动出现在三个地方:用户的订阅管理页面、你的 App Store 产品页面、以及 Today/Games/Apps 标签页的编辑推荐位。这意味着即使用户已经卸载了你的应用,Apple 也能在 App Store 里把优惠推到他们面前。这比你自己发邮件、投广告的触达效率高了一个量级。

配置时你还能设置优惠优先级——高优先级的 offer 会排在常规优惠事件前面,也会影响 Apple 个性化推荐时的排序。区域性控制也支持,可以针对不同国家和地区设定不同价格。

offer 字段的统一设计

Transaction 和 RenewalInfo 里新增的 offer 字段设计得很干净。它是一个 optional 类型,只有当用户实际兑换了优惠时才会出现。这意味着你不需要检查一堆分散的字段来判断”这笔交易有没有用到优惠”——offer 存在就说明有,里面的 paymentMode 直接告诉你优惠方式。

这个设计也延伸到了 App Store Server API,JWSTransaction 和 JWSRenewalInfo 里对应的字段是 offerIdentifierofferType 和新增的 offerDiscountType。三端(StoreKit、Server API、Server Notifications V2)的数据模型终于统一了。

代码片段

读取 Transaction 中的优惠信息

// 遍历交易,检查是否有优惠
for await result in Transaction.updates {
    if case .verified(let transaction) = result {
        // offer 字段是 optional,有值说明用户使用了优惠
        if let offer = transaction.offer {
            print("优惠 ID: \(offer.id)")
            print("优惠类型: \(offer.type)")       // introductory / promotional / code
            print("支付模式: \(offer.paymentMode)") // freeTrial / payAsYouGo / payUpFront
        }
    }
}

场景:在客户端检查用户当前交易是否包含优惠信息。坑:offer 只在 iOS 17.2+ 的 Transaction 和 iOS 18.0+ 的 RenewalInfo 中可用,老版本需要回退到旧字段。

在 SubscriptionStoreView 中展示促销优惠

SubscriptionStoreView(groupID: "my_subscription_group") {
    // 你的订阅 UI 内容
}
.subscriptionPromotionalOffer { product in
    // StoreKit 提供该产品可用的促销优惠列表
    // 你只需选择合适的 offer 并生成签名
    let offers = product.promotionalOffers
    guard let selectedOffer = offers.first else { return nil }
    return .init(offer: selectedOffer, signature: generateSignature(for: selectedOffer))
}

场景:在订阅商店界面里自动展示符合条件的促销优惠。坑:签名生成逻辑需要在你的服务端实现,不能硬编码在客户端。

在 macOS 上展示 Offer Code 兑换入口

// macOS 15 终于支持了,API 和 iOS 一致
Button("兑换优惠码") {
    // presentOfferCodeRedemptionSheet() 在 macOS 15 可用
}

场景:让 macOS 用户也能直接在应用里输入优惠码。坑:注意这个 API 需要 macOS 15.0,旧系统上调用会直接失效。

最佳实践

已有项目: 如果你已经在用 promotional offers,现在应该把 Transaction 里的优惠检查迁移到新的 offer 字段上,老字段未来可能被废弃。如果你有 macOS 版本的应用,赶紧加上 offer code redemption sheet 的支持——这是用户等了很久的功能。

新项目: 规划订阅优惠体系时,把 win-back offers 纳入第一版设计。它的配置成本很低(App Store Connect 里拖拽几下就搞定),但能覆盖”用户流失”这个订阅产品最大的痛点。建议先用付费订阅时长 + 流失时间两个维度做分群,再根据效果细化规则。

还有什么值得关注

  • Win-back offers 支持设置结束日期和优惠优先级,可以用来做限时回归活动。
  • Apple 的编辑团队可能会把你的 win-back offers 放进 Today/Games/Apps 的个性化推荐里——这是免费的曝光机会。
  • App Store Server API 的新字段 offerDiscountType 对历史交易也有效,你可以回溯分析过去的优惠使用情况。
WWDC 2024