How to get production receipts in a development environment

I want to convert a previously released app from a paid version to a paid version.

In order to branch those who are buying paid version and those who are not, I want to get receipts and refer to the value of receipts [“original_application_version”] to get whether I bought paid version before.

In the development environment, the value is always returned as 1.0, so the accuracy of the test is low and the problem.



I want to get and test the same receipt as the production environment, but is it possible to get the same receipt as the production environment even though it is a development environment?

Please tell me how to do it if possible!



Please let me know if there is a good way to distinguish between previously released apps (paid version) and non-released apps (billed version).

Thanks.

Accepted Reply

The test is pretty simple. Just add a line of code that changes "1.0" to some other value (either an old version number or a new version number) and see if your app handles that correctly. You can rely on the production receipt to have the actual value for the version that was originally purchased. Be sure to remove that added line before submitting the updated version.

Replies

To differentiate between your users you should use the original_purchase_date.

There is no original_purchase_date in the receipt, just in the in_app_purchase part of the receipt.

The test is pretty simple. Just add a line of code that changes "1.0" to some other value (either an old version number or a new version number) and see if your app handles that correctly. You can rely on the production receipt to have the actual value for the version that was originally purchased. Be sure to remove that added line before submitting the updated version.

mss_prorouterequested information on how to get a production appStoreReceipt in the sandbox environment. I'm reminded as to how one transitions from a paid app to a free app with in_app purchases. In this case, one uses the original_application_version to distinguish users who purchased the app, to those newer users who installed the free app. Here's a canned response which I send to developers who submit questions on this transition process. The same thing applies in this case with regards to working with the original_application_version field.


BEGIN

When a developer has an existing paid application, but would like to make the app Free with In-App Purchase the transition process is called the “Paid to Freemium” process. You will modify the app so that at first launch, the app validates the appStoreReceipt. Assuming that the validation status is 0, the app process then checks the JSON payload, for the “original_app_version” value to determine which version of the app, the iTunes user first installed. If the “original_app_version” value is for the previous “paid” app version, the application provides the user with access to the premium content. If the “original_app_version” value is for the current “free” version, then the user must make the In-App Purchase to have access to the premium content. If the user is a “free” app customer, then the user has access to only minimal functionality, but they can access the “Buy” button to obtain premium content access. The “paid” app version customer should never be presented with the “Buy” button.


The specific process for a paid to freemium app

1. on first launch, the app determines whether it has checked the applicationReceipt pointed to by


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


If the receipt is not present, the app alerts the user that the applicationReceipt may need refreshing and asks whether the user want to refresh the receipt. If the user agrees, the app makes the SKReceiptRefreshRequest call and process the resulting receipt. See step 2.


If the user declines to refresh the receipt, the application makes the assumption that the user is a new user who has no privileges to access premium content and exits the receipt validation process to the main part of the app.


The gotcha here is that when you install the app using Xcode, there will not be an appStoreReceipt. This is true whenever the app is installed by some other means than by the app store. This also happens in App Review which uses a custom App Install process. One other note, if the app finds no appReceipt, it must not force the issue to require the use of SKReceiptRefreshRequest to have a receipt installed. The SKReceiptRefreshRequest presents the user with an Authentication dialog. If the user cancels this dialog, then treat the user in the same manner as if the appStoreReceipt was present, but the contents of the in_app array is empty. The contents of the in_app array is an array of purchase records of In-App Purchases made by the user within the app.


2. Validating the receipt. I refer you to the “Receipt Validation Programming Guide”.

<https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Introduction.html>

There are 2 methods described. For an iOS app, the static method is more complicated in that you are required to obtain and build your own OpenSSL library to use to validate the receipt with. Most developer implement the process where the applicationReceipt is forwarded to the iTunes Store verifyReceipt server for validation. Note that the proper method is to encode the receipt into base64, then forward the receipt to your server, which then forwards the receipt to the appropriate verifyReceipt server.


On the subject of “appropriate” verifyReceipt server, I refer you to Tech Note 2413, In-App Purchase FAQ

<https://developer.apple.com/library/archive/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL>


What url should I use to verify my receipt?

Use the sandbox URL https://sandbox.itunes.apple.com/verifyReceipt while testing your application in the sandbox and while your application is in review.

Use the production URL https://buy.itunes.apple.com/verifyReceipt once your application is live in the App Store.

Important: The App Review team reviews apps in the sandbox.

Always verify your receipt first with the production URL; proceed to verify with the sandbox URL if you receive a 21007 status code. Following this approach ensures that you do not have to switch between URLs while your application is being tested or reviewed in the sandbox or is live in the App Store.

The 21007 status code indicates that this receipt is a sandbox receipt, but it was sent to the production service for verification. A status of 0 indicates that the receipt was properly verified. See for WWDC 2012: Managing Subscriptions with In-App Purchase more information.


