72

I am trying to verifying that an auto-renewable In App purchase has not expired server side (not on a device).

I am using Apple's Grand Unified Receipt (iOS 7 style transactions). The response returned by Apple contains in_app and latest_receipt_info elements with an array of receipts. The in_app JSON element contains LESS receipts than latest_receipt_info. I was expecting both elements to contain the same number or receipts.

Also, I was expecting that the in_app element would contain ALL the receipts. However, it appears that latest_receipt_info actually contains all the receipts. Apple documentation seems to suggest to use in_app for finding a latest receipt.

I am surprised to see the latest_receipt_info because Apple's Documentation state that this element is "Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions." (not iOS 7+).

Which JSON element should I iterate to find the latest receipt for auto-renewable iOS 7 style transactions: in_app or latest_receipt_info?

ljk
  • 488
  • 1
  • 5
  • 11
Chris
  • 1,244
  • 1
  • 12
  • 15
  • did you find answers to your problem? – Cmag Oct 01 '14 at 19:48
  • 2
    No, I never found an answer or confirmation to which JSON element I should iterate through for finding the latest receipt. I did end up using the "latest_receipt_info" because it seems to contain the all the receipts. – Chris Oct 02 '14 at 14:18
  • Chris, I think you're right about using `latest_receipt_info`. Thank you so much for replying! Spent considerable time debugging this and am still at a loss :) Does apple always send you the latest_receipt_info? Here is what i always get back from their service : https://gist.github.com/vasiliyb/23b5ac1fb36c6fd50006 – Cmag Oct 02 '14 at 14:56
  • 3
    @Cmag, yes, I've consistently seen Apple return `latest_receipt_info` and `latest_receipt`. I use `latest_receipt_info` to check if an auto-renewal has renewed (or expired). And, I store `latest_receipt` for later use. I will send the stored `latest_receipt` to Apple the next time I check for a renewal. – Chris Oct 02 '14 at 17:43
  • Chris, excellent info! Thanks! So you dont simply forward the strings you get from the phones to apple, you actually store Apple's `latest_receipt`? I dont think i understand the process very well :( So if its a first time purchase, and you dont have the latest_receipt in say mongo, then forward the string that you got from the phone, otherwise send the stored latest_receipt, and verify with the string that was posted to you? – Cmag Oct 02 '14 at 18:21
  • 4
    @Cmag, yes, the 1st time purchase I send the receipt (string) from the device (there isn't a latest receipt in my database yet). If validation is successful, Apple will return a response with `latest_receipt`. I'll store `latest_receipt`. Then when an auto-renewal occurs (for me, subscriptions are 1 month), I'll send my stored receipt to Apple to see if an auto-renewal occurred. My thinking is that I wanted to always try to send to Apple the most up to date receipt that I'm aware of and hopefully by doing so Apple will always send me back the most up to date `latest_receipt_info`. – Chris Oct 02 '14 at 19:24
  • 1
    Right now the phone sends NodeJS API the receipt string every time the application is restarted/closed/put in background. This string is the same each time (i just verified). So the response i get back from apple after sending this string is always different. When trying to understand the JSON response from apple, i nearly broke my brain. Docs are almost non-existent :( What I dont understand is why the `latest_receipt` string always different in apple JSON response? Bigger question is... how do i validate if the receipt is valid. Horrible experience. – Cmag Oct 02 '14 at 20:34
  • so you dont always forward apple the receipt validation attempt, only a month after the first purchase date in your database? – Cmag Oct 02 '14 at 20:35
  • 1
    How often you send the receipt to Apple is up to you. For me, I just need to check for renewals every month. Testing in Sandbox, Apple will autorenew frequently so maybe that is why the latest_receipt is different (?). Apple will return a [status code](https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html) can help you tell if it's valid or not. – Chris Oct 03 '14 at 16:57
  • im still having horrible experience. The phone sends me a receipt, i send it to apple. the response `latest_receipt` from apple is different than what the phone sent me. brand new purchase in sandbox... how the heck can i get this to work :( – Cmag Oct 07 '14 at 17:44

5 Answers5

17

Just wanted to make clear that only the latest_receipt_info field is returning the latest renewed receipt. This is based on what we're actually getting back from Apple.

The relevant documentation is here on page 21.

Although it states that latest_receipt and latest_receipt_info fields are "Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions", we've found that they are being returned in our iOS 7 receipts. The in_app field within the receipt object is also returned with almost identical data, but doesn't contain the latest receipt info, which is the one you care about in the case of an auto-renewal.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Samir Uppaluru
  • 171
  • 1
  • 6
16

To verify that an auto-renewable subscription has not expired check the latest_receipt_info element.

This is what's currently (2017-08-09) officially documented on the in_app and latest_receipt_info elements:

in_app

In the JSON file, the value of this key is an array containing all in-app purchase receipts based on the in-app purchase transactions present in the input base-64 receipt-data. For receipts containing auto-renewable subscriptions, check the value of the latest_receipt_info key to get the status of the most recent renewal.

(source)

latest_receipt_info

Only returned for receipts containing auto-renewable subscriptions. For iOS 6 style transaction receipts, this is the JSON representation of the receipt for the most recent renewal. For iOS 7 style app receipts, the value of this key is an array containing all in-app purchase transactions. This excludes transactions for a consumable product that have been marked as finished by your app.

(source)

jox
  • 2,218
  • 22
  • 32
3

Babken Vardanyan mentioned in multiple answer threads that latest_receipt_info is sometimes missing. One interesting thing I've seen in receipts in this thread https://forums.developer.apple.com/thread/92200 was the difference in latest_receipt_info vs in_app. When doing a diff I noticed the the first receipt in the chain was missing (containing is_trial_period=true). It didn't generate an in-app receipt equivalent.

As linked in the documentation: https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW4 says to use latest_receipt_info as "for iOS 7 style app receipts, the value of this key is an array containing all in-app purchase transactions."

In regards to subscriptions, you should not use in_app and only use latest_receipt_info.

https://www.revenuecat.com/2018/09/24/apple-subscription-notifications-are-almost-useless covers the edge cases like cancellation_date where it is a customer service provided cancellation like seen in this apple developer forum thread: https://forums.developer.apple.com/thread/96670

Kevin King
  • 31
  • 1
0

I want to link the latest docs App Store Receipts

  • latest_receipt_info

An array that contains all in-app purchase transactions. This excludes transactions for consumable products that your app marks as finished. This only returns for receipts that contain auto-renewable subscriptions.

An array that contains the in-app purchase receipt fields for all in-app purchase transactions.

Since in_app contains all receipts, I prefer to iterate it and find the latest receipt of auto-renewable subscriptions.

zangw
  • 43,869
  • 19
  • 177
  • 214
-1

Receipt Fields(Apple Docs)

The documentation above makes it pretty clear to use in_app. Please note the line "Keys not documented below are reserved for use by Apple and must be ignored by your app" in the documentation. So even though the decoded receipt has latest_receipt_info, we should ignore that.

But I agree, the documentation is very lacking and that makes me nervous. Let me know how was your experience using iOS 7 style receipt verification in production.

ljk
  • 488
  • 1
  • 5
  • 11
Taher Saeed
  • 685
  • 7
  • 15
  • 8
    I believe this is incorrect - if latest_receipt_info is there, it contains renewals not mentioned anywhere else. Importantly they seem to have happened after the ones mentioned in_app. So a bit scary! – Alexp Aug 14 '15 at 14:40
  • 2
    It's pretty shady that the information they tell you to use is not applicable for renewing subscriptions, and that people have to use 'undocumented' fields to get this, meaning Apple can (and will) change them without notice. – Brian Nov 19 '15 at 16:25