Validating iOS in-app-purchases using my own server - ios

I want to validate iOS in-app-purchases using my own server. The iOS app will talk to my server which in turn will talk to Apple's server to determine if the IAP is valid. I'm fairly new to networking so I have a basic question: How can I make sure my iOS app is talking to my server securely?
I imagine the app will talk over https but I don't know how this works. Any advice on setting up https communication between the two (or alternate methods of secure communication) are greatly appreciated!

Lawson has a point. You can't make your server perform the purchases directly at the AppStore. That has to be performed by your app.
It should request the available product identifiers from your server and then send an SKProductsRequest to the AppStore.
If required it processes the purchase over the app store and tells your own server about it by sending a so called receipt. The server can validate the receipt directly at the AppStore. But that is the only thing the server can do it self at the AppStore.
You can read more about it here: Overview of In-App Purchase: Server Product Model
As for the connection between your app and your server, I don't think you have much of a (reasonable) choice but to use SSL. Almost anything more secure would require a PKI.

Sorry about the previous answer... I misread your question. You can have your server send a POST request to Apple's server, then parse the response.
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

Related

Can we store iOS in app purchase receipt to our server

I am developing an in app purchase app (auto renewal) and purchase/cancellation should effect on all platform (Android, iOS, Web).
My question is what is best way to keep track the latest status of the purchase. I know there is a way called server to server notification using web hook, but I am thinking can we store the receipt data to server and validate this receipt time to time with iTunes apis?
Does receipt data change over the updates on subscription or it is same even after changing the device?
All I want to validate it at server side, because there is a possibility that user can uninstall the app or not using it.
You can validate the receipts at server end. There are two options.
Enabling server to server notifications.
Store all receipts in your databases and verify it with server for latest update.
You need to use meta data from the receipt under key "latest_receipt" to get the latest update in the subscription.
Below is the link for reference.
https://developer.apple.com/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store#//apple_ref/doc/uid/TP40010573-CH104-SW1
Initial receipt meta data does not change over the time. You can save initial receipt metadata in your database and use it for further updates like - renewal, cancellation, upgrade or downgrade, refund etc.
Yes, you can, moreover server-to-server validation & observing is most preferable way recommended by Apple. They provided JSON API and accepts your server end-point to post any changes directly to server.
For details read Choosing a Receipt Validation Technique and related in topic.
The common start point is In-App Purchase

Why should I use my own server to validate iOS receipt?

I want to validate iOS receipt.
I thought I would send a receipt to the App Store verification server (https://sandbox.itunes.apple.com/verifyReceipt or https://buy.itunes.apple.com/verifyReceipt).
But Apple reference says:
It is not possible to build a trusted connection between a user’s
device and the App Store directly because you don’t control either end
of that connection.
And apple recommend sending a receipt to my server then send it to the App Store verification server to validate.
(https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1)
I don't understand why a connection between a device and the App Store is not trusted regardless of using HTTPS connection.
Your app is running on hardware controlled by the user. They have physical access to it, and can do anything they want with it. The operating system doesn't make it easy for a user to mess with things, but it can be done and hackers do it.
You can validate the iOS receipt on the iOS device. But you cannot be sure that the receipt is actually valid. The user could have hacked the device to make you think the receipt is valid.
I don't understand why a connection between a device and the App Store is not trusted regardless of using HTTPS connection.
HTTPS does not protect you from a hacker who has physical control over iOS device. A hacker can install different SSL keys on the device, allowing it to connect with a different server.
When your app tries to communicate with Apple's server, any network administrator can change it so that some other server is contacted instead of Apple's one. This server would normally be rejected because the SSL key will be untrusted... but if the user controls the device, they can make it trust an invalid SSL key.
Your server, however, is controlled by you. Your customers do not have physical access to it. And therefore your server (hopefully!) cannot be hacked. This means your server can be trusted, unlike the device. When your server establishes an SSL connection to Apple's server, you know you really are talking to Apple's server. Not one that your user installed to bypass in-app purchasing.
So, if the user buys something in your app... you don't want to store the thing being purchased inside the app. You want to store it on a server, and that server only sends the purchased data to the device after it has verified the receipt with Apple's server.
If you don't want to spend money running your own server, then you will simply have to accept that any tech savvy person with a few hours of free time can create fake iOS purchase receipts and convince your device that they are valid.

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

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.

could I validate user auto-renew subscription directly inside the app? or my App would be rejected?

The server code to validate a receipt its very simple, so could I validate the subscription directly on my app? or I must implement it in some server in order to not get an Apple rejection?
and if my application have videos that are added every month, that kind of content might cause an app rejection to?
thanks in advance!
you don't have to validate on a separate server. you can validate directly with the apple server. it is however not recommended. i have several apps that validate directly with the apple server (all accepted) because my client does not want to deal with the verification on their end.

(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.

Resources