15

I am trying to figure out the best way to ensure that a user will not lose their consumable in app purchase.

However, I am starting to feel like this StackOverflow user.

After a complete transaction I want to send to a server that the user bought a consumable in app. However, the connection might fail at that point (the server is down or the user loses connection).

I was considering in that scenario to send the receipt as soon as the connection can be made again. However, from what I have read the receipt contains all in app purchases and I can't send only the receipt for the last bought item.

How can I ensure that the purchase made by the user was synced with the server?

Community
  • 1
  • 1
Tiago Almeida
  • 13,437
  • 3
  • 62
  • 80

1 Answers1

17

The solution is to not call

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

on the consumable purchase until it has been synced with the server. This way, the transaction will be delivered again to your observer every time it is set to observe the payment queue. You will need manage app side whether it's a purchase that has been delivered but not synced, or a new purchase.

Once you have synced the transaction with the server. You can call -finishTransaction:. See the "Finishing The Transaction" in the docs here:

https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/DeliverProduct.html#//apple_ref/doc/uid/TP40008267-CH5-SW3

EDIT

So the user is charged by Apple, and Apple send you a SKPaymentTransaction object that represents the transaction. If the transactionState property of that transaction is SKPaymentTransactionStatePurchased, then it means Apple has processed the payment and the user has been charged.

Once you have received this SKPaymentTransaction object from the paymentQueue, you deliver the content to the user, and sync the transaction with your server. Once both of those have been successfully completed, you call -finishTransaction. Calling -finishTransaction tells StoreKit that you have successfully completed everything you need to do. -finishTransaction is a synchronous call, you are handing responsibility back to StoreKit, and you don't need to concern yourself with how that is communicated back to iTunes/Apple, StoreKit handles it all for you.

You should not call -finishTransaction until you have done everything you need to do. If something goes wrong (device battery dies, loss of internet connection, app crash), you will be delivered the same SKPaymentTransaction from the payment queue next time your observer is registered with the queue, and can try to sync again with your server. This will keep happening until you call -finishTransaction.

There is a scenario where the user is never able to sync with your server, never connects to internet again etc. That is out of your control, and at that point it is the responsibility of Apple to decide whether they charge the user.

It's also worth noting that the user can contact Apple and request a refund if they wish to.

Sam Clewlow
  • 4,123
  • 23
  • 36
  • If I don't call finish transaction the user loses his money or it only loses after I call finish transaction? Also, how can I handle the cases where the server synced, I am going to finish the transaction, but somehow the itunes connect doesn't receive the finish transaction operation? – Tiago Almeida Mar 11 '15 at 16:51
  • 1
    I am totally sold. Thank you very much for the great answer. The only thing that is not cover perfectly is how you know that your SKPaymentTransaction corresponds to a certain model that you have (to sync with the server). But that answer is already good enough :) – Tiago Almeida Mar 11 '15 at 17:18