MerchantKit
一个面向 iOS 开发的现代 In-App Purchases 管理框架。
MerchantKit
极大地简化了独立开发者为了向应用添加付费组件所做的工作,包括跟踪已购买的产品、提供自动续订订阅、恢复交易等。
MerchantKit
专为具有有限购买产品集合的应用设计,是向应用添加一次性购买或持续订阅的“专业版”的绝佳方式。
示例代码片段
确定产品是否已购买
let product = merchant.product(withIdentifier: "iap.productidentifier")
print("isPurchased: \(merchant.state(for: product).isPurchased))"
购买产品
let task = merchant.commitPurchaseTask(for: purchase)
task.onCompletion = { result in
switch result {
case .succeeded(_):
print("purchase completed")
case .failed(let error):
print("\(error)")
}
}
task.start()
当订阅过期时收到通知
public func merchant(_ merchant: Merchant, didChangeStatesFor products: Set<Product>) {
if let subscriptionProduct = products.first(where: { $0.identifier == "subscription.protier" }) {
let state = merchant.state(for: subscriptionProduct)
switch state {
case .isPurchased(let info):
print("subscribed, expires \(info.expiryDate)")
default:
print("does not have active subscription")
}
}
}
项目目标
- 直观简练的 API,支持非消耗性、消耗性和订阅内购。
- 简化了应用程序中内购界面的开发,包括用于动态创建例如“每月 £2.99”或“七天免费试用”等字符串的本地化格式化程序。
- 除苹果 iOS 随附的组件外,无外部依赖。该项目链接
Foundation
、StoreKit
、SystemConfiguration
和os
用于日志记录。 - 优先考虑开发者的便利性和可访问性,而非安全性。
MerchantKit
用户承认一定程度的盗版是不可避免的,不值得追查。 - 宽宏大量的开源许可证。
- 使用惯用语言结构与最新版本的 Swift 兼容。
当前的代码库处于不断变化中,该项目不保证 API 的稳定性。尽管如此,MerchantKit
是有用的、有效的,并且可能会为您节省时间。但话虽如此,MerchantKit
还远远没有完成。测试套件尚不完善。
安装
CocoaPods
要使用CocoaPods将MerchantKit
集成到您的Xcode项目中,请在您的Podfile
中指定它。
pod 'MerchantKit'
Carthage
要使用Carthage将MerchantKit
集成到您的Xcode项目中,请在您的Cartfile
中指定它。
github "benjaminmayo/merchantkit"
手动
编译MerchantKit
框架并将其嵌入到您的应用程序中。您可以从Github下载源代码并将Xcode项目嵌入到您的应用程序中,但您需要手动升级到最新版本。
入门指南
- 在您的应用程序代理中导入
MerchantKit
并在application(_:, didFinishLaunchingWithOptions:)
中创建一个Merchant
实例。提供一个配置(如Merchant.Configuration.default
)和一个代理。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
self.merchant = Merchant(configuration: .default, delegate: self)
...
}
- 尽快注册产品(通常在
application(_:, didFinishLaunchingWithOptions:)
中)。您可能希望从文件中加载Product
结构,或者简单地以常量的形式在代码中声明它们。然后,这些常量可以在以后静态访问。
let product = Product(identifier: "iap.productIdentifier", kind: .nonConsumable)
let otherProduct = Product(identifier: "iap.otherProductIdentifier", kind: .subscription(automaticallyRenews: true))
self.merchant.register([product, otherProduct])
- 在脱离
application(_:, didFinishLaunchingWithOptions:)
方法之前,在商家实例上调用setup()
。这告诉商家开始观察支付队列。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
self.merchant = Merchant(configuration: .default, delegate: self)
self.merchant.register(...)
self.merchant.setup()
...
}
- 利润!或者something.
商户配置
Merchant
使用配置对象初始化;一个 Merchant.Configuration
的实例。该配置控制了如何验证收据并将产品状态持久化到存储。大多数应用程序可以直接使用 Merchant.Configuration.default
并获得预期的结果。如果您需要更定制化的功能,可以提供自己的 Merchant.Configuration
。
提示:MerchantKit
提供了一个内置的配置 Merchant.Configuration.usefulForTestingAsPurchasedStateResetsOnApplicationLaunch
。在开发过程中,用于测试购买流程,因为这个配置不会将购买状态持久化到永久存储。您只需重新启动应用程序,就可以反复测试“购买”任何 Product
,包括非消耗品。正如其难以记的名字所示,您不应该在生产应用程序中使用此配置。
商户代理
代理实现 MerchantDelegate
协议。该代理提供了在应用全局级别响应事件的途径。该 MerchantDelegate
协议声明了一些方法,但只需实现其中一个。
func merchant(_ merchant: Merchant, didChangeStatesFor products: Set<Product>) {
// Called when the purchased state of a `Product` changes.
for product in products {
print("updated \(product)")
}
}
代理可以选择接收加载状态更改事件以及处理由 App Store 外部发起的推广内购流程的定制化处理点。这两个方法的合理默认实现已经被提供。
产品接口控制器
Merchant
提供的任务让开发者可以通过反映 Swift 语法特性的接口访问核心的获取和购买产品操作。许多应用程序不需要直接实例化任务。ProductInterfaceController
是由 MerchantKit
提供的一个较高级别的 API,覆盖了许多项目的用例。在一个 iOS 应用中,用于显示升级界面的视图控制器将由一个单一 ProductInterfaceController
支持,该控制器封装了所有必要的产品和购买逻辑。
ProductInterfaceController
类包括展示 In-App Purchase 所需的常见行为。然而,它仍然足够抽象,不会固定于某种特定的用户界面外观或布局。
开发者只需提供要显示的产品列表,并告诉控制器获取数据。控制器通过 delegate
通知应用程序何时更新其自定义 UI。它处理数据加载、间歇性网络连接以及产品可用性和状态的变化。
请参阅示例项目,了解ProductInterfaceController
的基本实现。
格式化工具
MerchantKit
包含了几个格式化工具,以帮助开发者向用户展示应用内购的价格。
PriceFormatter
是最简单的。只需给它一个Price
对象,它就会返回如 '£3.99' 或 '$5.99' 的格式化字符串,遵循商店的区域设置。如果价格免费,您可以指定一个自定义字符串。SubscriptionPriceFormatter
接收一个 Price
和一个 SubscriptionDuration
。相关的 Purchase
对象公开这些值,因此您可以轻松地将它们传递给格式化工具。它根据周期和订阅是否会自动续订生成如 '$0.99每月','£9.99每两周' 和 '$4.99一个月' 的字符串。
除了续订周期,订阅还可以包含免费试用期和其他促销活动。您可以使用SubscriptionPeriodFormatter
来格式化应用程序中的文本标签。如果您在 iTunes Connect 中更改免费试用优惠,则标签将动态更新以反映更改的条款,而无需新的 App Store 二进制文件。例如
func subscriptionDetailsForDisplay() -> String? {
guard let terms = purchase.subscriptionTerms, let introductoryOffer = terms.introductoryOffer else { return nil }
let formatter = SubscriptionPeriodFormatter()
switch introductoryOffer {
case .freeTrial(let period): return "\(formatter.string(from: period)) Free Trial" // something like '7 Day Free Trial'
default: ...
}
}
PriceFormatter
在 App Store 支持的每个区域设置中都能使用。SubscriptionPriceFormatter
和 SubscriptionPeriodFormatter
目前仅供少数语言使用。欢迎提供自愿翻译。
请参阅示例项目,该项目中可以实验 PriceFormatter
和 SubscriptionPriceFormatter
的各种配置选项。
消耗型产品
Merchant
跟踪非消耗型和订阅产品的购买状态。消耗型产品被认为是过渡性购买,不会在首次购买时间之后记录。由于其特殊性质,必须以不同的方式处理。在创建 Merchant 时确保提供consumableHandler
。这可以是任何符合 MerchantConsumableProductHandler
协议的对象。该协议有一个必需的方法
func merchant(_ merchant: Merchant, consume product: Product, completion: @escaping () -> Void) {
self.addCreditsToUserAccount(for: product, completion: completion) // application-specific handling
}
Merchant
将始终报告消耗型产品的状态为 PurchasedState.notPurchased
。忘记实现代理方法会导致运行时致命错误。
待完成(无特定顺序)
- 增加
SubscriptionPriceFormatter
和SubscriptionPeriodFormatter
的本地化数量。 - 在示例项目中添加了大量文档,包括更多示例。
- 支持内置购买可下载内容。
- 可能还有很多我还没想出来的功能。
鸣谢
由 Benjamin Mayo 开发和管理,Twitter上的 @bzamayo。