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.
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:
As soon as the transaction arrives, process it and record a value in the user preferences if processing was successful.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With