latest_receipt_info key missing in response

We are seeing a specific customer uploading a receipt for an in-app purchase auto-renewable subscription that, when submitted to Apple for verification, returns back a JSON response without any latest_receipt_info key nor any latest_receipt key. The only keys we see are 'status', 'environment', and 'receipt', but receipt has none of the information necessary such as the expiration date.


Is this a known issue? (I contacted iTC but they told me to contact DTS).

Post not yet marked as solved Up vote post of esilverberg Down vote post of esilverberg
8.9k views

Replies

This is either a device running an old version of iOS or, more likely, a hack attempt using an old receipt.


Here is what an old receipt looked like:



{"status":0, "environment":"Sandbox",

"receipt":{"receipt_type":"ProductionSandbox", "adam_id":0, "bundle_id":"com.kramerandkramerXXXXXX", "application_version":"1.0", "download_id":0, "request_date":"2014-01-04 22:09:48 Etc/GMT", "request_date_ms":"1388873388018", "request_date_pst":"2014-01-04 14:09:48 America/Los_Angeles",

"in_app":[

{"quantity":"1", "product_id":"com.kramerandkramersoftware.XXXXXXXXXX", "transaction_id":"1000000097744614", "original_transaction_id":"1000000097744614", "purchase_date":"2014-01-04 22:09:47 Etc/GMT", "purchase_date_ms":"1388873387000", "purchase_date_pst":"2014-01-04 14:09:47 America/Los_Angeles", "original_purchase_date":"2014-01-04 22:09:46 Etc/GMT", "original_purchase_date_ms":"1388873386000", "original_purchase_date_pst":"2014-01-04 14:09:46 America/Los_Angeles", "is_trial_period":"false"}]}} /n/n {

environment = Sandbox;

receipt = {

"adam_id" = 0;

"application_version" = "1.0";

"bundle_id" = "com.kramerandkramerXXXXXXXX";

"download_id" = 0;

"in_app" = (

{

"is_trial_period" = false;

"original_purchase_date" = "2014-01-04 22:09:46 Etc/GMT";

"original_purchase_date_ms" = 1388873386000;

"original_purchase_date_pst" = "2014-01-04 14:09:46 America/Los_Angeles";

"original_transaction_id" = 1000000097744614;

"product_id" = "com.kramerandkramersoftwareXXXXXXXXXX";

"purchase_date" = "2014-01-04 22:09:47 Etc/GMT";

"purchase_date_ms" = 1388873387000;

"purchase_date_pst" = "2014-01-04 14:09:47 America/Los_Angeles";

quantity = 1;

"transaction_id" = 1000000097744614;

}

);

"receipt_type" = ProductionSandbox;

"request_date" = "2014-01-04 22:09:48 Etc/GMT";

"request_date_ms" = 1388873388018;

"request_date_pst" = "2014-01-04 14:09:48 America/Los_Angeles";

};

status = 0;

}

The issue is that this is happening entirely on our server and therefore should be independent of iOS/client, though our client only runs on iOS 8.4 or later. If we send Apple's servers (https://buy.itunes.apple.com/verifyReceipt)a receipt that a client sends to us, Apple's servers should know how to send us a response that includes the latest_receipt key. I feel instead like this is a very serious regression in their validation API....

But what if the receipt the client sent your server is a bogus receipt from many years ago for some other app. All the Apple servers can do is decode it and send the decoded receipt back to your servers.

My client is calling the following method:


        NSData *receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];


If this is returning a bogus receipt from some other app, that would indicate a very severe bug in Apple's underlying StoreKit code...

You do not know what your client is doing. You might know what your app would do. But, for exxample, you do not know if your client is diverting your app's transmission to your server and modifying it with a different receipt using a man-in-the-middle hack.


But while such nefarious activity is one possibility, it could also be an old receipt stored by your code from a transaction.transactionReceipt. It is unlikely to be the receiptData you reference in your code above.

The documentation says latest_receipt is only returned for "iOS 6 style" receipts:

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


Apple have always included latest_receipt for me (in my apps), but are perhaps not consistent in doing so?


If you are using the > iOS 7 app-receipts, there should not be possible for the user to modify the receipts (and get valid results back from Apple).

According to 2016 WWDC videos, Using Store Kit With Swift (702), I think the answer is this:

If you have an older receipt that has been renewed, Apple will return the newest receipt when trying to verify the old receipt. That would explain why "latest recept" is missing, since the receipt you get back is the latest.


Can you verify that this is true?

>Apple have always included latest_receipt for me (in my apps), but are perhaps not consistent in doing so?

The transaction.transactionReceipt has been deprecated and could disappear at any moment. Only this receipt contains a latest_receipt field

