From the StoreKit guide:
If the user attempts to purchase a nonconsumable product or a renewable subscription they have already purchased, your application receives a regular transaction for that item, not a restore transaction. However, the user is not charged again for that product. Your application should treat these transactions identically to those of the original transaction.
This presents a huge problem in an app I am working on. We have licensed a large body of content from a publisher for sale through in-app purchase. They require that every time we sell a piece of this content (i.e. user pays us), our server calls an API on their servers to report the transaction. This is for accounting purposes and ultimately used to determine how much we pay them at the end of the month, per our agreement with them.
I have read several suggestions on SO and elsewhere about calling restoreCompletedTransactions rather frequently and maintaining a local understanding, on the device, of what the user has already purchased so they cannot be allowed to purchase it again. This to me seems like something that should be able to be implemented on the server side. However, the receipts that we are getting back from the Apple servers are exactly the same for a buy and a re-buy, as promised by the StoreKit guide.
If payment callbacks from StoreKit cannot be trusted as a valid accounting mechanism in this kind of situation ("you got paid" vs. "you didn't get paid"), what other real-time insights into transaction traffic are available? I don't think the publisher we are working with is going to be happy if we tell them we have to wait 45 days after the end of the month to get the REAL paid dollar amount out of iTunes Connect.
I have recently looked into the same problem. In my case, I wanted to implement accurate revenue tracking using Mobile App Tracking to track revenue generated from different customer acquisition campaigns.
Fortunately enough, there is a way to do it. It should be noted that SKPaymentTransactionStatePurchased
vs. SKPaymentTransactionStateRestored
solely depends on the initating action, e.g. whether you initiated a restore or a (re-)purchase, so that doesn't work.
What does work instead is checking for SKPaymentTransaction.originalTransaction
which will be != nil
for restores and re-purchases. The latter is unfortunately undefined behavior (docs). I'd consider a null check fair enough though.
Another option is to validate the transaction-receipt of transactions with SKPaymentTransactionStatePurchased
and check that the original_transaction_id
property in the returned, validated receipt matches the transaction_id
.
The bad news is: In the current iOS version (4.3.x) there's no way to distinguish between a buy and a re-buy of non-consumable products.
To ease the situation I would recommend two things:
After a successful purchase, store the product identifier
of the purchased product in the NSUserDefaults
on the device. You can then hide the already purchased products from the user and thus handle a re-buy situation.
The NSUserDefaults
are backed up by iTunes when the user synchronizes his device. So your stored purchase information is not lost when the user gets a new device.
Store the receipt data together with the device ID on your server. Analyze the receipt's product identifier and the device ID.
If you receive another receipt with the same product identifier and device ID combination, then assume a re-buy. At least this would allow you to cover most of the re-buy cases.
Assuming that an ordinary iPhone user switches his device every 1-2 years, you will at least cover most of the re-buy cases and maybe apple will fix this in the future.
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