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