iOS in app-purchase receipt validation - sandbox vs production url? - ios

I followed Ray Wenderlich's tutorial to implement receipt validation in my app. The code connects to Apple's validation server directly from my app rather than going through my own server.
After I submitted my first binary to the AppStore, I tested my app and the in-app purchasing didn't work because I had switched it over from the sandbox URL to the production URL.
Will this also fail when they AppStore reviewers test it and therefore be rejected? I've read this post but I'm still very confused about whether that applies to me if I'm not using my own server.

The solution is quite simple and it was explained on session 308 of WWDC 2012 (the video is available for registered developers). The session was related to subscriptions but you can extend it for in app purchases.
What happens is that when you develop you hard code your app to validate the receipt with the sandbox. Then you send the app to review, you clearly hard coded your app to validate the receipt with the production server.
But nothing prevents you from doing the validation in two steps:
always validate the receipt with the production server first, this will apply for 99% of your app life. If the receipt is validated, you're done.
if previous validation failed, just validate the receipt with the sandbox server. This should cover your development needs and of course fake receipts will fail validation too.
By the way, and this is officially stated in the documentation ONLY for subscriptions, if you try to validate a sandbox receipt with the production server you will get a specific status code; there is another status code that covers the case of production receipt validated with the sandbox server. In all cases the two worlds, sandbox and production, are always separated.
Don't forget also that with iOS7 added a new safer way to manage receipt validation directly from the device: consider in fact that receipt validation directly from the client (that you don't fully control, e.g. with jailbroken devices) is less secure than receipt validation done through a server you control.

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.
Note: The 21007 status code indicates that this receipt is a sandbox receipt, but it was sent to the production service for verification.
There is no public API to call to distinguish the production and sandbox environments so that you can decide which server to use ahead of time. If you have implemented the recommended receipt validation process, the fix can be implemented at your server which contacts the StoreKit server. If the status code for the validation attempt is 21007, then try again at the sandbox server.

It could fail. I had an app with in app purchases (but not based on my server, straight apple code) that work in development but crashed once released by Apple. It crashed because I had not done all the proper steps in iTunes Connect.
The surprising thing was that the reviewers didn't catch this, presumably since they were also working in a sandboxed environment.
I'm not sure this helps you, hope it does.

Related

In App Purchases don't work during Apple review

In App Purchases work in Sandbox environment but don't work during Apple review which results in the app rejection:
I can't find the problem as In App purchases work well on the test device with Sandbox accounts. I did send the IAPs for review as well and I don't do any kind of receipt validation.
Any ideas where I can find the problem roots?
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.
Look here: https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
How do I verify my receipt (iOS)?
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.
https://developer.apple.com/library/content/technotes/tn2259/_index.html
Add some sort of logging that you can see remotely. I suspect they're hitting one of the SKErrorDomain errors because of the weird environment they operate in (app store signed builds, but sandbox accounts).
Do you refresh the receipt in your purchase flow? That's a step where they typically encounter an error.

Is it possible to see if an in app purchase was made in sandbox mode

I am working on an iOS library that records a successful in app purchase to our API for later processing. What I would like to be able to do is log to our api if the IAP was made in sandbox (with a test user) or not.
That information is in the receipt. You can:
1) grab the receipt and submit it to the Apple servers (sandbox or production). It will come back either valid or with a 21007/21008 error revealing its environment.
2) grab the receipt and decode it. There is an undocumented field that indicates whether or not it is from the sandbox.
I'd go with #1
After a while of hunting I came across this post which seems to be what I am trying to do. Check if iOS app is live in app store I will update the post once I am fully deployed and tested but it seems to be working for now.

iOS Subscription Verification

I have an interesting problem... (for me at least)
I have developed an app that uses an auto-renewing subscription. They way I have it set up, the app saves the subscription receipt then each time the subscription needs to be verified, the app sends the receipt info to my server, which verifies the receipt is still valid.
In my app I have an app-wide constant called "testMode". Among other things, this causes the app to query my server with either a Sandbox, or Production flag. So if I am testing the app in test mode, the app will query a file on my server that checks the sandbox Apple server. If the app is not in test mode (like when I submit it to Apple) it queries the production file on my server which checks the data against Apple's production server.
My app was rejected because it produced an error when subscribing. That error was 21007 which means a sandbox receipt was sent to the production verification server.
So my question is: How can I submit an app that users the production server verification, if the Apple testers use the sandbox environment?
I ran into similar issues. This is the best way to handle it:
Whenever your server receives a new receipt from the app, first verify it with the production server. Then if you get error 21007, try the same receipt with the sandbox server. This is what I do and it has worked well. Apple should recommend this, but they don't.
Using this method, you should be able to get rid of your testMode in the app (unless you use it for other purposes).

