InAppPurchase
一个简单、轻量级且安全的内部购买框架
特性
- 简单且轻量
👍 - 支持推广内部购买
💰 - 无需考虑
StoreKit
!😎 - 覆盖率高且安全
✅
安装
Carthage
github "jinSasaki/InAppPurchase"
CocoaPods
pod "InAppPurchase"
使用
设置观察者
注意:该方法应在启动时调用。
let iap = InAppPurchase.default
iap.addTransactionObserver(fallbackHandler: {
// Handle the result of payment added by Store
// See also `InAppPurchase#purchase`
})
如果您想检测意外的交易,请将 addTransactionObserver()
与 fallbackHandler
一起传递。
例如,您的应用请求了一笔支付,但在该过程中崩溃了。这笔交易未完成,将在下一次启动时接收。
当任何处理程序未被通过 purchase(productIdentifier: handler:)
方法等设置为 InAppPurchase
时,将调用此 fallbackHandler
。
自iOS 11起,推广内购是可用的。《InAppPurchase》支持它!
使用 shouldAddStorePaymentHandler
添加观察者。
另请参阅 SKPaymentTransactionObserver#paymentQueue(_:shouldAddStorePayment:for:)
和 推广内购指南
let iap = InAppPurchase.default
iap.set(shouldAddStorePaymentHandler: { (product) -> Bool in
// Return whether starting payment
}, handler: { (result) in
// Handle the result of payment added by Store
// See also `InAppPurchase#purchase`
})
Product#priceLocale
只有当通过 AppStorePromoting 进行购买时,SKProduct#priceLocale
才未初始化。这会导致一个 BAD_ACCESS 崩溃。这是一个 StoreKit 错误。InAppPurchace 解决了在收到付款时发生的崩溃,但它在访问 Product#priceLocale
时尚未解决。因此,我建议在 AppStore Promoting 支付过程中不要使用 Product#priceLocale
。
如有需要,停止支付观察。
let iap = InAppPurchase.default
iap.removeTransactionObserver()
获取产品信息
let iap = InAppPurchase.default
iap.fetchProduct(productIdentifiers: ["PRODUCT_ID"], handler: { (result) in
switch result {
case .success(let products):
// Use products
case .failure(let error):
// Handle `InAppPurchase.Error`
}
})
恢复已完成的交易
let iap = InAppPurchase.default
iap.restore(handler: { (result) in
switch result {
case .success(let productIds):
// Restored with product ids
case .failure(let error):
// Handle `InAppPurchase.Error`
}
})
购买
let iap = InAppPurchase.default
iap.purchase(productIdentifier: "PRODUCT_ID", handler: { (result) in
// This handler is called if the payment purchased, restored, deferred or failed.
switch result {
case .success(let response):
// Handle `PaymentResponse`
case .failure(let error):
// Handle `InAppPurchase.Error`
}
})
交易处理
如果您想处理交易完成的时机,请在初始化时将 shouldCompleteImmediately
设置为 false
。
let iap = InAppPurchase(shouldCompleteImmediately: false)
iap.purchase(productIdentifier: "PRODUCT_ID", handler: { (result) in
// This handler is called if the payment purchased, restored, deferred or failed.
switch result {
case .success(let response):
// Handle `PaymentResponse`
// MUST: InAppPurchase does not complete transaction, if purchased, restored. Your app must call `InAppPurchase.finish(transaction:)`.
if response.state == .purchased || response.state == .restored {
iap.finish(transaction: response.transaction)
}
case .failure(let error):
// Handle `InAppPurchase.Error`
}
})
多个 InAppPurchase 实例
如果您想使用多个 InAppPurchase,请创建每个实例。
但是,由于重复处理,请注意回退处理。
这是重复处理示例
let iap1 = InAppPurchase.default
let iap2 = InAppPurchase(shouldCompleteImmediately: false)
iap1.addTransactionObserver(fallbackHandler: {
// NOT CALLED
// This fallback handler is NOT called because the purchase handler is used.
})
iap2.addTransactionObserver(fallbackHandler: {
// CALLED
// This fallback handler is called because the purchase handler is not associated to iap2.
})
iap1.purchase(productIdentifier: "your.purchase.item1", handler: { (result) in
// CALLED
})
为了避免这种情况,我建议您为每个实例指定产品 ID。
let iap1 = InAppPurchase(shouldCompleteImmediately: true, productIds: ["your.purchase.item1", "your.purchase.item2"])
let iap2 = InAppPurchase(shouldCompleteImmediately: false, productIds: ["your.purchase.item3", "your.purchase.item4"])
iap1.addTransactionObserver(fallbackHandler: {
// NOT CALLED
// This fallback handler is NOT called because the purchase handler is used.
})
iap2.addTransactionObserver(fallbackHandler: {
// NOT CALLED
// This fallback handler is NOT called because "your.purchase.item1" is not specified for iap2.
})
iap1.purchase(productIdentifier: "your.purchase.item1", handler: { (result) in
// CALLED
})
此外,如果您没有指定 productIds
或将其设置为 productIds: nil
,则 iap 实例允许所有产品 ID。
用于依赖注入
应用中的购买逻辑应该是安全且可测试的。
例如,您按照以下方式实现了执行 In-App-Purchase 的类。
// PurchaseService.swift
import Foundation
import InAppPurchase
final class PurchaseService {
static let shared = PurchaseService()
func purchase() {
// Purchase with `InAppPurchase`
InAppPurchase.default.purchase(productIdentifier: ...) {
// Do something
}
}
}
由于在购买过程中使用了 InAppPurchase.default
,因此难以测试这个类。
可以将此 PurchaseService
重构为注入依赖关系。
使用 InAppPurchaseProvidable
协议。
// PurchaseService.swift
import Foundation
import InAppPurchase
final class PurchaseService {
static let shared = PurchaseService()
let iap: InAppPurchaseProvidable
init(iap: InAppPurchaseProvidable = InAppPurchase.default) {
self.iap = iap
}
func purchase() {
// Purchase with `InAppPurchase`
iap.purchase(productIdentifier: ...) {
// Do something
}
}
}
然后您可以使用 InAppPurchaseStubs.framework
轻松地测试 PurchaseService
。
// PurchaseServiceTests.swift
import XCTest
@testable import YourApp
import InAppPurchaseStubs
// Test
final class PurchaseServiceTests: XCTestCase {
func testPurchase() {
let expectation = self.expectation(description: "purchase handler was called.")
let iap = StubInAppPurchase(purchaseHandler: { productIdentifier, handler in
// Assert productIdentifier, handler, and so on.
})
let purchaseService = PurchaseService(iap: iap)
purchaseService.purchase(productIdentifier: ...) {
// Assert result
expectation.fulfill()
}
wait(for: [expectation], timeout: 1)
}
}
如果您需要更多信息以进行测试,请参阅 InAppPurchaseStubs 和 Tests。
需求
- 支持iOS 9.0+
- 支持Xcode 9+
- 支持Swift 4+
许可
MIT