44

I have implemented in app purchases into my app update for the first time, only too wait 3 weeks and have it rejected for the following reason:

We found that your app offers In-App Purchase/s that can be restored but it does not include a "Restore" feature to allow users to restore the previously purchased In-App Purchase/s. To restore previously purchased In-App Purchase products, it would be appropriate to provide a "Restore" button and initiate the restore process when the "Restore" button is tapped.

Now I was thinking of adding a navbar button to the right (top) of my table where the app purchases can be seen/tapped and adding the following code that will be linked to the button:

 [[SKPaymentQueue defaultQueue]   restoreCompletedTransactions];

Can someone verify that this is correct and most likely all that is needed? Would like this to pass successfully this time. Thanks in advance!

Jojodmo
  • 23,357
  • 13
  • 65
  • 107
Alex G
  • 2,299
  • 5
  • 37
  • 54
  • 7
    I got the same damn rejection notice. Do you always provide a "restore" button?!?! I want to automatically check to see if there where restorable in-app purchases but when I call the restoreCompletedTransactions the user is prompted for a password. That sucks! This there another API for this? Can I just change my "BUY" button to "BUY/RESTORE"??? Which still sucks. I do not have room for a full time "RESTORE" button. – whatchamacallit Jun 27 '12 at 22:20
  • 1
    @whatchamacallit All I did was put the following code from above to a method connected to a button in the navbar and it got approved. If you do not have room for the button in the navbar then you probably need to put it in the view itself... perhaps in the table or above the table. Goodluck! – Alex G Jun 28 '12 at 11:57
  • 5
    I don't see any Restore button on Angry Birds for Mighty Eagle? Why isn't it rejected? – erkanyildiz Jun 29 '12 at 19:59
  • 1
    And there is another: Forever Drive. This one too has no Restore button and not rejected. – erkanyildiz Jun 29 '12 at 20:54
  • 1
    @erkanyildiz Have a look at the details of Restoring Previous Completed Purchases here : (link requires apple developer account) https://developer.apple.com/appstore/in-app-purchase/In-App-Purchase-Guidelines.pdf Consumable products don't require a restore. – Geoff Evason Oct 22 '12 at 00:51
  • @Geoff Evason The Mighty Eagle is not consumable. – erkanyildiz Oct 22 '12 at 08:41
  • How am I suppose to check to see if this works if I can't take back the purchase? I got all these testing accounts that needs credit card info attached to them. I'm so angry right now! – iOSAaronDavid Oct 17 '14 at 17:46

4 Answers4

43

I use a variation of this:

//inside of an IBaction
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];


// Then this is called
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
    NSLog(@"%@",queue );
    NSLog(@"Restored Transactions are once again in Queue for purchasing %@",[queue transactions]);  

    NSMutableArray *purchasedItemIDs = [[NSMutableArray alloc] init];
    NSLog(@"received restored transactions: %i", queue.transactions.count);

    for (SKPaymentTransaction *transaction in queue.transactions) {
        NSString *productID = transaction.payment.productIdentifier;
        [purchasedItemIDs addObject:productID];
        NSLog (@"product id is %@" , productID);
        // here put an if/then statement to write files based on previously purchased items
        // example if ([productID isEqualToString: @"youruniqueproductidentifier]){write files} else { nslog sorry}
    }  
}

Sorry, I'm on my iPad if this makes no sense.

Brian Noah
  • 2,962
  • 18
  • 27
  • 8
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; solved my problem! Thanks! A lot of other examples neglect this line. – Sunkas Jul 25 '12 at 10:25
  • 2
    This is not accurate. After you call restoreCompletedTransations the delegate method that will get called is -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions – odyth Sep 04 '12 at 19:49
  • It may not be accurate for you, but it works amazingly well for me. hmmm. – Brian Noah Sep 08 '12 at 18:19
  • And like I said, I use a variation of this, and I was typing from memory on my iPad. – Brian Noah Sep 08 '12 at 18:19
  • in the comments you wrote in your code u have to replace isequaltostring with isEqualToString and there is one more thing in the row with "for (SK.....) at the end there is a "." replace it with a "{". I know you wrote this from iPad but just for those who don't know much about this :D Thanks at all for the code it worked perfectly – MasterRazer Jan 06 '13 at 16:47
  • Thanks Noah, made the changes and did a little indentation fixes. – Brian Noah Jan 09 '13 at 23:10
  • For all those finding a swift 3 version.. https://stackoverflow.com/a/41525018/2714877 – Mamta Mar 21 '18 at 17:53
28

Alex, i've been rejected for the same reason last week, and this is right what Apple wanted - after adding such a Restore button they didn't ask any other question on this subject.

Of course, you need not only to call [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];, but implement the restoring itself too (i mean, providing the content to user).

sblom
  • 26,911
  • 4
  • 71
  • 95
Nikita Pestrov
  • 5,876
  • 4
  • 31
  • 66
  • Thanks for the reply Nikita. Very frustrating indeed. Basically what I have done with my in app purchases is once the user has made a purchase, they get put into a purchased array for that product ID. when they tap a view that offers the in-app purchase, the view checks if the user is in the purchased array and based on this redirects automatically to there purchase view or to an advertisement view. Can you tell me what is happening exactly when the restoreCompletedTranscations is called? Thanks – Alex G Jun 13 '12 at 19:45
  • restoreCompletedTranscations will give you a restored transactions to your SKPaymentObserver delegate, so you'll be able to see, what user has bought before. The trasnsactions are just SKPaymentTransactions, and their state is SKPaymentTransactionStateRestored. – Nikita Pestrov Jun 13 '12 at 20:09
  • 2
    how to get last transaction detail or restore last complete transaction? This above method gives me lots of id and not given last purchase id. – vipinsaini0 Apr 26 '17 at 08:26
4

This is because you can be signed in with the same Apple ID on different iOS devices.

For example, let's say I'm logged into test@iCloud.com on an iPad. When I download your application, I realize that I would like to remove the ads, so I pay 99¢ to remove them.

A year later, I decide to buy an iPhone, and sign into test@iCloud.com on that account, and I download your app again. However, the ads are still there, even though I already paid to remove them on my iPad. That's where the restore feature comes in. Using that, I can restore the purchases that I made on my iPad, and make them work on my iPhone.

To restore the purchase, you could use:

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

This causes the

(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentQueue *)queue

method to be called. Inside of that, you need to provide the user with the content that they bought.

Jojodmo
  • 23,357
  • 13
  • 65
  • 107
3

Alternative to restore button could be a restore switch in app settings bundle. It does not overwhelm UI and seems like Apple welcomes it (but be sure to mention that you have implemented mechanics this way).

BOOL shouldRestorePurchases = [[NSUserDefaults standardUserDefaults] boolForKey:@"restorePurchasesKey"];
Roman B.
  • 3,598
  • 1
  • 25
  • 21
  • @AbdulYasin the method described offers to store a boolean in settings bundle. In the app you have to implement logic as you would do with the "restore button". – Roman B. Apr 13 '15 at 14:48