所有文章
深度解析阅读需 8 分钟

Apple 的 Retention Messaging API 能把你的话放到取消页面上,但退款申请从不经过那里

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

一个人坐在咖啡馆的桌旁,手持 iPhone,屏幕停在订阅设置页面上,这正是 Apple 的 Retention Messaging API 所针对的时刻

要点

  • 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 或你的应用内退款表单已经支付的钱Apple12 小时内提交的 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 调用。

一位开发者坐在桌前,盯着显示器上的服务器响应时间图表,旁边放着笔记本电脑和 iPhone,这正是 Apple 的 Retention Messaging 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 应对主动流失的主要内置工具。

来源和延伸阅读

RefundHalt

App Store 和 Google Play 退款自动驾驶

继续阅读

下一项退款请求已经在路上。

读完又一封关于未能申辩退款的支持邮件所需的时间,足够您完成 RefundHalt 设置。