iOS/macOS In app purchases - what to save in KeyChain - ios

I am implementing In app purchases for one of my apps which is cross platform (iOS + macOS). The way I implemented non-consumable IAP, is to save a flag in keychain for a specific key after the purchase was made. (e.g value true for the key com.app.prodctId) and just check at runtime if that flag exists and what the value is. However, this seems very insecure because some users might just add the key with the specific value in keychain, thus gaining acces to the locked feature without purchasing it. A solution to this would be to encrypt(or hash the data using a salt) the flag before saving it, but I would need to have a separate key for each iCloud account (so the user can enjoy the produc ton all devices across his iCloud account) and I am not really sure what this can be.
Is there anything that can be fetched per iCloud account so I can use as encryption key/salt for a hash? Or is there a better way to manage non-consumable in app purchases?

For every user downloading your app, Apple creates a receipt containing some meta-information (which app version the user downloaded, when, and so on).
When your app offers in-app purchases, those are also saved in the receipt. Since this receipt is automatically synced with all devices of the user, it is the right choice to check what a user bought and unlock the content on all of his devices correspondingly.
See here for an article from Apple about receipt validation techniques.
If you choose the local (on-device) receipt validation, I recommend you to use the TPInAppReceipt library to encode the receipt. That saves a lot of headaches.

Related

iOS in-app purchase for a paid app

I created an app which is a paid app. Now I want to make it free so more user can download the app and use in app purchase to limit some features. But some user already paid to buy my app. How can I implement in app purchase for new user at the same time keep full feature access to old user?
If you connect to your server for registering user info, you can always create an API which executes on app launch to verify that user is full access user or not.
But I am afraid your case is not the above one.
In that case you would require to sync your data (some encrypted key in this context) with iCloud and when application is launched you can verify the type of user.
Using data in iCloud is more safer as compared to keychain as it covers device format scenario. But definitely not foolproof.
Other solution can be using Apple Purchase Receipt to verify the version of previous purchase. But this is only supported since iOS7.
Checkout some opensource libs to understand the parsing of receipts:
https://github.com/rmaddy/VerifyStoreReceiptiOS
So combining multiple strategies is the only answer for your question.
You can do this by reading the App Store receipt. The receipt contains the version number and date of the original purchase.
There are two main caveats: first, this only works on iOS 7 and above. Secondly, Apple don't include code for parsing the receipt (so it's not too easy for users to hack I understand). There are, however, onen source libraries, though using a common one will be less secure.
There are no perfect solutions to this scenario.
Suggestion 1:
Roll out one last paid update. In this update, use keychain to store those IAP flags. Then in the free version, check for these flags in keychain. This will work even if app was deleted and reinstalled with the free version later. But it will not work if the device is being reset completely whether due to some iOS version updates or user's unless a backup and restore also is involved.
Suggestion 2:
Not quite a suggestion. But I have seen similar apps on AppStore have just rollout free version. Then app incurred bad reviews from those previous users!
This is a simple example, but if you're working with a database on a server (not on the phone itself), can't you use a boolean for each feature you plan on selling, and just set that boolean to true for all users currently in the database. This is assuming true means they've bought the feature, and false means they haven't bought it.
You could run this query once after releasing your updated app, and then every user after that would have a default value of false for these features you're selling.

Tracking iOS In-App Purchase currency

We have an app where we offer consumable in-app purchase which is in the form of virtual currency. (e.g. 100 coins) The in-app currency is used for some of the advanced features in the app and the user can replenish them as needed.
We are looking for a way to reliably track the virtual currency (surviving reinstalls) and are leaning towards a server based solution where we can track these in a database. The other factor in play here is that we want to reduce the friction in the app so there is no login, registration or fb connect.
We ran into one issue with this approach:
- We need to find a unique identifier associated with the "device" that will survive reinstalls.
Also, we realize that this approach wouldn't support synching the currency balances across multiple devices of a single user. Any way to tie this data to user's appstore account?
Generate a fingerprint when a user logs in to a particular device for the first time.
Save this fingerprint in the keychain with the with the value "kSecAttrAccessibleWhenUnlockedThisDeviceOnly" for the "kSecAttrAccessible" key.
Things to consider,
Item will keychain will persist even user deletes the app
Keychain Items are backed up and restorable even to other device by since you using the above key the fingerprint would not be restored to the other devices.
If the devices ownership is changed then replace it with a new one for the new user or associate with the new user.

Non-Renewing Subscription Restoration

