Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I call finishTransation on failed transactions during in app purchases?

Tags:

ios

storekit

I am writing an in-app purchase capability for ma iOS app. I'm selling some simple consumables (no downloads). I based my code on this tutorial/topic How do you add an in-app purchase to an iOS application? However I noticed it conflicts with official apple docs.

In this code when a Transaction fails for whatever reason, this code [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; is called. However on apple pages https://developer.apple.com/library/ios/documentation/StoreKit/Reference/SKPaymentQueue_Class/Reference/Reference.html#//apple_ref/occ/instm/SKPaymentQueue/finishTransaction: it is written: "Your application should call finishTransaction: only after it has successfully processed the transaction and unlocked the functionality purchased by the user."

So which approach is correct? Should I call finish on failed transactions?

like image 954
Kraszim Avatar asked Jul 09 '14 12:07

Kraszim


Video Answer


2 Answers

According to the Apple's In-App Purchase Programming Guide

Your app needs to finish every transaction, regardles of whether the transaction succeeded or failed.

So regardless of the transaction result, you always finish the transaction to remove it from the payment queue and then handle the state (successful or failed) in your code in order to provide the user with the appropriate information

like image 118
spassas Avatar answered Sep 21 '22 15:09

spassas


This is an old thread, but I find the answer incomplete, which can be misleading, and Apple docs are a bit vague on the subject. The problem is how we define a 'failed' transaction.

We can identify many states:

SKPaymentTransactionStateFailed

You get notified Apple noticed something went wrong (i.e. user cancelled, credit card declined, the kid is not authorized by its father to make the purchase, etc) and informs you of the event.

You must call finishTransaction

SKPaymentTransactionStatePurchased

We can split this one in multiples ones:

  1. Receipt validated and content delivered. You must call finishTransaction
  2. Receipt is invalid. There is no agreement here, as Apple docs are vague. It's 95.0% certain this is a hacked/jailbreak purchase. You could call finishTransaction to remove it from the queue and thus lighten the load on your servers (otherwise the queue will keep asking your client to revalidate). However the other 5% is that Apple servers were down and somehow they returned invalid (or you interpreted as invalid) when actually Apple servers just were temporarily down.
    • This applies specially to consumable IAPs. Because once you call finishTransactions, the consumable IAP is gone from the receipt
    • For non-consumable IAPs, you can safely always call finishTransaction because worst case scenario the user can tap the button to restore transactions (which you have to implement as per App Store Guidelines)
  3. Receipt is valid, but could not be verified for temporary reasons (e.g. your servers are down, Apple servers down, internet connection went down). You must not call finishTransaction. The payment will remain on the payment queue and it will eventually ask you to try again (note that the device that attempts to try again may be a different iPhone, if the user is logged on from multiple Apple devices). Hopefully this time the servers will be back up.
    • This is a must for consumable IAPs. You can't call finishTransactions or else you'll get legit angry customers
    • Once again, for non-consumable IAPs, you can safely always call finishTransaction and rely on Restore Transactions instead.

If you don't perform validation nor have server-side inventory, none of this really matters to you, since there are not many points of failure.

Without validation nor server-side inventory, after receiving SKPaymentTransactionStatePurchased you update your database on the phone's storage with the additional/unlocked content, and after that you just call finishTransactions.

SKPaymentTransactionStateRestored

Same flow as SKPaymentTransactionStatePurchased.

SKPaymentTransactionStatePurchasing

You must not call finishTransaction

SKPaymentTransactionStateDeferred

You must not call finishTransaction

like image 41
Matias N Goldberg Avatar answered Sep 19 '22 15:09

Matias N Goldberg