It’s your choice to send the receipt directly or to use your own server - By using your own server you can prevent a “man-in-the-middle” attack.


3. Parsing the validated receipt.

Assuming that the validation status result is 0, then parse the JSON results and look for the field “original_application_version”. The field “original_application_version” is a UTF8String and must be compared to the CFBundleVersion for the paid version of the app. If the original_application_version is equal to or less than the paid version of the app, the user is a paid app version user and should be given access to the paid content of the app. If the original_application_version is greater than the paid version of the app, then this is a new user who must purchase premium access using In-App Purchase.


The gotcha here is that in the sandbox, using SKReceiptRefreshRequest to install an applicationReceipt will result in one where the original_application_version is “1.0”. What you want to make sure, is to compare this field with the paid-app version - lets say it’s “4.0” make sure to compare against this value. So the issue becomes, if there’s no applicationReceipt, and the app uses the SKReceiptRefreshRequest call to refresh the receipt, the original_app_version field with be “1.0” - so use that as your test case for a paid app customer. Use the lack of receipt as the new app user test case so that you can test the in app purchase process.


When the app makes it to the production App Store, and a paid user installs the app from the app store, the applicationReceipt will be present and the original_app_version will match the version the user first installed - or it could be the current version of the app, if for a new user.


So at this point, the status of the user has been defined. and the app moves to the next step.


I should add, there is one production case where the app will not have an applicationReceipt - this occurs if the user restores their device from a local backup without internet access.

END


rich kubota - rkubota@apple.com


developer technical support CoreOS/Hardware/MFI

Hello PBK, thanks for your answer, I looked in the documentation in:

https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html


And as you said there is no original_purchase_date in the receipt.


But I don't know if maybe the documentation is not updated or what is going on.


I send the receipt to:


h ttps://sandbox.itunes.apple.com/verifyReceipt


An when I got the data back I do:


let appReceiptJSON = try JSONSerialization.jsonObject(with: data)


The receipt contains the key : original_purchase_date


(lldb) po appReceiptJSON

▿ 6 elements

▿ 0 : 2 elements

- key : status

- value : 0

▿ 1 : 2 elements

- key : environment

- value : Sandbox

▿ 2 : 2 elements

- key : receipt

▿ value : 18 elements

▿ 0 : 2 elements

- key : receipt_type

- value : ProductionSandbox

▿ 1 : 2 elements

- key : app_item_id

- value : 0

▿ 2 : 2 elements

- key : receipt_creation_date

- value : 2019-03-25 15:17:18 Etc/GMT

▿ 3 : 2 elements

- key : bundle_id

- value : com.MyCompany.MyApp

▿ 4 : 2 elements

- key : original_purchase_date

- value : 2013-08-01 07:00:00 Etc/GMT

▿ 5 : 2 elements

- key : in_app

// I DELETE DATA FROM HERE

▿ 6 : 2 elements

- key : adam_id

- value : 0

▿ 7 : 2 elements

- key : receipt_creation_date_pst

- value : 2019-03-25 08:17:18 America/Los_Angeles

▿ 8 : 2 elements

- key : request_date

- value : 2019-03-29 20:00:09 Etc/GMT

▿ 9 : 2 elements

- key : request_date_pst

- value : 2019-03-29 13:00:09 America/Los_Angeles

▿ 10 : 2 elements

- key : version_external_identifier

- value : 0

▿ 11 : 2 elements

- key : request_date_ms

- value : 1553889609789

▿ 12 : 2 elements

- key : original_purchase_date_pst

- value : 2013-08-01 00:00:00 America/Los_Angeles

▿ 13 : 2 elements

- key : application_version

- value : 1

▿ 14 : 2 elements

- key : original_purchase_date_ms

- value : 1375340400000

▿ 15 : 2 elements

- key : receipt_creation_date_ms

- value : 1553527038000

▿ 16 : 2 elements

- key : original_application_version

- value : 1.0

▿ 17 : 2 elements

- key : download_id

- value : 0

I would like to point out that from the following sessions:


It's recomended Use hardcoded values —> Not Info.plist values


WWDC 2014 Session 305 Preventing Unauthorized Purchases With Receipts


WWDC 2017 Session 305 Advanced StoreKit



Verify Application:


- Check the Bundle Identifier


- Check the Bundle Version


- Use hardcoded values —> Not Info.plist values

Yes - but you cannot rely on undocumented features remaining stable in the next release of iOS. Nor can you rely on the content of that field to be "the original purchase date" of the app by that person.

Thank you

The link to the documentation you referred to has been archived. According to a newer documentation link on App Store receipts, it documents these fields:


original_purchase_date

string

The time of the original app purchase, in a date-time format similar to ISO 8601.


original_purchase_date_ms

string

The time of the original app purchase, in UNIX epoch time format, in milliseconds. Use this time format for processing dates.


original_purchase_date_pst

string

The time of the original app purchase, in the Pacific Time zone.