In-App purchase renewals and server-side verification

Hi,


We're about to ship an update to our app including an auto-renewing in-app purchase product. We're verifying the receipts server-side and followed the documentation linked below:

https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html


After a client-side purchase, we send the application receipt to our backend for verification, and we're checking that there is a valid in-app purchase contained in `latest_receipt_info`.

Based on the `expires_date` of the in-app purchase, we'll start checking this user's receipt again server-side several times leading to the expiry date.


Although the client will get new finished transactions as the subscription is renewed, we currently don't send the updated receipts to our backend, instead relying on the `latest_receipt_info` field.


Is it safe to do so? Will Apple's server keep updating `latest_receipt_info` even if we keep sending the same receipt?


The initial sandbox testing looks like it will, but I'm concerned about the behaviour in 6-12 months.


Thank you,

Vincent

Replies

You are correct to be concerned for two reasons. First, only transaction.transactionReceipt's (not the new style receipt) are documented to return this field when they are decoded by Apple servers. This old type of receipt has been deprecated and new purchasers of your autorenewable subscription may not be able to access it any day now. Second, Apple is trying to convert everyone to the new type of receipt and their servers may stop decoding these old type receipts any day or, at the least, stop providing the 'latest_receipt_info' data on decoding. Actually, unlike all fields in the new style receipts and all other fields in the old style recepts, this field is not actually in the receipt. It gets appended to the receipt by the Apple servers which is a different process than simply decoding a receipt. And this unusual process may not exist in the future.

Hi,


Thank you for your answer and allow me to clarify a couple things:


I am not using the old style of receipts. After a successful purchase, I get the application receipt using the appStoreReceiptURL method of NSBundle. Our backend then sends that receipt to Apple's server. I don't use the transactionReceipt property of SKPaymentTransaction.


The documentation about latest_receipt_info is unclear:

"Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions. The JSON representation of the receipt for the most recent renewal."


I think this means that when using iOS 6 style transaction receipts, latest_receipt_info is only returned when the product is an auto-renewable subscription. Since I'm not using iOS 6 style transaction receipts, then the field is always returned.


How do you usually deal with auto-renewable in-app purchase products?

>Since I'm not using iOS 6 style transaction receipts, then the field is always returned.

I believe this is an incorrect interpretation of:

>"Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions."


I believe that in non-iOS 6 style receipts there is no latest_receipt_info field, or if there is such a field you can't rely upon it.


You deal with autorenewable subscriptions by asking each device to restoreCompletedTransactions. Once they do that they will then receive a new transaction at each renewal event. If the app discovers that a subscription has not been renewed at the time of an expiration it infoms the user that they need to check their subscription and then proceeds to do a receipt refresh (which will cause the user to have to log in to the app store) and checks if there was a transaction that was missed. This is all done from the device, not from your server. If you wish, you can send the receipt to your server or just have the device report to your server the status of the subscription.

It would be great if someone at Apple could clearify what "Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions." actually means. I can see it go both ways but more importantly, how do you check renewed subscriptions, when the app isn't running.


Let's say you have a magazine subscription and a renewing in-app has just expired. Do you let the user download or not?


If it means that for "ios 7 style receipts", the field is always returned - then we can just check if the sub has been renewed, otherwise we must somhow magically wake the device (since we can't from a server), get the user to login with it's iTunes account - and then finally refresh the new receipt (or perform a restore). Not a super user-friendly situation.


(I just discoverd this in the documentation, has always assumed the returned fields will always be returned)

It seems like madness that servers can't check for new transactions on subscriptions and we now require the user to open the app before the developer knows we've taken another subscription payment. This causes so many obvious issues for anyone running a subscription service.


I can't see any logical reason why the latest transaction isn't being returned via the verifyReceipt JSON endpoint...


Anyone found a solution to servers checking subscription status'?

First - because you resurrected an old post this thread will not appear to anyone who does not have email contact with the old thread ------Second, this issue has been discussed and the answer is to submit an enhancement request through the bug reporter asking for this feature ----- Third, this feature is quite complex on Apple's part. Decoding is a straightforward programming task. But appending the latest_receipt requires maintaining an up to date database, accessing it, extracting information and appending it. All so you don't have to require that the user access their Apple device. It also exposes a number of security flaws by not requiring continued access to the user's iTunes Account.

>This causes somany obvious issues for anyone running a subscription service.

Maybe not that obvious to those that handle the code behind the curtain, feel free to point out your issues to Apple via the bug report link below right.

Good luck.

  • Apple should just do it like Google.

Add a Comment

Apple should clarify the meaning of "Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions." Without the latest_info field in the response it is impossible for any cross platform app to handle the refund/cancellation case. It is unlikely this is what Apple intends. Otherwise, the following information no longer applies.

https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW4

"The values of the latest_receipt and latest_receipt_info keys are useful when checking whether an auto-renewable subscription is currently active."


https://developer.apple.com/library/prerelease/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT

"The Cancellation Date (cancellation_date) field is designed for use with auto-renewable subscription and non-consumable products. This field is set when a customer contacts Apple customer support for a refund and the transaction is canceled. cancellation_date is not automatically added to your app's receipt when set. It appears in the latest_receipt section of your app's receipt when the receipt is updated (for instance, when a new payment transaction occurs or when you restore purchases using SKPaymentQueue’s restoreCompletedTransactions), when your app calls SKReceiptRefreshRequest to refresh it, or when your app performs receipt validation with the App Store (https://buy.itunes.apple.com/verifyReceipt)."


http://stackoverflow.com/questions/6439482/how-does-apple-notify-ios-apps-of-refunds-of-in-app-purchases-iap

The current position of Apple is that when it decodes and returns a post-iOS6 receipt it does return an additional field 'latest_receipt_info'. When it does the same thing for an iOS6 receipt it does add the 'latest_receipt_info' field. For a period of time Apple added that field to post-iOS6 receipts; it may well still be doing that sometimes but not, apparently, always. You are welcome to submit an enhancement request through the bug reporter. This feature is a convenience but it is also a security risk because it does not require that a particular device have onging access to the user's iTunes Account to receive a subscription.


The 'cancellation_field' is a separate thing - it does not refer to users 'cancelling' their autorenewing function before a subscription is autorenewd. It only applies when a user asks for and gets their money back after a purchase has been completed.