概述
In-App Purchase 允许用户购买应用内的内容和功能。用户可以在应用程序中进行购买,或者直接从App Store中购买。
- 如果您想要在 app 内解锁特性或功能 (解锁方式有:订阅、游戏内货币、游戏关卡、优质内容的访问权限或解锁完整版等),则必须使用 App 内购买项目。App 不得使用自身机制来解锁内容或功能,如许可证密钥、增强现实标记、二维码等。App 及其元数据不得包含按钮、外部链接或其他行动号召用语,以指引用户使用非 App 内购买项目机制进行购买。
- 如果 app 允许用户购买将在 app 之外使用的实体商品或服务,则必须使用 App 内购买项目以外的购买方式来收取相应款项,如 Apple Pay 或传统的信用卡入口。
StoreKit 框架可代表您的 app 连接到 App Store,以提示并安全地处理付款。然后,该框架会通知您的 app,后者便会交付购买的产品。要验证购买,您可以在服务器上通过 App Store 验证收据或在设备端验证收据。对于自动续期订阅,App Store 也可以向您的服务器发出重要订阅事件通知。
要使用 In-App Purchase,必须首先在 App Store Connect 中配置产品。App 内购买项目配置流程
Apple 共提供了四种类型的IAP项目:
- 消耗品(Consumables):是一种在使用一次后就用完的类型。可以多次购买。例如:生命、宝石、金币、体力等
- 非消耗品(Non-consumables):是一种客户只需购买一次的类型,它们不会过期。例如:游戏场景、美颜APP中的滤镜等
- 非自动续期订阅(Non‑Renewing Subscriptions):用户可以购买一定时间内的服务或内容访问权限。这种类型的订阅不会自动续订,如果结束之后想继续访问,用户需要购买一个新的订阅。例如,视频或音乐软件中的 1月VIP 或 1年VIP
- 自动续期订阅(Auto-renewable subscriptions):用户可以在你的应用程序中购买内容、服务或高级功能的持续访问。用户在决定取消之前,将按循环次数收取费用。比如,视频软件中连续包月VIP。
苹果还要求为所有可恢复的 App 内购买项目设计一套恢复机制:
如果用户重新下载应用程序或者切换到一个新的设备,一定要立即提供对他们过去在应用程序内购买的内容或功能的访问。根据 App Store Review Guidelines 3.1.1,应用程序必须包含一个恢复机制,用于任何可恢复的应用程序内购买。
来源:App 内购买项目
IAP基本流程
- 加载购买项目的Product Identifiers,这部分可以硬编码在客户端本地或者从服务端获取
- 从AppStore获取产品信息,由StoreKit框架从AppStore获取
- 展示商品UI,这一步也可能是和第一步同时进行,从服务端拉取商品列表包括 Product Identifier
- 请求用户付款,使用StoreKit完成
- 应用程序处理交易,StoreKit回调应用程序。应用程序应该校验交易的正确性,交易生成之后,设备端会有交易的Receipt,可以用于应用程序校验交易的正确性和合法性。
- 解锁内容或权益,应用程序自己的逻辑
- 完成交易,调用StoreKit的方法结束交易。
校验收据有两种类型:
- 在客户端本地对收据进行解析并校验
- 客户端将收据信息进行Base64编码后发往服务端,服务端发到AppStore,由AppStore进行校验,AppStore会返回解码后的收据信息,服务端也可以进行一些校验。
不要在您的 app 内调用 App Store 服务器 verifyReceipt(英文) 端点。您无法直接在用户设备和 App Store 之间建立受信任的连接,因为您无法控制该连接的任何端点,从而容易遭受中间人攻击。
来源:通过 App Store 验证收据
恢复购买
您可以使用 StoreKit 跨设备同步和恢复非消耗型项目和自动续期订阅。当用户购买自动续期订阅或非续期订阅时,您的 app 应当让用户能够在所有设备上访问这一订阅,并让用户能够恢复以前购买的项目。 来源:App 内购买项目
StoreKit
StoreKit有两个版本API:
- In-App Purchase: 一个基于 Swift 的 API,以 JSON Web Signature (JWS) 格式提供 Apple 签名交易验证,从 iOS 15、macOS 12、tvOS 15 和 watchOS 8 开始提供。
- Original API for In-App Purchase : 一个使用 App Store 收据提供交易信息的API,从 iOS 3、macOS 10.7、tvOS 9 和 watchOS 6.2 开始提供。
苹果现在把原来的 StoreKit v1 定义为 Original API for In-App Purchase
,StoreKit v2 定义为 In-App Purchase
, StoreKit V2是在WWDC2021中发布的。这两个 API 都提供了对AppStore中数据的访问。用户使用其中任何一个API进行的应用内购买对这两个API都是完全可用的。
对于开发者如何选择:Choosing a StoreKit API for In-App Purchase
验证收据
收据提供了一个销售记录或者任何购买记录,可以在客户端或者服务端验证收据来验证购买的内容是否真实有效。
如何选择收据验证方法:Choosing a Receipt Validation Technique
消耗型产品的收据在完成交易之后不会再保留在收据中,而非消耗型产品、自动续期订阅、非续期订阅或免费订阅的 App 内购买项目收据无限期保留在收据中。
服务端验证收据
收据的结构:developer.apple.com/documentati…
AppStore 服务端通知
App Store 服务端通知 能够提供用户状态的实时更新,以及与应用内购买相关的关键事件,比如退款或订阅状态的更改。开发者收到这些通知时候可以及时地作出处理。通知的类型有如下几种(notification_type):
INITIAL_BUY
:用户首次购买订阅时发生。DID_RENEW
:用户的订阅已成功自动更新到新的周期,应用程序应该提供对订阅内容的访问。DID_CHANGE_RENEWAL_STATUS
:订阅状态发生变更,表示用户手动在设置中关闭或者打开了自动订阅的开关。DID_FAIL_TO_RENEW
:由于计费问题无法续订。DID_RECOVER
:在自动续订扣费失败之后,Apple自动重试成功。INTERACTIVE_RENEWAL
:升级或者过期之后重新订阅DID_CHANGE_RENEWAL_PREF
:降级REFUND
:App Store
为 "非消耗品","消耗品", "非续期订阅"类型的应用内购买项完成了退款。CANCEL
:用户主动取消了自动续期订阅
,并且用户收到了退款。CONSUMPTION_REQUEST
:用户为应用内购买发起了退款请求,App Store
请求 开发者服务器提供用户的消费数据。PRICE_INCREASE_CONSENT
:表示AppStore已经开始要求用户同意价格上涨请求。REVOKE
:家庭共享相关,暂不介绍。
除了REFUND
是针对"非消耗品","消耗品", "非续期订阅"类型,其他类型的通知均是针对"自动续期订阅"类型。
通知的结构:developer.apple.com/documentati…
苹果在WWDC2021又发布了App Store Server Notifications V2
(Manage in-app purchases on your server - WWDC 2021),细分了更多的订阅通知,但目前没有看到官方文档。
自动续期订阅
自动续期订阅能够让用户持续访问APP中的服务或内容,除非用户选择取消订阅,否则AppStore会在到期时自动续期。
自动续期订阅的净收入结构和 App Store 上的其他商业模式不同。在订阅用户第一年服务的每个结算周期,您会收到订阅价格的 70% (减去适用税款)。订阅用户累积满一年付费服务后,您的收入将增加到订阅价格的 85% (减去适用税款)。
订阅群组以及订阅级别
自动续期订阅群组设置概述这里介绍了订阅群组的一些概述。
一个订阅群组可以包含多个具有不同访问级别、价格和持续时间的订阅,便于用户选择最符合自己需求的选项。但是一个用户只能从一个群组中购买一个订阅,因此对于大多数 app 而言,最佳做法是只创建一个群组,这样可以防止用户意外购买多个订阅。
同时对于一个订阅群组中的订阅产品,可以设置不同的订阅级别。
如果您在一个群组中提供多个订阅价格等级,可以将每个订阅分配到不同级别。这个排序将决定订阅用户可以使用的升级、降级和跨级路径。请按照降序方式对您的订阅进行排序,将提供最新内容、功能或服务访问权限的订阅排在最前面,而不考虑持续时间。如果提供的内容相当,您可以向同一个级别添加多个订阅。
用户可以在 App Store 上的帐户设置中管理他们的订阅。在这里,他们可以看到所有续订选项和订阅群组,并可以随意选择订阅的升级、跨级或降级。当用户更改订阅级别时,根据更改的具体内容不同,更改生效的时间也有所不同:
升级。 用户购买服务级别高于当前订阅的订阅。他们的订阅服务会立即升级,并会获得原始订阅的按比例退款。如果您希望用户能够立即访问更多内容或功能,请为该订阅指定较高排名,将其作为升级选项。
降级。 用户选择服务级别低于当前订阅的订阅。订阅会继续保持不变,直到下一个续订日期,然后以较低级别和价格续订。
跨级。 用户切换到相同级别的新订阅。如果两个订阅的持续时间相同,新订阅会立即生效。如果持续时间不同,新订阅会在下一个续订日期生效。
对于VIP会员的连续包月/包季/包年,最佳做法是在同一个订阅群组中添加三个订阅产品,并设置相同的订阅级别,因为其提供的服务是相同的,只有时限不同。
订阅失效
在订阅到期之前的24小时内,App Store 开始尝试自动续订。在一段时间内,应用商店会多次尝试自动续订,但如果尝试失败次数太多,最终会停止。当第一次扣费失败后,则进入重试状态,在这种状态下,App Store 将最多尝试60天直到过期。开发者也可以为扣费失败的情况提供一个宽限期,如果在宽限期内扣费成功,则用户的订阅周期不会发生变化。
Handling Subscriptions Billing
账单宽限期
账单宽限期是指如果订阅因付款问题而无法自动续期,订阅者在一段时间内仍可访问 App 中的付费内容,在此期间 Apple 将继续尝试收取费用。如果 Apple 能够在您订阅产品的帐单宽限期内恢复订阅,则订阅者的付费服务天数累积将不会中断,您的收益也不会受影响。如果未启用帐单宽限期,该订阅者的付费服务天数将暂停累积,直到 Apple 成功收取费用。但需要注意,启用了账单宽限期之后需要保证在账单宽限期内也能提供服务。
如何知道用户订阅是否可用
- 每个周期扣款之后,客户端会生成receipt,客户端将这个receipt发送到服务端进行校验,服务端校验成功为其开通权限。这种方式严重依赖于客户端,如果用户没有打开客户端,服务端可能收不到新的收据,也就无法开通权益,当用户在其他平台比如Web网页上则无法使用。
- 服务端进行状态轮询。AppStore 的 verifyReceipt 接口除了会返回当前收据的交易信息,还会返回最新的交易信息,所以服务端可以保存上次最新的收据,并进行状态轮询,以便能够及时得到最新的交易。
- AppStore 服务端通知。在每个订阅的生命周期中,AppStore会将关键的事件以通知的形式发送给开发者服务器,服务器收到之后可以进行相关业务逻辑的处理。
退款
对于退款操作,当用户请求退款,并且退款成功之后,苹果会发送通知,通知类型如下:
- CANCEL:对于自动续期类型的订阅。当用户升级到同一个订阅群组中的另一个产品时,也会收到这个通知。
- REFUND:对于其他类型的订阅 开发者服务器收到这两类通知之后可以进行业务上的处理。
在WWDC 2021,苹果在决定是否能给用户退款的决策中加入了一个新的决策影响因素:开发者信号。当用户请求退款之后,AppStore会给开发者服务器发送一个通知,类型为 CONSUMPTION_REQUEST,请求开发者提供一些消费信息,用于协助决策是否给用户退款,开发者可在12小时内调用 AppStore Server API 中的Send Consumption Information接口提供相关信息。如果之后AppStore为用户完成了退款,那么开发者会收到REFUND
或者CANCEL
通知,开发者可进行相应的业务逻辑处理。
App Store 服务端 API
苹果在WWDC2021上发布了 App Store Server API,使开发者可以在服务端查询用户的交易信息,目前提供了三个接口:
- 获取交易历史记录。获取用户在应用中的所有交易记录。Get Transaction History
- 获取所有订阅的状态。获取用户在应用中的所有订阅状态。Get All Subscription Statuses
- 发送消费信息。当用户申请退款时,苹果会通知开发者服务器(
CONSUMPTION_REQUEST
类型),在服务器接收到CONSUMPTION_REQUEST
通知后,可以在12小时内向 App Store 发送关于该用户的消费信息,协助苹果决定是否同意用户退款。Send Consumption Information
JWT验证
新的App Store API 都需要使用 JWT验证。
相关链接
- 苹果iOS内购三步曲:App内退款、历史订单查询、绑定用户防掉单!--- WWDC21
- iOS 内购(In-App Purchase)总结
- 苹果应用内购买(IAP)—从入门到放弃
- www.dengshunlai.com/blog/iap.ht…
- iOS内购IAP - 19篇
Apple 中文文档
- APP审核指南
- Apple 简体中文文档
- App 内购买项目
- StoreKit - App 内购买项目
- StoreKit - 通过 App Store 验证收据
- 自动续期订阅
- App 内购买项目配置流程
Apple 英文文档
- App Store Review Guidelines
- In‑App Purchase
- Auto-renewable Subscriptions
- StoreKit
- StoreKit - Choosing a StoreKit API for In-App Purchase
- StoreKit - In-App Purchase
- StoreKit - Original API for In-App Purchase
- App Store Receipts
- Choosing a Receipt Validation Technique
- App Store Server Notifications
- App Store Server API
WWDC 视频
- All Videos
- Meet StoreKit 2 - WWDC 2021
- Manage in-app purchases on your server - WWDC 2021
- Support customers and handle refunds - WWDC 2021
- What’s new with in-app purchase - WWDC 2020
- Architecting for subscriptions - WWDC 2020
- In-App Purchases and Using Server-to-Server Notifications - WWDC 2019
- Subscription Offers Best Practices - WWDC 2019
- Best Practices and What’s New with In-App Purchases - WWDC 2018
- Engineering Subscriptions - WDC 2018
- What's New in StoreKit - WWDC 2017
- Advanced StoreKit - WWDC 2017