Greg Heo

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:

  1. If the app is already unlocked, we don’t even need to show the “Restore” button. Easy!

  2. If the app hasn’t been unlocked and we receive the paymentQueue:restoreCompletedTransactionsFailedWithError: message, show an error alert to the user.

  3. 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.

  4. 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.