Saving information about previous purchased items in iOS - 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.

Related

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

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.

iCloud Keychain required for kSecAttrSynchronizable?

I have implemented synchronised keychain storage in my App with the SecKeychain API using kSecAttrSynchronizable. Am I correct in thinking that iCloud keychain must be enabled (iOS settings > iCloud > iCloud keychain) for this to work?
If so, is there any way to tell if this is enabled?
The kSecAttrSynchronizable attribute is used by your app to tell keychain if it is desired behavior that this keychain entry is synced to other Apple devices of that user or not.
Why would you desire an item not to be synced? E.g. the user doesn't want it to be synced as he wants to use a different password on his iPhone and on his iPad; so you may like to offer a pref and let the user chose if he desires syncing this password. Or syncing this password is simply not meaningful, e.g. it is a password that is used to encrypt local data of your application, local data that will only be present on the current device and thus this password is of no use to other devices, they lack the data for decryption.
However, your app can only express the wish for your item to be synced, whether syncing takes place at all is up to the user. The user decides if he wants password syncing at all, after all password syncing requires the passwords to be stored on Apple's servers. They are stored encrypted and not even Apple themselves have access to the password data, yet some users still don't have a good feeling having when that kind of information lying around on public servers on the Internet. So unless a user enables syncing, no passwords are synced.
Why would you need to know if the user has enabled it? To tell the user that passwords are not synced unless he enables syncing of passwords? Sounds a bit like telling the user that the room is dark, unless he turns on the light. If it is not on, the user may have chosen so on purpose, you cannot know if he did, can you? If you offer an option for password synchronization in the app prefs and the user enables is, just show an info text below that this feature requires iCloud keychain syncing to be enabled or else will have no effect.
Other than that, if you don't offer such an option, it should still be pretty obvious to all users that when your app promises "Now supporting iCloud Keychain Synchronization" that this feature will only work if "iCloud Keychain Synchronization" is enabled, don't you think? So to my best knowledge, there is no API that will directly tell you. One may detect in indirectly through other means but I don't recommend such kind of hacks, they are very fragile and sooner or later break with an update.

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.

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.

iPhone app consumables - Server vs keychain method

I had an iPhone app developed by a company, which contains consumables purchases, specifically a currency named as "coins", used for app-specific purchases. In the current version, the team has added the ability for a user to register under a username/email/password. This information is stored on my external server and used as a recovery for the user's purchased coins via signing in using these credentials.
The model looks fine programmatically but is not convenient in terms of usability. Ideally, i would want my users to not have to register at all. I have been reading that it is a possibility to store the user's purchased consumables on the client side, using the internal keychain.
Now this would imply that consumables can only be restored on this single device (i guess unless iCloud is used). But is this allowed by Apple ? Or there should be a method to restore across different devices ?
If the keychain method is allowed, I believe that it would be way better for my app to go with that. I just wanted to make sure that Apple would not have a problem with that. I have been reading that it's allowed, but do not want to have unpleasant surprised after changing the whole client-server model to client-only keychain.
If you have experience with that, could you please let me know what the case is ?
EDIT : Somewhere on SO I read that using Gamecenter's GKPlayer class is also a way to store information on my server and retrieve that automatically in order to identify a user purchase. This would mean that the step of username/email registration is avoided, which is something i really really want to avoid. Is this really a possibility that is allowed by Apple ?
EDIT 2 : I am now thinking that I will keep the client-server model but lose the username/password and use the device id instead. This way i will be able to keep the restoration of coins working on a specific device. Would that be ok you think?

Resources