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
Related
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?
#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
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.
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.
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