Posts

Post not yet marked as solved
10 Replies
5.1k Views
I am trying to restore Non-Consumable IAP with SKPaymentQueue.default().restoreCompletedTransactions() and nothing work according to plan. In sandbox I get "There's no information available for In-App Purchases. Try again later. 21105" and in Prod just nothing happens. From what I can see that error in Sandbox is thrown with actual SKPaymentQueue.default().restoreCompletedTransactions() line and nothing is processed after that. I am sure everything is right, since I can make my purchase by hand and it works, I can also repurchase anything again with error, that I already have it and then item get activated, but Restore button is not working (restorePurchases()). I do have 38 IAP in this app, but I am not sure if that is related in any way, and they all are Non-Consumable. It is iMessage Extension that sells stickers. What is also strange, is that Apple did kick my app back because I used my Own Art without My own permission (that was halarious), but everything states, that with Non-Consumable must have Restore and Apple check for that, and in this case everything passed while it is not working.My whiole IAP handlicg class look like this:import Foundation import StoreKit import os.log class IAPService: NSObject { private override init() {} // make sure there is no extra copies static let shared = IAPService() // makes this singleton fileprivate var products = [SKProduct]() let paymentQueue = SKPaymentQueue.default() let defaults = UserDefaults.standard var haveData = false var reference = StickewrsCollectionViewController() fileprivate var request = SKProductsRequest() let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "IAPService") let receiptFetcher = ReceiptFetcher() func getProducts() { let allStickersIAP = “IAP reference” var iapList = Dictionary<string, string="">() if let path = Bundle.main.path(forResource: "StickersData", ofType: ".plist") { let dict = NSDictionary(contentsOfFile: path) as! Dictionary<string, anyobject=""> let globalData = dict["StickerData"] as! Array iapList = (globalData[1]) as! Dictionary<string, string=""> } var products: Set = [allStickersIAP] for iap in iapList { products.insert(iap.value) } request = SKProductsRequest(productIdentifiers: products) request.delegate = self request.start() paymentQueue.add(self) SKPaymentQueue.default().add(self) os_log("Getting products") } func purchase(product: String) { if (SKPaymentQueue.canMakePayments()) { guard let productToPurchase = products.filter({$0.productIdentifier == product}).first else {return} let payment = SKPayment(product: productToPurchase) paymentQueue.add(payment) } } func restorePurchases() { print("Restoring purchases") os_log("Restoring purchases") SKPaymentQueue.default().restoreCompletedTransactions() } func iapCheck() -> Bool { return SKPaymentQueue.canMakePayments() && haveData } func getProductData(iap: String) -> SKProduct { return products.filter({$0.productIdentifier == iap}).first ?? SKProduct() } func priceStringForProduct(item: SKProduct) -> String? { let price = item.price if price == NSDecimalNumber(decimal: 0.00) { return NSLocalizedString("free", comment: "") } else { let numberFormatter = NumberFormatter() let locale = item.priceLocale numberFormatter.numberStyle = .currency numberFormatter.locale = locale return numberFormatter.string(from: price) } } func setReference(ref: StickewrsCollectionViewController) { reference = ref } public func passPopUp(_ text: String) { reference.ShowPopUp(text) } // Called when the application is about to terminate. func applicationWillTerminate(_ application: UIApplication) { // Remove the observer. SKPaymentQueue.default().remove(self) } } extension IAPService: SKProductsRequestDelegate, SKPaymentTransactionObserver { func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { products = response.products haveData = true } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchasing: break case .purchased: do { defaults.set(true, forKey: transaction.payment.productIdentifier) defaults.synchronize() // save changes in PlayerPrefs print(transaction.payment.productIdentifier) queue.finishTransaction(transaction) } case .restored: do { print("Do actual restoring") os_log("Now restoring %@", transaction.original!.payment.productIdentifier) reference.ShowPopUp("Restoring purchase \(transaction.original!.payment.productIdentifier)") defaults.set(true, forKey: transaction.original!.payment.productIdentifier) defaults.synchronize() // save changes in PlayerPrefs print(transaction.payment.productIdentifier) queue.finishTransaction(transaction) } default: queue.finishTransaction(transaction) } } reference.redrawnAfterPurchase() } func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { for transaction in queue.transactions { let t: SKPaymentTransaction = transaction let prodID = t.payment.productIdentifier as String defaults.set(true, forKey: prodID) defaults.synchronize() // save changews in PlayerPrefs print(prodID) queue.finishTransaction(transaction) } reference.redrawnAfterPurchase() reference.ShowPopUp(NSLocalizedString("restoreCompleted", comment: "")) } } extension SKPaymentTransactionState { func status() -> String { switch self { case .deferred: return "deferred" case .failed: return "failed" case .purchased: return "purchased" case .purchasing: return "purchasing" case .restored: return "restored" } } }Whart I did wrong and how I can make RestorePurchases actually do something ?
Posted
by Burve.
Last updated
.