Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transaction comes back after finishTransaction: has been called on it

Tags:

I am using in-app purchase for an iPhone app. I have a class that acts as SKProductsRequestDelegate and SKPaymentTransactionObserver, and it's all working fine in the currently released version available on iTunes.

However, after recently adding a new non-consumable product and testing it within the Sandbox environment, I'm now encountering a strange problem. Every time I launch the app, the purchase I made yesterday reappears in the transactions list passed to me by paymentQueue:updatedTransactions:, despite the fact that I had called [[SKPaymentQueue defaultQueue] finishTransaction:transaction] already (several times). It's undead!

In my paymentQueue:updatedTransactions: implementation, I have:

for (SKPaymentTransaction* transaction in transactions) 
    switch (transaction.transactionState)
    {
        case SKPaymentTransactionStatePurchased:
        case SKPaymentTransactionStateRestored:
        {
            ....
                DDLog(@"Transaction for %@ occurred originally on %@.", transaction.payment.productIdentifier, transaction.originalTransaction.transactionDate);
                ....

I then process the purchase, download the user content and finally, in another method, do this:

for (SKPaymentTransaction* transaction in [[SKPaymentQueue defaultQueue] transactions])         
            if (([transaction.payment.productIdentifier isEqualToString:theParser.currentProductID]) &&
                 ((transaction.transactionState==SKPaymentTransactionStatePurchased) || (transaction.transactionState==SKPaymentTransactionStateRestored))
               )
            {
                DDLog(@"[[ Transaction will finish: product ID = %@; date = %@ ]]", transaction.payment.productIdentifier, transaction.transactionDate);
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }

As you may have noticed, I'm not holding on to the original transaction object for the sake of simplicity, and it's relatively easy to find it later from the call to [[SKPaymentQueue defaultQueue] transactions]. Regardless, I do indeed see the expected output; that the transaction is completed and that it precisely matches the product ID and date of the original transaction. However, next time I run the app the whole things starts over! It's like the iTunes Store was never notified that the transaction completed, or refuses to acknowledge it.

like image 401
Craig McMahon Avatar asked Jun 29 '10 09:06

Craig McMahon


3 Answers

This issue was also raised in the developer forums, and the general conclusion was that it was down to a difference in the handling of transactions in iPhone OS 4.0. The problem only seems to occur when there is a significant delay between receiving a notification of the finished transaction and calling finishTransaction on the payment queue. In the end we didn't find an ideal solution, but what we did was this:

  1. As soon as the transaction arrives, process it and record a value in the user preferences if processing was successful.

  2. The next time that transaction appears in the queue, which may not be until the next launch of the app, immediately call finishTransaction on it.

Our products are "non-consumable" so it's enough to check that the product paid for is valid and error-free to safely ignore any 'undead' repeated transactions from iTunes. For consumable products one would need to save more information about the purchase, such as the original payment date, to make sure that future transaction notifications can be matched to purchases that were already processed.

like image 123
Craig McMahon Avatar answered Sep 30 '22 18:09

Craig McMahon


That problem happened to me as well, I found the a solution. That may help you in similar cases.

I was calling finishTransaction immediately but next time when I try to buy something, the previous product was coming as well! So at first time, I was buying one product. But at second time, I was buying the second product and the first product too.

I found out that I'm adding SKPaymentTransactionObserver multiple times! That was causing the problem, making multiple buys.

When the process ends, I mean when you call finishTransaction, right after that, call: [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

That will clear out transactions and remove the observer. So next time, you won't make multiple buys.

like image 43
alper_k Avatar answered Sep 30 '22 17:09

alper_k


I have not delved deeply into this, but I was seeing my calls to finishTransaction reliably failing in iOS 4. On a hunch I put the call to finishTransaction in a dispatch_async call and the problem went away.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
});

Perhaps a salient point is I was calling finishTransaction from within a block which ultimately ran after a network call to my server.

like image 31
Dav Yaginuma Avatar answered Sep 30 '22 17:09

Dav Yaginuma