SKPaymentTransaction.transactionState is always .restored

1.1k Views Asked by At

I'm testing my app auto-renewable subscriptions but SKPaymentTransaction.transactionState never changes to .purchased and it's always .restored. Everything was working ok but I can get nothing but .restored since I first called SKPaymentQueue.default().restoreCompletedTransactions(). I tried deleting the app from my iPhone, relogin into my sandbox account, creating a new sandbox account, restarting Xcode, cleaning the build folder,... What I'm doing wrong? Please find my code below:

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        switch transaction.transactionState {
        case .purchased:
            SKPaymentQueue.default().finishTransaction(transaction)
            print(".purchased")
        case .failed:
            SKPaymentQueue.default().finishTransaction(transaction)
            print(".failed")
        case .restored:
            SKPaymentQueue.default().finishTransaction(transaction)
            print(".restored")
        default:
            break
        }
    }
}

@IBAction func restore(_ sender: UIBarButtonItem) {
    SKPaymentQueue.default().restoreCompletedTransactions()
}

func subscribe() {
    if SKPaymentQueue.canMakePayments() {
        let payment = SKPayment(product: availableProducts[indexPath.row])
        SKPaymentQueue.default().add(payment)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    SKPaymentQueue.default().add(self)
}
3

There are 3 best solutions below

0
On

You should check result of queue

let transactions = queue.transactions
    if transactions.count > 0 {
        for transaction in queue.transactions {
            if (transaction.transactionState == SKPaymentTransactionState.failed) {
                //possibly handle the error
                purchaseStatusBlock?(.failed)
            } else if (transaction.transactionState == SKPaymentTransactionState.purchased) {
                //deliver the content to the user
                purchaseStatusBlock?(.purchased)
            } else if (transaction.transactionState == SKPaymentTransactionState.restored) {
                //deliver the content to the user
                purchaseStatusBlock?(.restored)
            } else {
                //handle other transaction states
            }
        }
    } else {
        purchaseStatusBlock?(.failed)
    }
0
On

I found here: Clearing SKPAymentsQueue : Forcing Unfinished Transactions To Finish that my problem was that I had some unfinished pending transactions so I added this into my viewDidLoad to "force" finishing them all. Should I permanently include it to avoid it from happening again?

let currentQueue: SKPaymentQueue = SKPaymentQueue.default();
for transaction in currentQueue.transactions {
    currentQueue.finishTransaction(transaction);
}
2
On

Probably,it's always .restored, because the transaction is not finished.


Usually we put SKPaymentQueue.default().add(observer) in AppDelegate,

so that the transaction will always get handled.

 class AppDelegate{

      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            SKPaymentQueue.default().add(self)
      }

}

extension AppDelegate: SKPaymentTransactionObserver {

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

When u put SKPaymentQueue.default().add(observer) in ViewController, since ViewController maybe get pushed and popped.

That is to say , the ViewController instance get allocated and deallocated .

Since the transaction handling is asynchronous, the ViewController instance corresponding to its transaction, maybe not existed, or not the same one.

So it behaves wieldly.