Background:
Apple requires you to implement a restoration mechanism to this type of purchases to let users to get back their purchases after wiping the device or to view purchases from user's other device.
One accepted approach form apple for doing this is to do an optional user registration and handle it on your server to save the transaction receipts. since apple don't do that like other type of purchases as non-consumable. which allow you to get all the receipts of the users to restore them.
Apple say that the registration should be optional. but we must indicate to the user that registration is required to view purchases from other devices.
My Suggested Approach:
I am going to implement the restoration mechanism by saving the purchases receipts in iCloud. I will indicate that logging into iCloud is required to access the subscription content from user's other iOS devices.
On purchasing succeeded app. will check if iCloud is available to save the subscription details(Transaction Receipt) Otherwise, subscriptions will be saved to User Defaults. Every time the app. is launched it will check if iCloud available and iCloud will be synced with User Defaults. subscriptions details available on User Defaults and not on the iCloud will be copied to the iCloud and the subscriptions that are available on iCloud and not on User Defaults will be copied to User Defaults.
Thats provide users the flexibility to login to iCloud in a future time just to move their subscriptions to another devices or just before they decided to delete the app. to save their subscription. (That all will exactly perform as we had the user registration option). Is that accepted?
Another thing. My app. suppose to work on iOS 4.x too. which mean that iCloud is not available. Is that ok too? or I have to give up running app. on iOS 4.x if I want to use iCloud approach? what if I also indicated that restoration will not be available for iOS 4.x?
One way is that , the restoration of payment and other data should be handle by your application or by your side on the server. But i think it will increase your burden of coding. But registration is optional and if user wants that he will be able to use application on his other iOS devices also then u can make registration compulsory, otherwise the restoration of payment or data should be done using keychain.
If u have still doubt then let me know.
So, in this case what if a user makes a purchase without signing into his iCloud account? We won't be providing him the content access on his other device in that case, right?
Is there any way to handle that scenario? Are we allowed to store the user's id and password into the iCloud? This may help us, but won't be a good idea to store the credentials.

iOS In-App Purchase No Back-end

I am investigating the use of in-app purchase for what essentially would be a "pro" version of my app.
The app itself would be free but once in the user has the option to purchase the pro content (only 1 thing). The "pro" content would already be on the app and there is no need to download it, it would simply "unlock" it.
Is this allowed from the Apple Guidelines?
As only 1 non-consumable would be purchased I think the use of a back-end server isn't required.
Again is that allowed from the guidelines?
And is it safe and simple to just store the result in NSUserDefaults and if installed on another device pull it from SKPayment restore purchased and such?
I've looked at several other questions.
In-App Purchasing?
Retrieve purchased information in In-App purchase
How do I add consumable In App Purchases using NSUserDefaults and not my own server?
And those seem to suggest that my approach is valid, but as I know those things have changed recently I want to make sure I'm taking the right approach.
Thanks!
No problem having the content built in.
Best practice is to perform receipt verification on a server with an authentication protocol between the app and server (this is also true for several other mobile app stores). If you perform the verification on the device, people can use existing tools to get around your IAP checking and steal content. Take a look at https://developer.apple.com/library/ios/#releasenotes/StoreKit/IAP_ReceiptValidation/ for some information.
So while a server is not required, it is recommended. Only you can say if protecting your content is worth the hassle of maintaining a server.
I agree with J. Freeman that straight storage in NSUserDefaults seems weak. I store things in a local file but the format is tied to the device and requires a server computed key to create it. Finally, yes you should use SKPaymentQueue restoreCompletedTransactions to get things purchased on another device. Realize that the restored transactions should also have their receipts verified on your server.
Yes that is fine. You do not need a backend to do in-app purchases, and it is ok to ship with your content built in.
The one thing I would say be careful with though is storing the unlock information in NSUserDefaults as someone will easily be able to forge purchases that way. You should store the unlock information in the keychain.

Saving information about previous purchased items in iOS

I am working on an app that has a number of in app store purchase items that will enable certain functionality.
After each item is purchased, I would need to remember it (of course). I would also like to persist this information in iCloud in case the app is deleted or is installed on another device.
What is the best data structure to be used?
Can user defaults database be used for this?
What is important is that the user will not have access to change those values and enable by themselves the paid functionality.
Can user defaults database be used for this?
It can, but it's not the best idea to do so, since
What is important is that the user will not have access to change those values and enable by themselves the paid functionality.
and NSUserDefaults stores its contents as binary or plaintext property lists. Easily changeable on a jailbroken device. (Also changeable on a non-jailbroken one by modifying the iTunes backup files).
What you could do is either store them in the keychain, although the keychain is not really designed for this (and it can also be dumped on a jailbroken device using Ptoomey3's awesome Kaychain-Dumper tool), or better store it remotely on your server and let your server check what the user has purchased.
Of course, if it's not only the server that does the check, so for example you don't only send or don't send content based on purchases, but you also use this check for performing actions within your app, then it also can be hacked (google "MobileSubstrate cheat DoodleJump" for a nice example).
All in all: there's no perfectly secure system.
I use the KeychainItemWrapper class to store a flag in the keychain. The nice thing is, so far, is that the keychain survives an app deletion. IF the user then reinstalls the same app on the same device, the keychain data is still there.
There is no need to use iCloud. If the user installs your app on another device, you just need to provide a "Restore Purchases" button in your app. Then you call the Store Kit APIs to restore existing purchases. The "In-App Purchasing Programming Guide" covers how to do this.
This same functionality will allow a user to restore their purchases even if the flags in the keychain are lost.

Resources