Explore Retention Messaging in App Store Connect
App Store & Distribution 进阶 15m

在 App Store Connect 中探索 Retention Messaging

Explore Retention Messaging in App Store Connect

2026年6月10日

在 Apple 官方观看视频

一句话判断

这场 Session 最值得关注的一件事是:Apple 终于开放了订阅取消页面的 Server-to-Server (S2S) 实时拦截接口,让订阅防流失从“统一发优惠券”变成了基于用户画像的“精准狙击”。

这场 Session 讲了什么

做订阅制 App 最憋屈的是什么?用户默默点进系统设置取消订阅,你连挽留的机会都没有。以前我们只能在 App 内做个“管理订阅”入口试图拦截,但用户真要取消时,根本不会走你的 App 流程。现在 Apple 把订阅管理页面的“取消确认”环节开放了。

这次更新分两步走。对于不想写后端的团队,可以直接在 App Store Connect (ASC) 里配置静态的 Retention Messaging (留存消息),配上图片和 Retention Offer (留存优惠)。对于有后端能力的团队,重头戏是 Real-time Retention Messaging (实时留存消息)。当用户点击取消时,App Store 会向你的服务器发一个 S2S 请求,你的服务器可以根据这个用户的历史数据,实时返回一段话、一个降级方案 (alternateProduct) 或者一个专属折扣 (promotionalOffer)。

这对靠订阅吃饭的 App 来说是实打实的印钞机。Apple 给出的数据很直接:用了这个功能的 App,平均取消挽留率 (Save rate) 提升了 82%,如果带上促销优惠,挽留率直接暴涨 223%。

值得深挖的点

Real-time S2S 架构的必然性与性能生死线

为什么 Apple 要做 Real-time Retention Messaging,而不是给开发者提供一个客户端 SDK 来拦截取消操作?因为 iOS 的订阅管理页面是系统级 UI,第三方 App 根本没有权限注入代码或修改视图。S2S 是 Apple 在保持系统 UI 控制权的前提下,唯一能给开发者留出动态操作空间的方案。

但这带来了一个巨大的 Trade-off:性能与可用性。当用户在系统设置里点击“取消订阅”时,App Store 必须等待你的服务器响应才能渲染挽留页面。如果你的服务器响应慢了,或者挂了,用户的取消体验就会卡顿甚至报错。这就是为什么 Apple 在 Sandbox 环境里强制要求通过 /performanceTest 接口进行性能测试,不达标根本不让上 Production。你的后端必须在极短的 SLA 内返回 JSON,并且要做好降级兜底——如果 S2S 失败,App Store 会自动 fallback 到你在 ASC 里配置的静态 Retention Messaging。

动态降级 (alternateProduct) 比无脑打折更聪明

Session 里提到了三种实时响应类型:纯消息、促销优惠 (promotionalOffer) 和替换产品 (alternateProduct)。很多开发者遇到用户取消,第一反应是“打折”。但无脑发折扣会严重侵蚀利润率,还会培养用户“一取消就打折”的羊毛党心理。

alternateProduct 提供了一个更优雅的解法:动态降级。当你的后端识别到用户是因为“用不到高级功能”而取消时,可以实时返回一个同订阅组内的低价基础版 Product ID。比如用户要取消 99 元的 Yoga+ 年费会员,你的服务器返回一个 39 元的 Yoga 基础版年费会员。这不仅保住了用户的 LTV (生命周期价值),还避免了直接降价带来的品牌伤害。结合 originalTransactionId 和你们自己的用户画像系统,这种“千人千面”的挽留策略才是这个 API 的真正杀手锏。

代码片段

1. 实时返回降级产品 (Alternate Product)

场景:用户点击取消高级订阅,后端判断其活跃度低,实时推荐同组内的低价基础版订阅。