(iOS + StoreKit) Can I detect when I'm in the sandbox?

I've got in-app purchases working just fine, and I'm going the server validation route. The server needs to know whether I'm in the sandbox or not, so for now I'm just sending it a "&sandbox=1" parameter. Of course when the full version of the app is out I won't be sending this parameter.
I'd rather not have this hardcoded in my app, as that will make testing difficult in the future, and it's one more (big) thing to remember to change before submitting builds to Apple.
Is there a way I can ask StoreKit if I am in the sandbox so I can then determine whether or not I need to send this parameter to my server? Alternatively, is there any other best practice for handling server validation?
Thinking about this more, should I just have the server always check the live system first, then the sandbox? If apple IDs are segregated between the live and sandbox systems then it wouldn't do any harm would it?
Thanks.
After a bit of digging I found this from Apple's Technical Note TN2259:
How do I verify my receipt (iOS)?
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.
So it looks like I should axe the &sandbox parameter completely and just do that. I really had to dig for this answer so I'm posting it here in hopes that someone else runs across it!
I encountered that very same problem, where my app was rejected because the "production" version of my app that I submitted was hardcoded to connect to a PHP script on my server that validates receipts with the real AppStore server (whereas my development build points to another PHP script that validates receipts with the sandbox server). However, after a few exchanges with Apple engineers, I found out that they use sandboxed user accounts to tests submitted applications, which explains why they got an error.
Instead of conditionally building my app to point to one script or the other, I will use a single script that tries the production server first and then falls back to the sandbox server if it receives the 21007 status code, as explained above!
Thanks a lot!
Always verify your receipt first with the production URL; proceed to verify with the sandbox URL if you receive a 21007 status code.
Unfortunately, the technical note fails to mention this is only valid for auto-renewing subscriptions!
As the In-App Purchase Programming Guide mentions below table 7-1:
Important The non-zero status codes here apply only when recovering information about a auto-renewable subscription. Do not use these status codes when testing responses for other kinds of products.
For non-renewing subscriptions, the production server does not return a status code, but a proper receipt.
In case you are forced to use non-renewing and implement your own subscription expiring logic, a possible solution is to send your app version along to your server, and keep track of which versions are in development at the moment, as such you can redirect to the sandbox.itunes server to verify receipts where appropriate, and mimic the x-minute expiring time of a subscription (as sandbox.itunes does for auto-renewing) for development on your server.

Error 21007: status result returned by Apple during in-app subscription restore

We are testing 'restore subscription' using Apple's new in-app subscription model. The In App Purchase Programming Guide lists 6 status codes that are returned by the Apple server during the restoration/purchase process ("Verifying an Auto-renewable receipt" - page 33/34).
However, the errors start at 21000 and end at 21006. We just tried to restore a subscription and received a status result of 21007. I have googled this error and am not finding any information about what this status result means.
Thank you in advance ....
Error 21008 is also another Error response that Apple returns if you point to the wrong production/sandbox server.
Do not point your script to the live Apple server when you're testing using the sandbox(!)
Lesson learned. Hope this post helps someone else.
I just returned from iOS 5 Tech Talk World Tour 2011 and the Apple Tech in the In App Purchase session said that the app review process uses the sandbox when reviewing a submitted app and that they would appreciate it if we would check for this error code and forward the request to the sandbox if we get this error code.
Error code 21008 was the same issue but for a different IAP. i.e. consumable versus non-consumable. I assume the same goes for that code as well. i.e. to forward to the sandbox if received.
Sounds like this might speed up the review process a little.
From Validating Receipts with the App Store:
Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you do not have to switch between URLs while your application is tested, reviewed by App Review, or live in the App Store.
I know that this is late but I figured anyone else come across this question, they would like to know what the proper URL to use.
You should use the: https://sandbox.itunes.apple.com/verifyReceipt URL to test with the sandbox.
If you have a server-side script that connects to the sandbox or real AppStore server to validate receipts, always try the real server first, and if you get a 21007 status code, fallback to the sandbox server, in order to support both environments when you submit your application for review, as they will test it with a sandboxed user account!
More info in the following post:
(iOS + StoreKit) Can I detect when I'm in the sandbox?
I'm seeing this also but from Apple's own tests (app is in review) and just received and email from Apple saying the review will take longer.
Perhaps it's some transient problem with App Store itself.

Resources