How to validate an iOS consumable in-app purchase? - ios

When a user purchases a consumable, my SKPaymentTransactionObserver receives an array of SKPaymentTransaction. The app can then check the transaction.TransactionState and transaction.Payment.ProductIdentifier to verify that the iap has actually been purchased.
But my app uses that in order to allow the user to download content from my web service. So the question is how can the web service validate that the app has actually purchased the iap, and not that it's a fake app just saying it did? I need some way to validate the SKPaymentTransaction on the server. Either by a signature, or by an app-store verification-service.
Windows has a way to do this on UWP . Is there a way to do this on iOS?
I've checked SKPaymentTransaction , SKPayment , etc. but all either don't show a way for validation, or they're about non-consumables (which are kept in the app's receipt).

There are two ways to validate an iOS consumable in-app purchase.
Firstly, You can validate the receipt locally in the app(see Validating Receipts Locally) . However, it is still susceptible to cracking.
The safest way is to perform server-side validation by sending the receipt to your server then sending it on to Apple (see Validating Receipts With the App Store).

Related

Correct workflow to online validate non-consumable in-app purchase receipts on iOS?

What is the correct workflow to online validate the in-app purchase of non-consumable IAPs on iOS?
While I found a lot of information about in-app purchases in general I found no details on how to actually perform online validation.
Is the following workflow correct and complete or am I missing something?
General setup:
iOS app starts purchase by adding an SKPaymentQueue to the payment queue
iOS calls paymentQueue:updatedTransactions: with transaction.transactionState == SKPaymentTransactionStatePurchased when purchase is complete
At this time the app receipt has already been updated and the purchase of the non-consumable item was added.
The receipt data and transaction.transactionIdentifier are send via HTTP POST to my own web service
The web service POSTs the data to Apples /verifyReceipt endpoint
Apples server response with an error if the receipt is invalid or with detailed JSON data if receipt is valid
The JSON Data received from Apple includes information about all prior non-consumable and subscription purchases AND the current consumable purchase.
The information about the consumable purchase can be identified by the product_id field or by using the transaction.transactionIdentifier
The web service adds the purchase to the current user account and/or sends back an valid/invalid response to the iOS app.
If necessary (when the purchase is not stored/handled online) the iOS app can handle the purchase (add coins, or whatever)
The transaction is closed using [queue finishTransaction:transaction]
Additional Observations:
The receipt includes information about all prior non-consumable and subscription purchases AND the current/last consumable purchase.
The information about the current consumable purchase is available until the next purchase (no matter if consumable, non-consumable or subscription).
Restarting the app does not destroy the information about the current consumable purchase
When the app is deleted and re-installed the receipt is empty / does not exist. After restoring previous purchases the receipt only includes information about non-consumable and subscription purchases. No information about non-consumable purchases is restored (no surprise)
Conclusion:
If (for some reason) the receipt cannot send be to the web service (server down, no connection, etc.), this can be retry later. The purchase data will be available within the receipt until the next purchase.
To be able to access the receipt data even after the next purchase one has to implement a custom storage method (e.g. save receipt data to file).
This way the user can be informed about outstanding validations and a retry option can be offered.
Is this a complete and valid workflow to validate consumable in-app purchases online or am I missing some special cases?

iOS In-app purchase in multi user app

I just run into a problem with in-app auto-renewable subscription. The app contains this kind of subscription and the app can be used by multiple users but the subscription is bind to the apple id is used on the device. So if a different user log in to the app than the system say he has a valid subscription. If I log the subscribed users on my backend server than if a user without subscription log into the app can not make a new subscription according to the Apple's response because the apple id used on the device. An other problem if a keep track of the subscriptions on my backend server if the user unsubscribe on the apple's webpage I can't notify the server about if.
What do I do wrong? What is the right workflow for this case?
I hope do you understand my dilemma.
Thanks!
Your use case is absolutely valid and that's exactly how the subscriptions In-App purchase work with any platform (Apple/Google).
In case of Subscriptions in-app purchase the content delivery is entirely the responsibility of the app provider and not the platform. You have no direct way of identifying if the app user has been switched to a different user as you can't access the current logged in user on iTunes account on the device.
You need to manage this use case on your own by keeping some data locally on the device and maintain user purchase history but still that wouldn't solve the purpose 100%. When user will go for purchase of the subscription it will show the service as already subscribed unless the iTunes user account is also switched on the device.
You can keep track of the unsubscribed state from the backend as when the subscription is successful you will get a receipt from iTunes which you can use to save in your DB. You need to run a backend job on the server side to validate the saved receipt to check the updated receipt which will give you details if the subscription has been expired or not.

