Restoring In-app Purchases
On the pesky requirement of having a "Restore Purchases" button in your iOS app.
I’ve noticed a bit of chatter on the Internets about this bit in Apple’s In-App Purchase Programming Guide:
…if your application supports product types that must be restorable, you must include an interface that allows users to restore these purchases
What the heck? Where am I supposed to find room for a “Restore” button in my app, you ask? Luckily, that’s your problem; what I’ll talk about here is how to handle the implementation.
Regular In-app Purchase
Let’s go over the basics of the simplest purchase before talking about the restore procedure:
SKPayment *payment = [SKPayment paymentWithProductIdentifier:@"productId"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
StoreKit will take over and if all is well, your transaction observer (self
in this case) will receive a call to this method:
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
The transactions
array will contain SKPaymentTransaction
objects, each with a
transactionState
property. This will usually be SKPaymentTransactionStatePurchased
for a successful purchase.
Anyhow, you already know all this. Let’s talk about the dreaded restore.
The Restoration
The general workflow is pretty similar. Rather than “purchase” a specific
product, send the restoreCompletedTransactions
message to the queue.
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
Again, StoreKit will take over and call paymentQueue:updatedTransactions:
for each restored purchase. These will look an awful like regular purchases
except the transactionState
will be SKPaymentTransactionStateRestored
.
The original transaction details are available in
transaction.originalTransaction
.
Once all the paymentQueue:updatedTransactions:
calls are made, the
transaction observer will receive this callback:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
If something goes wrong (StoreKit error, invalid password, user hits “Cancel”, etc.) you’ll receive this callback instead:
- (void)paymentQueue:(SKPaymentQueue *)queue
restoreCompletedTransactionsFailedWithError:(NSError *)error
Implementation Details
For a simple app, say with a single “unlock app” kind of in-app purchase, it would be easy to handle the various use cases:
If the app is already unlocked, we don’t even need to show the “Restore” button. Easy!
If the app hasn’t been unlocked and we receive the
paymentQueue:restoreCompletedTransactionsFailedWithError:
message, show an error alert to the user.If the app hasn’t been unlocked, we receive the
paymentQueueRestoreCompletedTransactionsFinished:
message, and the app still isn’t unlocked, show a “Sorry, no previous purchases could be restored” kind of message.If the app hasn’t been unlocked, we receive the
paymentQueueRestoreCompletedTransactionsFinished:
message, and the app is now unlocked, show a “Restore successful!” kind of message.
In Closing
Apps with more complicated IAP usage might need more complicated IAP restore procedures, but I hope this is a good start.