>If you are using the iOS 7 app-receipts, there should not be possible for the user to modify the receipts (and get valid results back from Apple).


My point above was that a user can get their valid receipt and post it on the internet for all to use. Your system needs to be sensitive to such a hack.

I believe you are incorrect. I believe that an

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];

NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];


receipt (not a transaction.transactionReceipt), when sent to the Apple servers, does not return the latest receipt information but only the information contained within that receipt. In order to get a current receipt you must first do a:


SKRequest *request = [[SKReceiptRefreshRequest alloc] init];

request.delegate = self;

[request start];


and await a call to


-(void)requestDidFinish:(SKRequest *)request{

if([request isMemberOfClass:[SKReceiptRefreshRequest class] ]){ .....




Soemone please correct me if I am wrong!!!!!

@PBK,

Can you please explain this receipt. I don't think that it is a stale receipt or a MIM attack. If you think otherwise, let me know.


{

"receipt":{"receipt_type":"Production", "adam_id":******, "app_item_id":******, "bundle_id":"com.****.***.****", "application_version":"2.3.1", "download_id":******, "version_external_identifier":*******, "receipt_creation_date":"2019-06-19 16:15:05 Etc/GMT", "receipt_creation_date_ms":"1560960905000", "receipt_creation_date_pst":"2019-06-19 09:15:05 America/Los_Angeles", "request_date":"2019-06-20 08:45:52 Etc/GMT", "request_date_ms":"1561020352231", "request_date_pst":"2019-06-20 01:45:52 America/Los_Angeles", "original_purchase_date":"2019-06-19 16:15:05 Etc/GMT", "original_purchase_date_ms":"1560960905000", "original_purchase_date_pst":"2019-06-19 09:15:05 America/Los_Angeles", "original_application_version":"2.3.1", "in_app":[]}, "status":0, "environment":"Production"}


"original_purchase_date":"2019-06-19 16:15:05 Etc/GMT"

@PBK,



Our QA got this while testing. So no MIM, and he did it on a new Mac device so not stale either.

Can you please explain this receipt. I don't think that it is a stale receipt or a MIM attack. If you think otherwise, let me know.


{

"receipt":{"receipt_type":"Production", "adam_id":******, "app_item_id":******, "bundle_id":"com.****.***.****", "application_version":"2.3.1", "download_id":******, "version_external_identifier":*******, "receipt_creation_date":"2019-06-19 16:15:05 Etc/GMT", "receipt_creation_date_ms":"1560960905000", "receipt_creation_date_pst":"2019-06-19 09:15:05 America/Los_Angeles", "request_date":"2019-06-20 08:45:52 Etc/GMT", "request_date_ms":"1561020352231", "request_date_pst":"2019-06-20 01:45:52 America/Los_Angeles", "original_purchase_date":"2019-06-19 16:15:05 Etc/GMT", "original_purchase_date_ms":"1560960905000", "original_purchase_date_pst":"2019-06-19 09:15:05 America/Los_Angeles", "original_application_version":"2.3.1", "in_app":[]}, "status":0, "environment":"Production"}

@yogkaush


We are getting a similar receipt as the last one you posted. This is on our production environment.

Did you managed to find what is the cause?

Unfortunately, we didn't get any clear response from Apple support on how to relate to users with those receipts, so currently we simply deny access to our subscription to those users.

The recipt above is for the purchase of the app itself. There are no IAPs as indicated by " "in_app":[] "

Thanks for the answer PBK.

I'm a little confused as our app is free, it doesn't cost anything, so i'm not sure why there is a receipt for the app purchase.

In addition, why such a receipt is present in the appStoreReceiptURL?
After a sucdessfull purchase against the App Store, our App retrieves the purchase token from the appStoreReceiptURL and send it to our backend for verification.

This is an example of a decrypted purchase token:

{

"receipt":{"receipt_type":"Production", "adam_id":*****, "app_item_id":*****, "bundle_id":"*****", "application_version":"7795", "download_id":*****, "version_external_identifier":*****, "receipt_creation_date":"2020-01-14 22:32:05 Etc/GMT", "receipt_creation_date_ms":"1579041125000", "receipt_creation_date_pst":"2020-01-14 14:32:05 America/Los_Angeles", "request_date":"2020-01-15 09:51:21 Etc/GMT", "request_date_ms":"1579081881913", "request_date_pst":"2020-01-15 01:51:21 America/Los_Angeles", "original_purchase_date":"2020-01-14 22:32:05 Etc/GMT", "original_purchase_date_ms":"1579041125000", "original_purchase_date_pst":"2020-01-14 14:32:05 America/Los_Angeles", "original_application_version":"7795", "in_app":[]}, "status":0, "environment":"Production"}