Apple 的 Retention Messaging API 能把你的话放到取消页面上,但退款申请从不经过那里
Apple 的 Retention Messaging API 允许你在订阅者点按取消时展示一条消息、一个换套餐建议或一项促销优惠。本文说明它的运作方式、运行成本,以及为什么退款申请到达 Apple 时根本不会经过那个页面。

要点
- Apple 的 Retention Messaging API 让开发者选择 iPhone 订阅者点按 Cancel Subscription(取消订阅)后看到的消息,共有四种类型:文本、带图片的文本、换套餐建议、促销优惠。
- Apple 的文档仍将 Retention Messaging API 描述为需要开发者申请加入的预发布功能。1.0 版于 2025 年 7 月 16 日发布,1.5 版于 2026 年 4 月 27 日跟进。
- 在实时流程中,当活跃订阅者查看带有取消按钮的订阅详情页面时,Apple 会调用开发者的 Get Retention Message 端点。生产环境中该端点必须在 700 ms 内响应,否则 Apple 会改用默认消息。
- 挽留消息只影响未来的续订。退款申请通过 reportaproblem.apple.com 或应用内退款表单提交给 Apple,而展示挽留消息的取消页面不在这条路径上。
- 当客户在应用内通过 StoreKit 的退款申请 API 申请退款时,Apple 会自行关闭自动续订,并发送子类型为 AUTO_RENEW_DISABLED 的 DID_CHANGE_RENEWAL_STATUS,与从“订阅”设置页面取消时的类型和子类型完全相同。
- 对于 Apple 的退款,开发者唯一能提供意见的渠道是 CONSUMPTION_REQUEST:须在 12 小时内用 Send Consumption Information 回复,且前提是客户同意分享这些数据。
- Google Play 没有文档记载的、由开发者选择取消页面消息的同类功能。开发者事后会收到 SUBSCRIPTION_CANCELED 通知,userInitiatedCancellation 字段会说明订阅者离开的原因。
在 iPhone 上点按取消的订阅者,现在可以在确认之前看到你选择的消息、一个更便宜的套餐或一项折扣。这就是 Apple 的 Retention Messaging API,也是开发者第一次能在 Apple 自己的取消页面上发声。它仍是预发布功能,需要申请才能加入。如果你关心退款,它还有一条硬性边界:它面对的是即将停止付费的人,而不是要求退钱的人。退款申请走的是另一条路。
Apple 的 Retention Messaging API 能做什么
Retention Messaging API 是一项服务器对服务器的服务。你预先把消息文本和图片上传给 Apple,Apple 审核后,你再决定订阅者在即将取消自动续期订阅时看到哪条已批准的消息。Apple 的文档列出了四种消息类型:
- 文本消息,可以包含项目符号。
- 带图片的文本消息。
- 换套餐消息,建议同一订阅群组中的另一项订阅。
- 促销优惠消息,以相同或不同档位的折扣价提供优惠,使用你在 App Store Connect 中设置并在服务器上签名的促销优惠。
消息出现在客户于订阅详情页面点按 Cancel Subscription(取消订阅)之后。随后 Apple 会显示 Confirm Cancellation(确认取消)页面。客户仍然可以取消、点按 Keep Subscription(保留订阅),或接受你建议的优惠或套餐。你无法隐藏退出的路径,Apple 也要求开发者不要上传误导性或不准确的内容。
默认消息与实时流程
运行方式有两种。简单的一种是默认消息:按产品和语言区域分配的文本,可带或不带图片、项目符号。更丰富的一种是实时流程:Apple 调用你服务器上的 Get Retention Message 端点,你用一个消息 ID 回应。只有实时流程才能展示换套餐和促销优惠消息。
实际上默认消息不是可有可无的。每当实时调用失败,Apple 都会以默认消息兜底;对于缺少默认消息的语言区域,Apple 不会为该产品显示任何挽留消息。
| 项目 | Apple 文档的说明 |
|---|---|
| 状态 | 预发布,开发者表达意向后加入 |
| 产品 | 自动续期订阅 |
| 消息类型 | 文本、带图片的文本、换套餐、促销优惠 |
| 实时响应时限 | 生产环境 700 ms |
| 生产环境权限 | 须先通过沙盒性能测试,约 700 ms |
| 标题与项目符号文本 | 各 66 个字符,最多五个项目符号 |
| 正文文本 | 144 个字符 |
| 图片替代文本 | 150 个字符 |
| 每个应用的消息数 | 2,000 |
| 审核 | 新消息初始为 PENDING,变为 APPROVED 后才会显示 |
| 设备 | iOS 15.1、iPadOS 15.1、visionOS 1、macOS 14 或更高版本 |
实时调用中 Apple 会告诉你的服务器什么
请求是一个由 App Store 签名的 JWS。解码后包含 originalTransactionId、appAppleId、productId、userLocale、一个 requestIdentifier UUID、环境以及 signedDate。Apple 要求你核对 appAppleId 是否与你的应用一致,不一致则不要响应。真正有用的是 originalTransactionId,因为它让你在决定说什么之前,先查到这位订阅者在你的应用里实际做过什么。
挽留消息出现在哪里,又不出现在哪里
Apple 只在一个页面上放置这条消息,即客户点按 Cancel Subscription 之后的 Confirm Cancellation 步骤,例如从 Apple Account > Subscriptions 或 App Store 上某项订阅的页面进入。这个页面关乎下一次续订是否发生,不涉及已经收取的钱。
退款申请是另一回事。Apple 的支持页面会引导客户前往 reportaproblem.apple.com,在那里选择 Request a refund(申请退款),挑选原因和项目,然后等待 24 到 48 小时获得进展。在你的应用内,StoreKit 的 beginRefundRequest 会打开 Apple 的退款表单,Apple 表示 App Store 最多需要 48 小时来批准或拒绝。两条路径都不经过 Confirm Cancellation 页面。
| 时刻 | 发生位置 | 涉及什么 | 谁来决定 | 你的输入 |
|---|---|---|---|---|
| 取消 | 订阅详情页面,然后是 Confirm Cancellation | 未来的续订 | 客户 | 你选择的挽留消息,须在 700 ms 内响应 |
| 退款申请 | reportaproblem.apple.com 或你的应用内退款表单 | 已经支付的钱 | Apple | 12 小时内提交的 CONSUMPTION_REQUEST 数据,前提是客户同意 |
挽留消息能阻止退款申请吗?
不能。退款申请根本不会到达展示消息的那个页面。Apple 的通知文档中有一个细节让这一点非常具体:当客户在你的应用内通过退款申请 API 申请退款时,Apple 会自行关闭自动续订,并发送子类型为 AUTO_RENEW_DISABLED 的 DID_CHANGE_RENEWAL_STATUS。从“订阅”设置页面取消的客户产生的类型和子类型完全相同。你的服务器为两种截然不同的客户收到同一种通知,而其中只有一位看到过你的挽留消息。
如果退款申请继续推进,提交证据的渠道是 CONSUMPTION_REQUEST。Apple 会针对任何产品类型的退款申请发送它,你有 12 小时用 Send Consumption Information 回复。只有在客户同意的情况下你才可以回复。之后,Apple 的决定会以 REFUND 通知送达;如果 Apple 拒绝了客户在你应用内发起的退款,则以 REFUND_DECLINED 送达。
Google Play 有取消消息 API 吗?
Google 的订阅生命周期文档中没有。Google Play 订阅者在 Play 订阅中心取消,访问权限保留到当前结算周期结束,在此之前可以点按 Resubscribe(重新订阅)撤销取消,Google 称之为恢复。你会在事后通过 SUBSCRIPTION_CANCELED 通知得知,订阅资源中的 userInitiatedCancellation 字段会告诉你用户离开的原因。暂停功能是 Google 用来减少主动流失的主要内置手段,Google 表示所有订阅默认启用该功能。
在退款方面,Google Play 有自己的证据渠道。当拒付需要开发者审核时,会收到 PendingRefundReviewNotification,你有 24 小时通过 orders.reviewrefund 回复。对于 2026 年 8 月 3 日之后下的订单,拒付一旦成立,Google 会把购买价格(扣除 Play 服务费)连同银行的拒付手续费一并转嫁给开发者。
取消页面在金钱上让你付出什么
挽留消息看起来是免费的。其实不是,账单落在三个地方。
每次页面浏览都要消耗算力
Apple 会在活跃订阅者查看带有取消按钮的订阅详情页面时调用你的端点,而不仅仅是在他们取消时。每一次访问都是一个请求,你的服务器必须在 700 ms 内完成认证、查询和回应,否则 Apple 会改用默认消息。这就排除了偶尔需要两秒冷启动的函数。你要为始终保持预热的容量付费,还要承担决策逻辑在每个请求中产生的数据库读取和付费第三方 API 调用。

