我正在使用 Storekit 在我的应用程序中实现应用内购买。现在已经上线了。我已经实施了非消耗品购买来删除广告。现在的问题是,即使新用户点击恢复购买,它也会成功恢复不应该的购买。由于缺乏测试设备,我无法对其进行调试,但我已要求多人下载该应用程序并恢复购买,他们已成功转换为高级版。
下面是我正在使用的代码:
enum IAPHandlerAlertType {
case initialize
case setProductIds
case disabled
case restored
case purchased
case failed
case error
case restoreFailed
var message: String{
switch self {
case .error: return "An error occured"
case .initialize: return ""
case .setProductIds: return "Product ids not set, call setProductIds method!"
case .disabled: return "Purchases are disabled in your device!"
case .restored: return "You've successfully restored your purchase!"
case .purchased: return "You've successfully bought this purchase!"
case .failed: return "Failed to buy this purchase!"
case .restoreFailed: return "Failed to restore this purchase!"
}
}
}
class IAPManager: NSObject {
//MARK:- Shared Object
//MARK:-
static let shared = IAPManager()
private override init() { }
//MARK:- Properties
//MARK:- Private
fileprivate var productIds = ["com.identifier.appName.removeAds"]
fileprivate var productID = ""
fileprivate var productsRequest = SKProductsRequest()
fileprivate var fetchProductComplition: (([SKProduct])->Void)?
fileprivate var productToPurchase: SKProduct?
var purchaseProductComplition: ((IAPHandlerAlertType, Error?)->Void)?
//MARK:- Public
var isLogEnabled: Bool = true
//MARK:- Methods
//MARK:- Public
//Set Product Ids
func setProductIds(ids: [String]) {
self.productIds = ids
}
//MAKE PURCHASE OF A PRODUCT
func canMakePurchases() -> Bool { return SKPaymentQueue.canMakePayments() }
func purchase(product: SKProduct, completion: @escaping ((IAPHandlerAlertType, Error?) -> Void)) {
self.purchaseProductComplition = completion
self.productToPurchase = product
if self.canMakePurchases() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
log("PRODUCT TO PURCHASE: \(product.productIdentifier)")
productID = product.productIdentifier
}
else {
completion(IAPHandlerAlertType.disabled, nil)
}
}
// RESTORE PURCHASE
func restorePurchase(){
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
// FETCH AVAILABLE IAP PRODUCTS
func fetchAvailableProducts(completion: @escaping (([SKProduct])->Void)){
self.fetchProductComplition = completion
// Put here your IAP Products ID's
if self.productIds.isEmpty {
log(IAPHandlerAlertType.setProductIds.message)
fatalError(IAPHandlerAlertType.setProductIds.message)
}
else {
productsRequest = SKProductsRequest(productIdentifiers: Set(self.productIds))
productsRequest.delegate = self
productsRequest.start()
}
}
//MARK:- Private
fileprivate func log <T> (_ object: T) {
if isLogEnabled {
NSLog("\(object)")
}
}
}
//MARK:- Product Request Delegate and Payment Transaction Methods
extension IAPManager: SKProductsRequestDelegate, SKPaymentTransactionObserver {
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
if let completion = self.fetchProductComplition {
completion(response.products)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
if let completion = self.fetchProductComplition {
completion([])
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
if let completion = self.purchaseProductComplition {
completion(IAPHandlerAlertType.restored, nil)
}
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
if let completion = self.purchaseProductComplition {
completion(IAPHandlerAlertType.restoreFailed, error)
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
log("Product purchase done")
SKPaymentQueue.default().finishTransaction(trans)
if let completion = self.purchaseProductComplition {
completion(IAPHandlerAlertType.purchased, nil)
}
break
case .failed:
log("Product purchase failed")
SKPaymentQueue.default().finishTransaction(trans)
if let completion = self.purchaseProductComplition {
completion(IAPHandlerAlertType.failed, trans.error)
}
break
case .restored:
log("Product restored")
SKPaymentQueue.default().finishTransaction(trans)
if let completion = self.purchaseProductComplition {
completion(IAPHandlerAlertType.restored, nil)
}
break
default: break
}
}
}
}
}
视图模型
func restoreAction() {
Spinner.start()
IAPManager.shared.fetchAvailableProducts { products in
if products.count > 0 {
IAPManager.shared.restorePurchase()
IAPManager.shared.purchaseProductComplition = { [self] result, error in
self.handlePurchaseRestoreResult(result: result, error: error)
Spinner.stop()
}
} else {
Spinner.stop()
}
}
}
func handlePurchaseRestoreResult(result: IAPHandlerAlertType, error: Error?) {
if error != nil {
showAlert = .init(id: .error)
return
}
switch result {
case .disabled:
showAlert = .init(id: .disabled)
case .purchased:
Defaults.isPremiumPurchased = true
Defaults.totalCoins += 1000
isPremiumPurchased = 1
break
case .restored:
Defaults.isPremiumPurchased = true
isPremiumPurchased = 1
break
case .failed:
showAlert = .init(id: .failed)
default:
break
}
}
我在这里做错了什么吗?
在向用户授予访问权限之前,您似乎缺少 validatePurchase 函数。在为用户设置购买状态之前,最好验证用户是否确实从您的数据库购买了溢价。以下是在向用户分发溢价之前您想要的功能的最小实现:-
func isPremiumUserOrNot() {
guard let receiptURL = Bundle.main.appStoreReceiptURL else {
// Handle no receipt case
return
}
// API call to check if the user is in your DB for receiptURL and return the status
}
添加此内容应该可以解决您遇到的问题。