Persisting a Receipt in iCloud using CloudKit in iOS - ios

#1
I'm developing iOS App with a non-renewing subscription in it.
I want to make the subscription available on all of the user’s devices and to let users restore the purchase.
As said in Apple's docs:
For non-renewing subscriptions, use iCloud or your own server to keep
a persistent record.
I do not what to use my own server because my App is available only for iOS for now. So iCloud seems to be easer solution.
After watching and reading A LOT of WWDC videos and docs about iCloud seems like the best solution for me is CloudKit because Key-value storage is limited to 1MB and I have a big chances to get total data size bigger than this per one user (after a year of different purchases for ex.).
Question is: am I right so far?
#2
I'm using RMStore Library for purchases. As is it said in docs RMStore doesn't have reference implementation of Transaction persistence to iCloud and I couldn't find any examples in the Internet so I'll have to do it by my own from scratch.
The first problem staring me in the face is: what if there will be some problem syncing the receipt to iCloud after user has purchased the subscription? For example: user bought the subscription, got some error syncing it to iCloud, closed the App and that's it. Is this a real scenario? For non-renewing subscriptions receipt is not stored anywhere by Apple so I am fully responsible for delivering and saving it for my user. Should I immediately save the receipt in NSUserDefaults or in Keychain after transaction is finished to be able to compare synced data to local one next time user launches the App? Or maybe I should not 'Finish the transaction' until receipt is synced? I could not find any guides from Apple for this...
#3
The next obvious question is: Can user clear my App's iCloud private storage? Can user somehow delete stored in the iCloud receipt thereby deleting all information about his purchases? If yes - how should I handle it? If this scenario is real I have no way to recover his purchase and open App's functionality for him until he buys subscription again.
Thank you in advance.

CloudKit is not limited to 1MB. A record is limited. But if you have more than 1MB of data in a record, you should consider a refactor. If it's just some sort of data blob, then you should save it in a CKAsset. For that the limitations is much higher.
Syncing will be a problem no matter what technique you use. One way to improve on that is by registering a begin purchase flag, then do the purchase and after that set the purchased flag. Then if a record stays in the 'begin purchase' state you know something went wrong and you can check with the App Store if the purchase was successful.
you should add a restore purchases function to your app

Related

How to restore consumable In App Purchases?

I want to restore consumable In App Purchases for a game. This game has only consumable in app purchases such as:
$0.99 for 1000 coins
$1.99 for 3000 coins
Using NSUserDefaults to persist the coins is not good because the user could delete the app and when they reinstall, they lose their coins. Also Apple has a restoreCompletedTransactions method but this isn’t for consumables so the developer has to keep track of this.
Please don't mention to use GameKit (Game Center) or a Web Server. Are there any other solutions? I've read that iCloud and Keychain are two other possible solutions (not sure if these are good for this).
PS: There are many answers on SO that are a few years old and that won't work for my case, so I am asking here again.
Keychain and iCloud is what you are looking at. Keychain data will also not be deleted when you uninstall the app.
There is a good helper on GitHub for keychain, which makes using it a breeze.
https://github.com/jrendel/SwiftKeychainWrapper
General read about data storing
How secure is NSUserDefaults on iOS 8,9?
To store your data in iCloud you should use key value storage for small data.
I use a singleton class to handle all this. For a simple example check out this answer I posted
SpriteKit: Why does it wait one round for the score to update? (Swift)
which is based in this great article
https://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2
Hope this helps

Is it even safe to persist the IAP in iCloud?

My case is annual subscription for full functionality which falls into non-renewing subscription IAP category.
Information about consumable products and non-renewing subscriptions
is added to the receipt when they’re paid for and remains in the
receipt until you finish the transaction. After you finish the
transaction, this information is removed the next time the receipt is
updated—for example, the next time the user makes a purchase.
If I'm getting it right, once I retrieve the receipt, successfully validate and persist, I finish the transaction and thats it. I can no more retrieve receipt for that purchase. So I better make sure that the persistence cannot be lost or I'm screwed (actually the buyer is). But I want nobody to be screwed so I need to guarantee the persistence.
For non-renewing subscriptions, use iCloud or your own server to keep
a persistent record.
Okay I have no reason to use my own server and like the idea of iCloud very much. But also getting concerned about it.
Is there an iCloud storage I can prevent records from being deleted or at least inform about about annual subscription lost prior deletion?
For IAP persistence, Key-Value storage seems to be best fit (also used as example in guide) but not that reliable as well. As far as I know, these data are not deleted together with app, but can be deleted at any time in iCloud settings. There is no other way to delete them is there? If not, I understand and agree that user should always has right to delete content in his iCloud storage. But he may not be aware of loosing his subscription. What about this?
I'm not familiar with CloudKit so far. Can it provide more convenient solution? Or is there even a way to persist that receipt for the whole subscription remaining time using iCloud?

