I have an auto-renewing subscription in my macOS app. The initial purchase refreshes the app receipt, which can be locally checked to validate whether the user has an active subscription.
When the subscription automatically renews, the app receipt is not updated. Even when the transaction passes through the SKPaymentQueue on app relaunch, the receipt is still not updated.
The only way I've managed to get a current receipt is to use SKReceiptRefreshRequest, but this prompts the user to input their App Store login credentials.
How can I make sure the user's subscription information in the receipt is up to date without using server-side receipt validation?
EDIT: The behavior described reflects my experience with the StoreKit sandbox. If the same behavior can be confirmed to be nonexistent in production, that would be awesome.
Related
I'm creating a Mac app that has a non-renewing subscription as an in-app purchase. I want to sync this data to the cloud for the following reasons:
I want the user to be able to use the app on all their Macs
The in-app purchase enables a widget, so that widget needs to access this data.
By Apple's documentation the restoration of non-renewing subscriptions has to be handled by the app by some kind of registration / cloud sync.
So I decided to implement a CloudKit sync to store the following data:
Which IAP product did the user purchase (currently only one value, but might change in the future)
When did the purchase occur.
Here's what I'm doing now when a user makes an in-app purchase:
I validate the receipt
If I find any IAP data, I sync that to CloudKit
I use a function to fetch the purchase data from CloudKit for the said widget
Question 1: As far as I can tell all the in-app purchases are contained in the receipt file, even after removing and restoring it. I could be using this, however the documentation clearly says I shouldn't. Even the forums are not certain about it... What do you think?
Question 2: While testing the app with multiple sandbox users I noticed that no matter which sandbox user I use to make the purchase, the currently logged in iCloud account (my personal account) gets the receipt data synced to CloudKit. Why isn't the purchaser (App Store) user getting the data to their private cloud database? How can I test that everything works fine? Do I have to log out of my iCloud account to make this work?
Thank you for your time :)
Question 1: Using the App Receipt to restore non-renewable subscriptions
From Apple's Documentation:
Apple's documentation on whether an in-app purchase of a non-renewing subscription remains in the receipt has contradictory answers:
Yes (retrieved 2016-05-11):
Table 1-2 Comparison of subscription types
Subscription type Auto-renewable Non-renewing Free
Users can buy Multiple times Multiple times Once
Appears in the receipt Always Always Always
Synced across devices By the system By your app By the system
Restored By the system By your app By the system
No (retrieved 2016-05-11):
The in-app purchase receipt for a consumable product or non-renewing subscription is added to the receipt when the purchase is made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed from the receipt the next time the receipt is updated—for example, when the user makes another purchase or if your app explicitly refreshes the receipt.
From Apple's Developer Forums:
In a thread reporting the temporary (now fixed) loss of non-renewable subscriptions from the app receipt, an Apple Developer Technical Support engineer said:
I've queried the iTunes Production Support engineer who made the change - The "fix" to provide the history of non-renewing subscriptions in the application receipt is permanent. My interpretation is that permament means that if we make a change, we'll announce the change at a Developer Conference and announce the function to be deprecated for a period of time.
With regards to using iCloud as a means to restore non-renewing subscriptions, I've heard from my App Review contact - an application "can use iCloud to track the non-renewing subscriptions (NRS) but it can’t force the user to login prior to making the purchase. It has to be optional - that can can alert the user that iCloud is required to access the NRS content from their other iOS devices - and providing a way to register later, if users wish to have access to this content at a later time."
Source: https://forums.developer.apple.com/thread/22345#79067
You could attempt refreshing the app receipt to restore the non-renewable subscriptions, but it has broken before, the documentation isn't clear, and there have been past reports of App Review rejecting applications that try this method.
Question 2:
CloudKit always uses the current iCloud account under Settings > iCloud.
App Store purchases use the Apple ID configured in Settings > iTunes & App Store.
The user may be signed into the same Apple ID for both iCloud & iTunes, but there is no guarantee. These are two entirely separate settings.
I was looking to this thread.
It's stated that the App Store calls the paymentQueue and posts a transaction with transaction.transactionState==SKPaymentTransactionStateRestored.
But I'm not getting those calls. I'm testing
with sandbox accounts/subscriptions, that expire and are auto-renewed every 3 minutes;
WITHOUT verifying receipts.
This way, my updatedTransaction method is never called after the first buy.
Anyway, if I later on restore completed transactions, I get all the receipts.
Any experience with sandbox? Also, the point 2) is needed or (as I think) it's optional?
The state: SKPaymentTransactionStateRestored is only determined if you have the receipt on the user's device and you use it to make the transaction. If you do not have the receipt, then you will need to call restoreCompletedTransactions to decide whether to apply the auto-renewed subscription.
The situation when you won't have a receipt is when the user buys an auto-renewable subscription, then deletes the app or installs the app on a different device. In other cases, you can safely store the receipt on the device.
The issue is detecting the case when the user has purchased an auto-renewing subscription and needs to have the content activated. Without a receipt, if you trigger the transaction, it will fail with the state SKPaymentTransactionStateFailed. Looking at the error code, you will see SKErrorPaymentCancelled. This, as far as I can tell, is a bug on Apple's part. You will need to make the best decision for your app to deal with it :/
I am working on one app in which i have implemented the in app purchase and also successfully verified the receipt.
I have done the auto renewal subscription in which I want to know how I can check the user subscription is active or not?
In order to verify the receipt i need the receipt from the SKPaymentTransaction. So my question is that how could I can check when user returns to my app that he still has subscription or it expired.
or simply which method can give me the user transaction property of SKPaymentTransaction on load of my view so i can verify the receipt.
Thanks in advance,
Satish
Finally I did it in the following way...
I save the transaction receipt in userdefaults when user first time subscribes to the subscription and when later I need to check for the subscription activation I use that receipt which tells me the latest expiry day..
Hope this will help...
my question is: If a subscription receipt expired, do i have to call [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]?
Background:
In sandbox, i dont get a new receipt automatically, so i think i have to use restoreCompletedTransactions.
But in this way, the user has to enter his password every week, bad user experience.
And if the user has turned of the subscription, it has expired and starts the app again. The popup has to appear?
The way to handle this is to store any or all receipts your receive that pertain to the subscription. Then when you occasionally verify one of these receipts with Apple, they will send you the latest pertinent receipt which will tell you the status of the user's subscription. This process doesn't require the user to enter their credentials.
Note: Apple prefers that you perform verification from your server, and not from the app, so that you can keep your secret private and not include it in your app's code.
In the this Article Apple wrote how to enable an auto-renewing subscription from the users point of view. But my question is how to handle this auto-renewing subscription as the developer of an app?
In my app (currently in developing) the user can buy some subscriptions (30 days, 3 months and 1 year) via in-app-purchase. After a successful payment I send the bought item identifier to my server to save the new subscription time (also used for other platforms). This works perfectly in the sandbox-environment.
But if I correctly understand the article the auto-renewing subscriptions is performed from inside the AppStore and inside my app. How can I now track the subscription?
If a subscription is autorenewed, the transaction won't pass the paymentQueue:updateTransactions method. The renew just happens on the Store.
If you want to test for it you have to either:
Revalidate the receipt on your application server, if you store the receipt there.
Revalidate the receipt on your iOS client
See: http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP40008267-CH104-SW1
In order to avoid testing for an autorenew each launch/activation you should store the endDate of the subscription period to test for a renew afterwards.
Also see: http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/RenewableSubscriptions/RenewableSubscriptions.html#//apple_ref/doc/uid/TP40008267-CH4-SW4
However, there seems to be a bug in the sandbox. Subscriptions sometimes get renewed, sometimes not. Hard to test....
based on the (rather scant) info found in apple's in-app-purchase documentation, my impression is that whenever you need to determine the state of a user's auto-renewal subscription, you would restore their transactions.
this would cause the app store to send all auto-renewal transactions to your app, at which point you would process the receipts and make the appropriate content available.
presumably, you would only need to do this when the user's current subscription (which you can track locally) is set to expire, or when they are first installing the app.