Gotchas of Testing Subscriptions with Sandbox, Part 3
Here’s how to handle in-app purchase transactions in StoreKit:
- Implement
SKPaymentTransactionObserver
. - Wait for
paymentQueue(_:updatedTransactions:)
to get called when transactions are updated. - Loop through the transactions:
- If the
transactionState
is.purchased
or.restored
, unlock the in-app purchase. - If the
transactionState
is.failed
, show an error or something. - Other states are less important, and you can ignore them if you want.
- If the
- Call
finishTransaction(_:)
on.purchased
,.restored
, and.failed
transactions to remove them from the queue.
If you don’t call finishTransaction(_:)
, the transactions will remain in the queue and show up over and over again. But it seems like finished transactions don’t always get removed in the sandbox environment.
I noticed that the receipt validation endpoint on my server gets called every time I launch my app. But it’s only supposed to happen when I process the transactions in paymentQueue(_:updatedTransactions:)
.
So I added a few print
statements, and as I suspected, paymentQueue(_:updatedTransactions:)
is getting called whenever I launch the app.
Maybe there’s a bug in my code? So I simplified paymentQueue(_:updatedTransactions:)
to this:
func paymentQueue(
_ queue: SKPaymentQueue,
updatedTransactions transactions: [SKPaymentTransaction]
) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased, .restored:
queue.finishTransaction(transaction)
case .failed:
queue.finishTransaction(transaction)
default:
break
}
}
}
Nope, the transactions still keep coming back.
Next, I implemented paymentQueue(_:removedTransactions:)
to confirm that the transactions are getting removed from the queue:
func paymentQueue(
_ queue: SKPaymentQueue,
removedTransactions transactions: [SKPaymentTransaction]
) {
print("Removed \(transactions.count)")
print("Remaining \(queue.transactions.count)")
}
They were removed, but come back after a relaunch for some reason.
And no, I didn’t register the observer twice: I start observing in application(_:didFinishLaunchingWithOptions:)
and remove the observer in applicationWillTerminate(_:)
.
I’m not really sure what’s going on? I just hope that this is one of the gotchas with subscriptions and sandbox and doesn’t happen in production.