// 你的服务器接收 App Store 的 POST 请求后,返回的 JSON 响应
{
    "alternateProduct": {
        // 预先在 Retention Messaging API 中配置好的消息 ID
        "messageIdentifier": "ed7f25fc-5741-46a3-8502-062e0fb8afd0",
        // 同订阅组内的低价替换产品 ID
        "productId": "Yoga_summer_2026_basic_annual" 
    }
}

坑:productId 必须和当前取消的订阅在同一个 Subscription Group (订阅组) 内,且必须是有效的可售卖状态,否则 App Store 会忽略此响应。

2. 实时返回专属促销优惠 (Promotional Offer)

场景:针对高价值但即将流失的用户,实时下发限时 3 个月免费的专属签名优惠。

// 你的服务器返回的 JSON 响应
{
    "promotionalOffer": {
        // 关联的挽留消息 ID
        "messageIdentifier": "80135e2b-ae15-4ec4-8c5c-9ecc8045c0dc",
        // 使用 StoreKit 2 生成的 V2 版本促销优惠签名
        "promotionalOfferSignatureV2": "eyJhbGciOiJFUzI…" 
    }
}

坑:必须使用 V2 版本的签名 (promotionalOfferSignatureV2),老版本的 V1 签名在这里不适用。签名生成逻辑需要严格遵循 StoreKit 2 的规范。

3. 识别 Retention Offer 交易

场景:在 App Store Server Notifications (V2) 或 StoreKit 2 的 Transaction 中,识别用户是否使用了留存优惠。

// 解析 JWS 格式的 Transaction 后的 JSON 载荷
{
    "bundleId": "com.example.app",
    "productId": "Yoga_summer_2026",
    "type": "Auto-Renewable Subscription",
    "transactionReason": "RENEWAL",
    // 新增的 offerType 值:5 代表 Retention Offer
    "offerType": 5, 
    "offerIdentifier": "Yoga_2026_cancel_free_3m",
    "offerDiscountType": "FREE_TRIAL",
    "offerPeriod": "P3M", 
    "originalTransactionId": "1000011859217"
}

坑:以前 offerType 只有 1 到 4,现在新增了 5。如果你的后端解析逻辑用的是严格的枚举映射,记得加上 5,否则反序列化会直接报错崩溃。

最佳实践

  • 已有项目的迁移策略:先别急着上 Real-time API。第一步先在 App Store Connect 里把静态的 Retention Messaging 配好,把基础的图片和 Retention Offer 挂上,吃掉那 82% 的挽留率红利。第二步再让后端团队开发 S2S 接口,做 A/B 测试对比动态策略的收益。
  • 新项目的采用建议:后端接口设计必须把“低延迟”放在第一位。不要在 S2S 处理逻辑里做复杂的数据库联表查询或调用第三方风控接口。把用户画像数据提前缓存到 Redis 里,收到 originalTransactionId 直接查缓存返回结果。
  • 实战中容易踩的坑:千万不要在 S2S 接口里返回错误的 JSON 格式或超时的响应。虽然 Apple 有 Fallback 机制会降级到 ASC 的静态配置,但如果你的接口频繁超时,Apple 可能会在系统层面降低对你端点的调用权重,甚至直接熔断。Sandbox 里的 /performanceTest 必须认真跑,确保 P99 延迟在 Apple 要求的阈值内。

还有什么值得关注

  • Asset Library 深度联动:Retention Messaging 的图片现在直接复用 ASC 的 Asset Library,不需要再单独传图,还能通过 API 动态管理,对有多语言或多节日运营需求的团队很友好。
  • Sandbox 强制性能测试/performanceTest 接口不是摆设,Sandbox 环境下不跑通这个测试并拿到合格结果,你的 Real-time URL 根本无法推送到 Production 环境。
  • Win-back 与 Retention 的组合拳:结合之前引入的 Win-back (挽回) 功能,现在订阅生命周期彻底闭环了:取消时用 Retention 拦截,取消后用 Win-back 召回。
Subscription Retention Messaging StoreKit App Store Connect API Server-to-Server