Use iCloud To Restore Non-Renewing Subscriptions

Apple's IAP Guide states that when using non-renewing subscriptions, you should offer a method to restore the subscription on all devices, using iCloud or your own server. My question is simply this (I don't have a server):
How in the world can you use iCloud to store that the user has a subscription and to restore it?
I'm just looking into this, and for what I understand you must save your relevant data (that is, subscription expiry, typically) in a file that will be saved when a iCloud backup is performed.
This way, the file will be sync in all devices, and your users will enjoy the subscription paid in one device, among all of them.
Customers should be warned they have to use iCloud or they won't have it available everywhere, of course.
Hope this helps,
jaime

How to 'fake' a restore in-app purchase for previous app users when moving from paid to free

Our app is moving from paid to free, and in the process, moving a key functionality from being included to activating via In-App purchase. Obviously, we don't want current users who paid for the app functionality to be charged again in the In-App purchase for functionality they already had. So on the update by the user, we want to 1) identify current users and 2) make it so they don't see the In-App purchase in the first place, sort of 'faking' the In-App purchase so that the app will appear to them exactly as it did before.
The app does not have a backend, so we have to determine current users from new by examining the saved user data fields for certain values. I do understand that if a previous user has deleted the app from their device that nothing can be done, and I don't mind charging them for the In-App purchase, since they never used the app anyway.
But for those current users who update and assuming we can examine the saved user data and determine that they are current users, what would be a good way to bypass the In-App purchase and make the app look like they already got it, when in fact they never paid for it? Thanks!
Here's what I would do - keep in mind this will take some time:
Set up a server (I prefer EC2) with mySQL on it. Plenty of tutorials about this.
Submit an update to your app that sends the user's UUID to your server.
Wait. This is the hardest part. You'll need to wait until satisfactory majority has updated to your app. That majority percentage is up to you to figure out. It could take months for this to happen.
Make your new, free, app send the UUID to the server.
Check to see if the UUID is in the DB.
If it is, set whatever you would have set when an in-app purchase was made to true.
You have several options:
Free in-app purchase for a limited time:
You would create a free tier in-app purchase content and release an update that somehow makes the user sign up for it. This way, when your user switches devices they can restore the purchase and regain the functionality.
Wait for a period so most people use the in-app purchase content
Change the tiers and release your app as free
Dual versions
Make a demo version of your app. Note this can be rejected by Apple.
Create a file in the filesystem
Make a file in the filesystem and save into iCloud. The app will check for the file and thats how you would determine if the user has paid for the app (or should buy the in-app purchase).
iCloud will synchronise the file between user's devices and it will make sure that whatever device the user uses the app will see the user as 'paid'.
I hope this helps, currently having this problem myself.

switching from auto-renewable to renewable subscription

My client has an app in the App Store that uses the auto-renew subscription type. Upon resubmitting the app it was rejected because they claimed they don't have periodic content (even though they do... but this problem seems to be ubiquitous currently). I was wondering what the best approach is to deal with non-renewing subscriptions. Specifically issues dealing with:
Losing your device and restoring your subscription
Restoring your subscription to multiple devices (as mandated by Apple)
Preventing users from abusing the system.
What I came up with so far is the following:
When a user purchases a 1 month subscription this information is stored locally (say in NSUserDefaults). Also, a unique ID is generated and sent to my clients server. When the subscription ends users are asked whether they want to be directed to the purchase screen. If users wishes to save the subscription in case they need to restore they device, or in case they lose their device, they can opt to have the unique identifier sent to their email address (which they are prompted to enter, indicating that this information will not be used). The app has an place to enter this ID. It will retrieve the subscription information from the server and again store this in the NSUserDefaults. Each time a restore is done, a counter is increased. When it has reached say 5, the user can no longer restore. The same principle described above works in the case of sharing subscriptions over multiple devices. Does this seem like a reasonable solution (and one that Apple will accept)?
Thanks for your time!
Why don't you want to use Apple's restoreCompletedTransactions? It just provides you with information about ALL the previous purchases, on any device, at any time.
http://developer.apple.com/library/mac/#documentation/StoreKit/Reference/SKPaymentQueue_Class/Reference/Reference.html#//apple_ref/occ/instm/SKPaymentQueue/restoreCompletedTransactions

Resources