9

I am restoring completed transactions (recurring) with

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

and in

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions

I got a list of history transactions restored, made by the app, but the method:

- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue

is not even called once, so I wonder which one should I use? I did some research and found that updatedTransactions: method should be used with checking transaction state, but if I got a list of transactions restored, it is just meaningless to treat them all as transactions. Which one should I use? Does paymentQueueRestoreCompletedTransactionsFinished only gives me the latest one (ie sandbox subscription expires in several minutes and I got a full list of history transactions made when testing, in updatedTransactions).

hzxu
  • 5,753
  • 11
  • 60
  • 95
  • I think `paymentQueueRestoreCompletedTransactionsFinished` is called if the user initiates a restore but there are no purchases on ASC to restore. You may need to hook into this in case it's necessary to stop a loading spinner, provide a message, etc. Otherwise it is not important. – David James Jun 23 '20 at 13:23
  • 1
    I can confirm from testing that `paymentQueueRestoreCompletedTransactionsFinished` is called regardless if there are transactions to restore or there are no transactions to restore, whereas `updatedTransactions` is called *only* if there are transactions to restore. – David James Jun 23 '20 at 16:58

4 Answers4

11

There is an excellent WWDC Video about using StoreKit, it is WWDC2012 Session 302.

To isolate each purchase, your updatedTransactions method could look something like this:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {

        for (SKPaymentTransaction *transaction in transactions) {

            switch(transaction.transactionState) {
                case SKPaymentTransactionStatePurchased:
                    // Unlock content
                    //... Don't forget to call `finishTransaction`!
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
                case SKPaymentTransactionStatePurchasing:
                    // Maybe show a progress bar?
                    break;
                case SKPaymentTransactionStateFailed:
                    // Handle error
                    // You must call finishTransaction here too!
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
                case SKPaymentTransactionStateRestored:
                    // This is the one you want ;)
                    // ...Re-unlock content...
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
             }
         }
}

Once you've determined that the purchase is being restored, you can make content available as you see fit - preferably by calling a separate method from within that switch statement and passing the transaction as a parameter. The implementation is up to you of course.

Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127
Ephemera
  • 8,672
  • 8
  • 44
  • 84
  • Yes, but what I got is more than 1 transactions restored for the same product ID, then do I use the transaction with most recent transactionDate? – hzxu Jan 14 '13 at 06:10
  • Hmm that shouldn't be happening... That possibly means, previously, the transaction wasn't completed with `finishTransaction:`. I'd reset your App (clean build and delete data from the iOS Simulator) to get rid of the queued transactions. Before you do that though, where are you setting up your `TransactionObserver`? – Ephemera Jan 14 '13 at 09:07
  • 1
    Maybe missing break in the SKPaymentTransactionStatePurchasing case? – neoneye Jul 05 '13 at 17:37
3
  1. Call [[SKPaymentQueue defaultQueue] addTransactionObserver:self] in (void)viewDidLoad or equivalent if applicable.
  2. Then call [[SKPaymentQueue defaultQueue] restoreCompletedTransactions].
  3. (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions will be called accordingly through (2).

If you don't call the method in (1), the application will never reach (3) to restore transactions in the first place.

Idan Moshe
  • 1,675
  • 4
  • 28
  • 65
El Tomato
  • 6,479
  • 6
  • 46
  • 75
  • 3
    Yes I did do 1. the problem is the method in 3. gives me a list of all subscription the sandbox account has made. I only need the one which is successfully restored. – hzxu Jan 14 '13 at 00:33
  • @hzxu i have same problem, Did you find any solution? – Chirag Shah Nov 29 '16 at 07:28
1

I had this same issue with paymentQueueRestoreCompletedTransactionsFinished never getting called. I fixed by going to iTunes & Aoo Stores in Settings and logged out of the sandbox test account and tried it again. Worked as expected the next time after being prompted to login again on a restore.

SlickDev
  • 607
  • 7
  • 7
  • I am pretty sure I would never have tried this as a fix, but it was exactly my issue. Thank you very much! I certainly hope this is a "dev environment" issue... seems like a lot of apps would break if this happened in prod. – Henry Mueller Mar 22 '21 at 02:00
0

From the Apple Docs:

paymentQueueRestoreCompletedTransactionsFinished: This method is called after all restorable transactions have been processed by the payment queue. Your application is not required to do anything in this method.

Dieter27
  • 134
  • 2
  • 8