Determining status of itunes subscription from webserver

I have some subscription-based content that should be accessible to the user from their iOS app and from the website as well. My understanding of in-app purchases (IAP) on iOS is that I have to set up some "products" in the App Store that the user then "subscribes to" from the app. Apple provides functionality (detailed here and here) on how to check subscription status from within the app in this case.
My question is :
What if i want to allow the user to access their subscription on the website ?
Is there a way to check the status of an IAP subscription in the App Store to see whether it is active or not from my web server ?
It's not clear from Apple's documentation whether this is the case.
Validating Receipts With the App Store
This is the documentation from apple on how to validate a receipt with iTunes. This does not necessarily have to be done from iOS. Once you send the receipt data to your web service you can make the same call directly to iTunes from there as well.
So to answer you questions:
1- Once you validate the receipt with iTunes from web, you can upgrade the user account to have a subscription.
2- You can validate the receipt data with iTunes whenever you like, in the response from iTunes one of the fields is the latest user receipt. This will allow you to check if the user still has an active subscription or not.

Restore Purchase in iOS InApp Purchase

I am developing a magazine app which has autorenewable subscription.I have implemented the purchase method and doin the validation using the receipt.My doubt are
1 . How to know whether the subscription has expired or not from the receipt
2 . How to restore the purchases or downloaded contents to another device .Currently my own server is providing the contents to download.So If I purchased some magazines in January and February, then if I try to restore the purchase in another device in march,how could I restore the downloaded contents or check with my server to download the previously downloaded contents.The receipt contains only product ID and nothing related or unique to user , to handle users in server so that the downloads and restore can be handled from the server.
Thanks.Looking forward for expert help.
Each user's device can obtain a receipt and either hand that receipt to your server or decode it and hand the results to your server. Your server will then respond to that device by downloading whatever it is entitled to receive. The receipt contains all of the purchases made under that account and the dates the purchases were made so you should be able to figure out the active time of the subscription. You could also use the 'expires_date' if the question is only whether the subscription is currently active.
If a device needs to prove that its subscription is valid it first tells the user that it needs to check on its subscription and then it executes either a restoreCompletedTransactions or a SKReceiptRefreshRequest.

Apple inapp Non-Renewing subscription purchase, force-require login?

here's my straightforward question:
is it allowed for my iOS application to force users to log into my custom account management system, so I can link them to my backend when they want to purchase a non-renewing subscription?
Here's the backstory:
I have a non-renewing subscription. For a good amount of users I can see that the Apple receipt does not contain information about this subscription; the in_app array is empty. This is correct according to the API:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Products.html
The in-app purchase receipt for a consumable product or non-renewing subscription is added to the receipt when the purchase is made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed from the receipt the next time the receipt is updated—for example, when the user makes another purchase or if your app explicitly refreshes the receipt
For some users, I can still see the purchase in the receipt, but let's consider that a bug from Apple and follow their API documentation to the letter.
(More on why this is a bug here https://forums.developer.apple.com/thread/22345)
In order to restore the user's purchase, I'd need something to identify him with. I could create a unique token, store that on the iCloud keychain and use that across the devices to detect the purchase belongs to that Apple account, but since my application supports account creation, I'd rather just use that mechanism. Else I'm using two different methods of purchase detection side by side: iCloud keychain token or a user account.
Thing is, I'm not sure I am allowed to force users to make an account before making a purchase. Is this something Apple would reject the app submission for with a message like "Your users must be able to buy stuff without going through your lengthy account registration process" ?
From Apple :
Non-renewable subscriptions. Subscriptions that don’t involve
delivering episodic content. Examples include access to a database of
historic photos or a collection of flight maps. It’s your app’s
responsibility to make the subscription available on all of the user’s
devices and to let users restore the purchase. This product type is
often used when your users already have an account on your server that
you can use to identify them when restoring content. Expiration and
the duration of the subscription are also left to your app (or your
server) to implement and enforce.
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Products.html
So its your app's responsibility to check that user has valid subscription or its over and let them purchase it again !
For this you will ask user to first make account or login into app so by this way you can track their subscriptions.

Resources