What’s new in Apple In-App Purchase
App Store & Distribution 进阶 13m

Apple App 内购买项目的新功能

What’s new in Apple In-App Purchase

2026年6月10日

在 Apple 官方观看视频

一句话判断

✅ 年度订阅终于能官方“分期付款”了,这是提升订阅转化率最实用的商业武器,比任何 StoreKit 代码层面的更新都值钱。

这场 Session 讲了什么

做订阅的开发者一直面临一个死结:推年度订阅,用户看到总价直接跑路;推月度订阅,用户用两个月就取消,LTV(生命周期价值)根本算不过账。Apple 这次直接给出了官方解法:12个月承诺期的月度订阅(Monthly subscriptions with a 12-month commitment)。用户按月扣费降低决策门槛,但合约锁定 12 个月。StoreKit 新增了 PricingTermsbillingPlanType 来区分买断和按月承诺,Server API 也同步下发了 commitmentInfo 让你追踪履约进度。

除了这个商业大杀器,IAP(App 内购买项目)的审核提交流程终于和 App 本体、In-App Events(App 内活动)统一了。以前 IAP 单独提交、状态不同步导致的“App 上了但 IAP 没审过”的惨剧,现在可以通过打包成一个 Submission 彻底解决。

值得深挖的点

12个月承诺期按月订阅:转化与留存的完美平衡

Apple 做这个功能的动机很明显:帮开发者搞钱,同时减少用户因为“被坑”产生的客诉。以前开发者为了锁定用户,经常搞“首月 1 元,次月恢复原价且强绑一年”的套路,不仅 UI 实现复杂,还极易被 App Review 拒审。现在 Apple 提供了原生的“分期”方案。

从设计上看,这是一个精妙的 Trade-off。Apple 承担了用户中途违约(比如信用卡扣款失败)的催收风险,开发者拿到的是确定的 12 个月 LTV 预期。在 StoreKit 中,你通过 billingPlanType == .monthly 筛选出承诺方案,用 commitmentInfo.price 展示总价。坑在于服务端处理:当收到 App Store Server Notifications V2 时,你必须解析 JWSTransaction 里的 commitmentInfo。如果 billingPeriodNumber 小于 totalBillingPeriods,说明用户还在承诺期内,此时即使用户在系统设置里点了取消,Apple 依然会按月扣费,你的服务端绝不能提前剥夺用户权益。

IAP 审核提交流程统一:告别“异步发版”的折磨

这绝对是基础设施层面的巨大进步。以前 IAP 审核和 App 审核是两条平行线,自动化发版脚本(比如 Fastlane)处理起来极其痛苦。现在 App Store Connect 的增强版提交体验(Enhanced submission experience)允许你把 IAP、订阅组、CPP(自定义产品页面)和 App 二进制文件打包在一起提交。

更关键的是 App Store Connect API 的更新。reviewSubmissions 接口现在支持 IAP 资源,旧的 IAP 专属提交 API 被直接废弃。这意味着你的 CI/CD 流水线必须重构。好处是状态完全同步,App 和 IAP 要么一起过审,一起被拒,不会再出现用户下载了新版 App 却买不到新 IAP 的尴尬。

代码片段

1. 在 StoreKit 视图中展示按月承诺方案

场景:使用原生的 SubscriptionStoreView 时,优先向用户展示 12 个月承诺期的按月付款选项,降低首屏价格锚点。

import StoreKit
import SwiftUI

struct SubscriptionStore: View {
    var body: some View {
        SubscriptionStoreView(groupID: "3F19ED53") {
            // 自定义营销文案
        }
        // 筛选出按月承诺的计费方案
        .preferredSubscriptionPricingTerms { _, subscriptionInfo in
            subscriptionInfo.pricingTerms.first {
                $0.billingPlanType == .monthly
            }
        }
    }
}

坑:pricingTerms 数组里可能同时有 .upFront(一次性买断)和 .monthly,如果没做筛选,系统默认会展示 .upFront,你的“分期”低价优势就白做了。

2. 服务端解析承诺期进度

场景:在 App Store Server API 返回的 JWSTransaction 中,判断用户当前处于 12 个月承诺期的第几个月。

// 解析 JWSTransaction 中的承诺期信息
struct CommitmentInfo: Decodable {
    let billingPeriodNumber: Int // 当前是第几个月
    let totalBillingPeriods: Int // 总承诺月数(通常是 12)
    let commitmentExpiresDate: Date // 整个承诺期结束的时间
}

// 在处理 Server Notification 时检查
if let commitment = transaction.commitmentInfo {
    if commitment.billingPeriodNumber < commitment.totalBillingPeriods {
        // 用户仍在承诺期内,即使他们取消了自动续订,Apple 也会继续扣费
        // 保持用户的 Pro 权益,不要降级
    }
}

坑:commitmentInfo 只有在 billingPlanType.monthly 时才有值。如果是普通的年度预付订阅,这个字段是 nil,直接解包会导致服务端崩溃。

最佳实践

  • 已有项目的迁移策略:立刻检查你的 CI/CD 脚本。如果你在用 Fastlane 的 deliver 提交 IAP,准备迎接 App Store Connect API 的变更,将旧的 IAP 提交逻辑迁移到 reviewSubmissions 集合中。同时,全局搜索 presentOfferCodeRedeemSheet,把旧的优惠码兑换逻辑替换为带 RedeemOptionVerificationResult 回调的新 API,现在你终于能准确知道用户兑换成功了没。
  • 新项目的采用建议:所有订阅类 App,无脑加上“12个月承诺期按月订阅”选项,并和普通的“年度预付”做 A/B 测试。在 UI 上,用 billingDisplayPrice 展示月供,用 commitmentInfo.price 展示总价,一定要在付款按钮上方清晰写明“承诺 12 个月”,避免客诉。
  • 实战中容易踩的坑:在 Xcode 27 的 StoreKit Testing 中配置好 Billing Plan 后,别忘了在 Transaction Inspector 里手动模拟“用户中途取消”和“扣款失败”的场景,确保你的服务端状态机能正确处理 commitmentInfo 的变化。

还有什么值得关注

  • Bundles and Suites(订阅捆绑和套件):支持跨 App 的订阅打包销售,目前只能在 Xcode 27 里测试 API,具体商业化细节 Apple 卖了个关子,下半年再公布。
  • Offer Code Redemption(优惠码兑换)API 升级:兑换接口现在返回 VerificationResult,不用再靠监听 Transaction.updates 去盲猜兑换结果了。
  • Retention Messaging API 支持新订阅:服务器到服务器的留存消息 API 现在能识别 12 个月承诺期用户,方便你在他们快到期时精准推送挽留优惠。
StoreKit In-App Purchase Subscriptions App Store Connect API