Is it even safe to persist the IAP in iCloud? - ios

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?

Related

Is there a way from the iOS SDK to query a user's entire in-app purchase history?

For metrics purposes, I need to view historical in-app purchase data for my app's users. I am aware of the restoreCompletedTransactions API but it appears this is limited to transactions "that can be restored". Is there a way to get a history of all transactions, even non-restorable?
First Part of your question: the historical transactional data is stored in the receipt.
To answer the second part of your question, previous consumable purchases and non-renewable subscriptions that have already been used or expired will not be in the receipt so they will need to be kept track of ahead of time.

Persisting a Receipt in iCloud using CloudKit in 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

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

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

Differentiating between initial buy and free "re-buy" in StoreKit/In-App Purchase

From the StoreKit guide:
If the user attempts to purchase a nonconsumable product or a renewable subscription they have already purchased, your application receives a regular transaction for that item, not a restore transaction. However, the user is not charged again for that product. Your application should treat these transactions identically to those of the original transaction.
This presents a huge problem in an app I am working on. We have licensed a large body of content from a publisher for sale through in-app purchase. They require that every time we sell a piece of this content (i.e. user pays us), our server calls an API on their servers to report the transaction. This is for accounting purposes and ultimately used to determine how much we pay them at the end of the month, per our agreement with them.
I have read several suggestions on SO and elsewhere about calling restoreCompletedTransactions rather frequently and maintaining a local understanding, on the device, of what the user has already purchased so they cannot be allowed to purchase it again. This to me seems like something that should be able to be implemented on the server side. However, the receipts that we are getting back from the Apple servers are exactly the same for a buy and a re-buy, as promised by the StoreKit guide.
If payment callbacks from StoreKit cannot be trusted as a valid accounting mechanism in this kind of situation ("you got paid" vs. "you didn't get paid"), what other real-time insights into transaction traffic are available? I don't think the publisher we are working with is going to be happy if we tell them we have to wait 45 days after the end of the month to get the REAL paid dollar amount out of iTunes Connect.
I have recently looked into the same problem. In my case, I wanted to implement accurate revenue tracking using Mobile App Tracking to track revenue generated from different customer acquisition campaigns.
Fortunately enough, there is a way to do it. It should be noted that SKPaymentTransactionStatePurchased vs. SKPaymentTransactionStateRestored solely depends on the initating action, e.g. whether you initiated a restore or a (re-)purchase, so that doesn't work.
What does work instead is checking for SKPaymentTransaction.originalTransaction which will be != nil for restores and re-purchases. The latter is unfortunately undefined behavior (docs). I'd consider a null check fair enough though.
Another option is to validate the transaction-receipt of transactions with SKPaymentTransactionStatePurchased and check that the original_transaction_id property in the returned, validated receipt matches the transaction_id.
The bad news is: In the current iOS version (4.3.x) there's no way to distinguish between a buy and a re-buy of non-consumable products.
To ease the situation I would recommend two things:
First
After a successful purchase, store the product identifier of the purchased product in the NSUserDefaults on the device. You can then hide the already purchased products from the user and thus handle a re-buy situation.
The NSUserDefaults are backed up by iTunes when the user synchronizes his device. So your stored purchase information is not lost when the user gets a new device.
Second
Store the receipt data together with the device ID on your server. Analyze the receipt's product identifier and the device ID.
If you receive another receipt with the same product identifier and device ID combination, then assume a re-buy. At least this would allow you to cover most of the re-buy cases.
Assuming that an ordinary iPhone user switches his device every 1-2 years, you will at least cover most of the re-buy cases and maybe apple will fix this in the future.
I have one solution,
Configure the product as consumable.
this will solve the problem - (They require that every time we sell a piece of this content (i.e. user pays us)).
Next you need to implement a logic in product buy option. It is in a way that once the user purchase a product the buy option need to remove otherwise the user may happendly go purchase and lost is cash once again for same product in same device.
you can use NSUserdefaults for this purpose.
thanks,

Resources