我的SKProductsRequestDelegate和SKPaymentTransactionObserver的设置几乎与StoreKit In-App Purchase Documentations中所述相同。
获取可用的SKProducts并购买非消耗性产品产品效果很好,我可以在func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
中处理不同的交易状态。但是有一种情况我不知道如何处理。这种情况是当我尝试购买已经购买的物品时。然后,商店UI会告诉用户类似的信息:“此应用内购买已被购买。它将被还原,而无需支付额外费用。”]
图片文字的粗略翻译:
[我的第一个假设是,应该调用.restored的情况,但事实并非如此。我已经浏览了几次文档,但无法弄清楚:,(。所以我的问题是:我在哪里可以处理此事件?
让我知道我是否可以以任何方式澄清这个问题。提前致谢! :)
我的StoreKit代码:
import Foundation
import StoreKit
import SwiftUI
class StoreHelper: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver, ObservableObject {
@Published var transactionState: [SKPaymentTransaction] = [SKPaymentTransaction]()
static let shared = StoreHelper()
func restore() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
//Initialize the store observer.
private override init() {
super.init()
// create productsIDs.plist path
guard let url = Bundle.main.url(forResource: "ProductIDs", withExtension: "plist") else { fatalError("Unable to resolve url for in the bundle.") }
do {
let data = try Data(contentsOf: url)
if let productIdentifiers = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: nil) as? [String] {
self.validate(productIdentifiers: productIdentifiers)
}
} catch let error as NSError {
print("StoreObserver: \(error.localizedDescription)")
}
}
// Keep a strong reference to the product request.
var request: SKProductsRequest!
func validate(productIdentifiers: [String]) {
let productIdentifiers = Set(productIdentifiers)
request = SKProductsRequest(productIdentifiers: productIdentifiers)
request.delegate = self
request.start()
}
@Published var prod = [StoreProduct]()
var products = [SKProduct]()
// SKProductsRequestDelegate protocol method.
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if !response.products.isEmpty {
products = response.products
// Adjusting published values must be done on the main thread...
DispatchQueue.main.async {
self.prod = self.products.map{
StoreProduct(id: $0.productIdentifier, name: $0.localizedTitle, price: $0.regularPrice!, product: $0)
}
}
}
for invalidIdentifier in response.invalidProductIdentifiers {
// Handle any invalid product identifiers as appropriate.
}
}
// MARK: - Buy Selected Product
func buyProduct(product: SKProduct) {
// Use the corresponding SKProduct object returned in the array from SKProductsRequest.
let payment = SKMutablePayment(product: product)
// payment.quantity = 2
SKPaymentQueue.default().add(payment)
print("StoreObserver: ----> BUY ADDED")
}
// MARK: - PROCESS PAYMENT
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("StoreObserver: -------------> QUEUE")
for transaction in transactions {
switch transaction.transactionState {
// Call the appropriate custom method for the transaction state.
case .purchasing: print("StoreObserver: InProgress \(transaction.payment.description)")
case .deferred: print("StoreObserver: inProgress_deffered \(transaction.payment.description)")
case .failed: print("StoreObserver: failed \(transaction.payment.description)") // pressing: cancel
case .purchased:
print("StoreObserver: complete! \(transaction.transactionIdentifier)")
case .restored: print("StoreObserver: restored \(transaction.payment.productIdentifier)")
// For debugging purposes.
@unknown default: print("StoreObserver: Unexpected transaction state \(transaction.payment.description)")
}
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("StoreObserver: restoreCompletedTransactionsFinished \(queue.description)")
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("StoreObserver: \(error)")
}
}
extension SKProduct {
/// - returns: The cost of the product formatted in the local currency.
var regularPrice: String? {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = self.priceLocale
return formatter.string(from: self.price)
}
}
如果您的应用销售非消耗性,可自动更新的订阅或不可更新的订阅产品,则必须为用户提供恢复它们的能力。在完成付款交易之前,请务必提供购买或恢复的内容。
因此,如果我对问题的理解正确,您可以在IAP屏幕中放置“恢复购买”按钮,这将解决您的问题。