Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with old transactions when dealing with restoring purchases?

I'm currently developing and testing my app with a sandbox user. Even though the transactions have been completed, when I try to Restore Purchases, I get as much as 32 old transactions from the queue.

Essentially I would like to alert as in Your purchase is being restored. Upon completion this dialog will close. and dismiss it when it's finished.

private func showRestoreInProgressAlert() {
        let alert = UIAlertController(title: "Restoring Purchase", message: "Your purchase history is being restored. Upon completion this dialog will close.", preferredStyle: .alert)
        present(alert, animated: true, completion: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(dismissRestoreInProgressAlert(notification:)), name: SubscriptionService.restoreSuccessfulNotification, object: nil)
    }

You may recognise this method below from SKPaymentTransactionObserver. Once the notification .restoreSuccessfulNotification has been sent, the alert would be dismissed as expected. But because there are 32 transactions in the queue, the popup keeps appearing and disappearing 32 times.

func handleRestoredState(for transaction: SKPaymentTransaction, in queue: SKPaymentQueue) {
        print("Purchase restored for product id: \(transaction.payment.productIdentifier)")
        queue.finishTransaction(transaction)
        SubscriptionService.shared.uploadReceipt { (success) in
            DispatchQueue.main.async {
                NotificationCenter.default.post(name: SubscriptionService.restoreSuccessfulNotification, object: nil)
            }
        }
    }


func paymentQueue(_ queue: SKPaymentQueue,
                      updatedTransactions transactions: [SKPaymentTransaction]) {

        for transaction in transactions {
            switch transaction.transactionState {
            case .purchasing:
                handlePurchasingState(for: transaction, in: queue)
            case .purchased:
                handlePurchasedState(for: transaction, in: queue)
            case .restored:
                handleRestoredState(for: transaction, in: queue)
            case .failed:
                handleFailedState(for: transaction, in: queue)
            case .deferred:
                handleDeferredState(for: transaction, in: queue)
            }
        }
    }

I already finish the transactions both in handlePurchasedState and handleRestoredState like this:

queue.finishTransaction(transaction)

So why do I still have so many old transactions sitting in the queue, whenever I click restore purchases?

UPDATE

This could be indeed an issue with the sandbox.

I tried to do a count but that doesn't help, simply because not all these transactions seem to be restorable.

I did the "hard-reset":

for transaction: AnyObject in SKPaymentQueue.default().transactions {
            guard let currentTransaction: SKPaymentTransaction = transaction as? SKPaymentTransaction else {return}
            SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
        }

Now it seems that slowly the transactions are reducing to zero, which means Sandbox is now losing them.

Why is SKPaymentQueue.default().finishTransaction() working, but queue.finishTransaction() didn't? Should I refactor my code to use SKPaymentQueue.default().finishTransaction() instead to be on the safe side? Or is that just a bad day with the IAP sandbox?

like image 290
Houman Avatar asked Dec 18 '17 14:12

Houman


2 Answers

Restoration re-delivers all purchases to your transaction observer delegate. That is the intended behaviour.

However, displaying the alert in the way that you want is quite straight-forward.

  1. When the user starts the restore operation, display the alert.

  2. Then, once all items are restored, you will get a call to the paymentQueueRestoreCompletedTransactionsFinished delegate method. In this method you can dismiss your alert.

like image 125
Paulw11 Avatar answered Jan 01 '23 20:01

Paulw11


All active subscription and all non-consumable items (purchased by the current user) will be returned each time you request a restore.

Think about it, if they would not be returned anymore after you finish, how would you be able to restore?

Apparently your app can have multiple active subscriptions and/or non-consumables for a user. To avoid getting multiple alerts, you should combine all StoreKit restore callback into one (or a few) user notifications.

Does this make sense in your situation?

like image 20
meaning-matters Avatar answered Jan 01 '23 19:01

meaning-matters