StoreKit 与内购新特性
What's new in StoreKit and In-App Purchase
2025年6月9日
一句话判断
Offer code 终于不再局限于自动续期订阅——消耗品、非消耗品、非续期订阅现在都能用 offer code 了,这对做促销活动和用户召回来说是一个被低估的变化。
这场 Session 讲了什么
三个部分:核心框架增强、签名请求、新的 SwiftUI 商店视图。
核心框架方面,AppTransaction、Transaction、RenewalInfo 三个关键类型都新增了 appTransactionID(全局唯一应用下载标识,回溯到 iOS 15)、originalPlatform(用户最初下载应用的平台)等字段。Transaction 新增了 offer 中的 Offer Period 和 advancedCommerceInfo。Transaction.currentEntitlements(productID:) API 替代了废弃的单值版本,现在返回一个异步序列。Offer code 扩展到消耗品、非消耗品和非续期订阅,新增 oneTime 支付模式。
签名请求方面,新增了 introductoryOfferEligibility 和 promotionalOffer 两个 purchase option,都需要 JWS 格式的签名。Session 演示了用 App Store Server Library 在服务器端生成签名的完整流程。
新的 SubscriptionOfferView 是 StoreKit 视图家族的最新成员,支持按订阅关系(upgrade/downgrade/crossgrade/current/all)自动选择展示的套餐。
值得深挖的点
Offer code 扩展到所有产品类型的意义
之前 offer code 只能用于自动续期订阅,这意味着做一次性促销(如”输入优惠码领取 10 个额外道具”)或者非续期订阅的折扣活动都没有官方支持。现在消耗品、非消耗品、非续期订阅都可以用 offer code 了,且回溯到 iOS 16.3。
这打开了一些之前不太方便的商业模式:比如教育类 app 可以给学校批量发放非消耗品的 offer code(解锁高级功能但不续期);游戏可以用 offer code 做限时活动(消耗品折扣);健身 app 可以用 offer code 推广非续期的季度订阅。
注意新增的 oneTime 支付模式——它描述的是”一次性支付”(区别于 freeTrial、payAsYouGo、payUpFront)。如果要支持旧版本 iOS(17.2 之前),可以用 offerPaymentModeStringRepresentation 属性获取字符串表示。
SubscriptionOfferView 的关系驱动展示
SubscriptionOfferView 的 visibleRelationship 参数是一个设计精巧的机制。它不只是简单地”显示某个套餐”,而是根据用户当前的订阅状态自动决定展示内容。比如 upgrade 会展示比当前套餐高一级的计划,downgrade 展示低一级的,crossgrade 在同级中选择最优性价比的。
这意味着你不需要写一堆 if-else 来决定给用户展示什么——声明式地指定”我想推广升级”,系统自动处理剩下的逻辑。all 关系则展示所有计划的价格信息,配合 subscriptionOfferViewDetailAction modifier 可以引导用户到完整的订阅商店页面。
一个常见的使用场景:在 ContentView 中读取 subscriptionStatusTask 环境值判断用户状态,如果是活跃订阅者就用 upgrade 关系展示升级选项,如果订阅已过期就用 current 关系展示当前套餐的折扣优惠。
代码片段
用新的 Transaction API 查询多个 entitlement:
// 查询某产品的所有 entitlement(考虑 Family Sharing 等场景)
for try await transaction in Transaction.currentEntitlements(for: "com.app.pro") {
// 处理每个有效的 transaction
unlockContent(transaction)
}
服务器端用 App Store Server Library 签名 promotional offer:
// Swift 服务器端
import AppStoreServerLibrary
let creator = PromotionOfferV2SignatureCreator(
bundleId: "com.example.app",
signingKey: signingKey,
keyId: keyId,
issuerId: issuerId
)
let signature = try creator.createSignature(
productId: "com.example.pro_monthly",
offerId: "promo_offer_id",
transactionId: appTransactionId
)
按用户订阅状态自动选择展示的套餐:
// 已订阅用户看到升级选项,未订阅用户看到标准套餐
SubscriptionOfferView(groupID: "subscription_group", visibleRelationship: .upgrade)
.subscriptionOfferViewDetailAction {
// 引导到完整的订阅商店
showSubscriptionStore = true
}
最佳实践
建议尽早迁移到 StoreKit 2。新功能(如 offer code 扩展、新字段、签名请求)都只在 StoreKit 2 中可用。如果还在用 StoreKit 1,现在是迁移的最佳时机。
优先使用 Transaction.currentEntitlements(for:) 替代旧的单值 API。一个产品可能有多个有效 transaction(比如用户同时拥有订阅和通过 Family Sharing 获得的访问权),新 API 能正确返回所有匹配结果。
避免在签名请求中省略 Transaction ID 字段。虽然它是可选的,但包含它可以让 App Store 更好地验证购买请求的合法性。
还有什么值得关注
appAccountToken现在也出现在 RenewalInfo 中,方便你在续期时关联用户账号。- UI context purchase 方法(iOS 18.2+)要求指定购买发起的 UIViewController 或 NSWindow,StoreKit 视图自动处理。
- 高级商务 API(Advanced Commerce API)新增了
AdvancedCommerceProduct类型,支持大型内容目录和创作者体验。