折扣是你主动放弃的收入
留住订阅者的促销优惠是实实在在保住的钱。给了一个本来就会留下的人,就纯粹是让利。下面是一个示例,不是基准数据。一个 $9.99 的月度套餐以半价提供三个月,每接受一次优惠就少收 $14.99。如果接受优惠的人中有三分之一本来就会按全价留下,那么一千次接受就意味着约 $5,000 的折扣给了根本不需要的人。账本的另一边,是那些真的要离开的订阅者。
糟糕的退出体验招来的退款
Apple 会把 Cancel Subscription 按钮保留在页面上,所以你困不住任何人,但你仍然可能让人厌烦。感到被逼迫的客户还有另一个选择,就是向 Apple 要回最近一笔扣款。退款一旦发生,钱会退回去,而与该订阅者相关的算力、存储、API 调用和联盟佣金都已经花掉了。这些成本是任何挽留消息都收不回来的。
如何使用挽留消息而不推高退款率
把取消页面当作讲求准确的地方,而不是耍聪明的地方。以下几条规则能让它停留在退款界线的安全一侧:
- 按订阅者分别决定。用 Apple 请求中的 originalTransactionId 查询近期使用情况。给真正在用产品的人提供折扣或更便宜的套餐。给几周没打开过的人一条简短、诚实的消息。
- 保持快速响应。在使用数据变化时预先计算好决策,让请求本身只需一次查询,轻松控制在 700 ms 以内。
- 为每个产品和语言区域都写一条默认消息。没有默认消息,调用失败就意味着什么都不显示。
- 密切关注 AUTO_RENEW_DISABLED。如果同一个 originalTransactionId 随后出现 CONSUMPTION_REQUEST,你面对的就是退款申请而不是取消,12 小时的倒计时已经开始。
- 回复每一个你被允许回复的 CONSUMPTION_REQUEST。RefundHalt 会在 12 小时窗口内向 Apple 发送准确的消费数据,并在 24 小时内回复 Google Play 的拒付审核,因为这是两家商店仅有的两个征求你一方说法的时刻。
值得向 Apple 申请权限吗?
如果你销售自动续期订阅,并且已经在处理 App Store Server Notifications V2,那么大概值得。取消页面是订阅者离开之前你能触达他们的最后一个地方,而换套餐的代价比彻底失去他们要小。只是要为一个在每次订阅页面浏览时都要响应的端点做好预算,并把退款防线放在退款真正发生的地方。
常见问题解答
- 什么是 Apple 的 Retention Messaging API?
- 它是一个服务器对服务器的 API,让开发者选择订阅者在自动续期订阅上点按 Cancel Subscription 后看到的消息。Apple 支持四种类型:文本、带图片的文本、换套餐建议和促销优惠。Apple 的文档仍将其描述为需要开发者申请加入的预发布功能。
- 我的 Get Retention Message 端点需要多快响应?
- 生产环境中须在 700 ms 内响应。如果你的服务器太慢或出错,Apple 会显示为该产品和语言区域配置的默认消息;如果没有默认消息,则什么都不显示。在 Apple 允许你设置生产环境 URL 之前,你还必须通过一项约 700 ms 的沙盒性能测试。
- 客户申请退款时会出现挽留消息吗?
- 不会。Apple 只在客户点按 Cancel Subscription 后的 Confirm Cancellation 页面上显示挽留消息。退款申请通过 reportaproblem.apple.com 或由 beginRefundRequest 打开的应用内退款表单提交,开发者在那里唯一的输入是 CONSUMPTION_REQUEST,须在 12 小时内回复,且前提是客户同意。
- 如何在 App Store Server Notifications 中区分退款申请和取消?
- 仅凭续订通知无法区分。从“订阅”设置页面取消,和通过你的应用发起的退款申请,都会产生子类型为 AUTO_RENEW_DISABLED 的 DID_CHANGE_RENEWAL_STATUS。同一笔交易随后出现的 CONSUMPTION_REQUEST,以及之后的 REFUND 或 REFUND_DECLINED 通知,才表明有退款申请在进行。
- Google Play 允许开发者在用户取消时显示消息吗?
- Google 的订阅生命周期文档没有描述这类功能。开发者会在事后收到 SUBSCRIPTION_CANCELED 通知,并可以读取 userInitiatedCancellation 字段了解用户取消的原因。暂停功能默认对所有订阅启用,是 Google Play 应对主动流失的主要内置工具。
来源和延伸阅读
- Apple Developer Documentation: Retention Messaging API
- Apple Developer Documentation: Setting up your Get Retention Message endpoint
- Apple Developer Documentation: Upload Message
- Apple Developer Documentation: Retention Messaging API changelog
- Apple Developer Documentation: notificationType
- Apple Developer Documentation: Send Consumption Information
- Apple Support: Request a refund for apps or content that you bought from Apple
- Google Play Billing: Subscription lifecycle
- Google Play Billing: Real-time developer notifications reference
- Google Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
App Store 和 Google Play 退款自动驾驶
继续阅读
你的应用 ROAS 仍在计入 Apple 和 Google 已退款的购买,请改用净 ROAS 出价
退款很少回传到广告平台,因此 ROAS 仍在计入 Apple 和 Google 早已退款的购买。本文说明 Firebase、Google Ads 和 AdAttributionKit 在哪些环节丢失退款、这会造成多少损失,以及如何为应用建立净 ROAS。
客户收据上的 App Store 订单 ID 能告诉你他们买了什么,以及 Apple 是否已经退款
Apple 的 Look Up Order ID 端点可以把客户 App Store 收据上的订单 ID 转换为已签名的交易,其中包括退款日期和原因。本文介绍如何用它处理退款邮件、它做不到什么,以及 Google Play 的订单 ID 有何不同。