Auto-Renewable subscription renewal and cancellation detection and receipt refresh - ios

I've implemented a local receipt validation routine to extract the expiration date of subscriptions and it is working correctly. But I'm having hard time finding out when exactly the receipt gets updated automatically and when should I force a refresh.
I have a couple of question regarding the matter
1) Does SKPaymentQueue.default() ever gets updated externally by Apple? Or it only gets updated when a transaction is triggered on the device locally?
2) Can we be certain that after a purchase of an auto-renewable subscription product, receipt gets updated automatically? Should I update the receipt with a SKReceiptRefreshRequest() after the purchase has been verified within
paymentQueue(_ queue: SKPaymentQueue, updated Transactions transactions: [SKPaymentTransaction])
Or after a purchase completes can I directly go and read from the receipt the new expiration date without calling SKReceiptRefreshRequest()?
Please note that I am asking about refreshing the receipt from Apple's servers and not validating or reading expiration date from it.
3) If user cancels the subscription or the payment method fails at the renewal date, does Apple automatically updates the apps receipt or should I refresh the receipt with a SKReceiptRefreshRequest() request each time app starts in order to detect subscription cancellations?
4) When an auto-renewable subscription gets renewed successfully, does Apple automatically updates the apps receipt or should I refresh the receipt with a SKReceiptRefreshRequest() request each time app starts to get the new receipt in order to get the new expiration date when an renewal occurs?
5) Does refreshing the receipt with a SKReceiptRefreshRequest() request asks the user for their App Store credentials every time?
As you can see my main confusion is around when it is needed to force a ReceiptRefresh and when can I rely on the SKPaymentQueue or StoreKit to update it automatically.
Any insight would be welcome.

Related

iOS In-app purchase: server side receipt validation for consumable products

I would like to implement In-App purchase of consumable products for an iOS app I'm developing. I want to validate the receipt on the server side as indicated in the documentation:
App start transaction asking to the Apple Store
Apple Store return receipt to the app
The app send the receipt to a trusted server
Trusted server sends receipt (in base 64 format) to the Apple Store through an HTTP POST
Apple Store replies to the HTTP POST with the validation
If the validation response is ok, the purchase can be enabled
I'm concerning about the following situation: a user buys a product and the Apple Store sends back the receipt to the App (steps 1,2). Then, when the app tries to send the receipt to the trusted server (step 3) the connection goes down. In this case receipt validation has to be retried in a second moment, however, I have the following questions:
a) in this case, has the user already paid for the product?
b) Since receipts for consumable products compare only at the time of the purchase, how should the validation retry be managed? Should I save the receipt locally in order to retransmit it in the future?
Can I simply does not mark the transaction as "finished"? From what I understand reading the documentation, in this case StoreKit should call the transaction queue observer again (passing again the receipt?) until the transaction is marked as "finished", is it correct?
Thank you in advance
a) in this case, has the user already paid for the product?
The PaymentTransaction observer will tell you what state the user's purchase is .Purchased, .Purchasing, .Failed . e.t.c.
So we can assume from your question that the user's state is .Purchased and You would have received a payment.
Now since they have a receipt and it’s hopefully from a real purchase they will be waiting for you to validate on your server and unlock the purchase because they have received a receipt containing data about their purchase.
b) Since receipts for consumable products compare only at the time of the purchase, how should the validation retry be managed? Should I save the receipt locally in order to retransmit it in the future? Can I simply does not mark the transaction as "finished"? From what I understand reading the documentation, in this case StoreKit should call the transaction queue observer again (passing again the receipt?) until the transaction is marked as "finished", is it correct?
You don’t need to store it locally, with the information stored about the purchase in the receipt. This is kept until your paymentObserver finishes the transaction or the receipt is refreshed/ updated again.
Your app will keep trying to validate with your server until it receives a response about the receipt usually when the user launches the app again,
At that point if we assume it's valid, you enable your product and then after this finish the transaction on the SKPaymentQueue.
In the documentation it says’s
“The in-app purchase receipt for a consumable product 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 “
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1
Also:
“For example, consider the case of a user buying something in your app right before going into a tunnel. Your app isn’t able to deliver the purchased content because there’s no network connection. The next time your app is launched, StoreKit calls your transaction queue observer again and delivers the purchased content at that time. Similarly, if your app fails to mark a transaction as finished, StoreKit calls the observer every time your app is launched until the transaction is properly finished.”
I hope this helps and I hope it answered your questions.

Never Get RENEWAL Notification Type on Apple statusUpdateNotification

I created an API to be called by Apple webhook to work with subscription. I've set the API url in Itunes to be called by the webhook.
According to Apple help site https://help.apple.com/app-store-connect/#/dev7e89e149d in regards to the Testing Auto-Renewable Subscription, when testing auto renewable subscriptions in the test environment, the Apple webhook will make a call to the API after 5 minutes if I select 1 month subscription duration.
But the thing is, after making first purchase, apple webhook doesn't follow up call to the API that I created to renew the subs. Even after I waited for more than 5 minutes. So I can't do any processing from the API to renew user subs in the database.
Is there some configuration that I had to make in order to test the auto renew and make apple webhook call the API to renew the sub?
We've found that Apple's subscription update webook calls are rather unreliable.
Based on experience:
Don't count on Apple to notify you of renewals. The notification may arrive arbitrarily late (often leaving a "gap" in time), or not at all. We've resorted to retaining and retrying the original iTunes-provided receipt to revalidate renewals at the end of subscription periods --the expiration dates will change to account for the renewal.
Don't count on Apple to notify you of cancellations, either. Same as above, calculate subscription duration, and retry receipts at the end of a period (the expiration date will NOT be updated if the subscription is cancelled).
Generally, this involves mapping the additional transactions/in_apps that are returned with the retried receipt to the original_transaction_id --and updating the expires_at accordingly on your end.
Hope this helps!
Update: In WWDC2020 Apple announced that they add a new notification type called DID_RENEW which will be sent after any successful auto-renew. More info on that in this video, starting at around 16:00.
Original answer:
I can't find the page you linked to, however this page may shed light on the subject (emphasis by me):
The App Store attempts to charge the user account 24 hours before an auto-renewing subscription expires. If the renewal is successful, there is no server-to server notification because the auto-renewing subscription did not enter into the expired state. However, in the few cases that iTunes is unable to renew the subscription (generally there was a connection problem with the credit card server) and the auto-renewing subscription is not renewed before the expiration_date passes, the auto-renewing subscription is technically considered expired. iTunes may continue to attempt to renew the subscription. If iTunes is successful, then the RENEWAL event is sent.
Meaning, you're not supposed to receive notifications for renewals, but only for cases where the subscription became expired and only then renewed. This might be the case for sandbox renewals as well.

Will app receipt reflect active subscription before transaction has been completed?

I'm developing the subscription logic for my application and wondering whether the app receipt will reflect an automatically renewed subscription before the transaction has been received and marked as completed?
Scenario: subscription has expired, but user has auto-renew turned on. He opens my application, which notices subscription has expired. While waiting for the new transaction to arrive, it checks the latest receipt for the current status. Will the latest in-app purchase receipt (latest_receipt_info -> lastObject) be for the previous transaction that just expired, or will it be the new updated transaction even though that has not yet been delivered to my application and marked as completed?
Should I treat an expiration date in the past (without is_in_billing_retry_period being true) as an expired subscription, or should I wait for any new transactions to tick in?
After some testing I have determined that the latest receipt indeed does contain the most recent expiration date, despite this not being delivered to the device and marked as completed.
There was however a small overlap just after the subscription had expired where the receipt was not yet updated with the latest transaction, and as such claimed the subscription had expired despite it automatically renewing. Giving it a few minutes however seemed to do the trick, as it now claimed the subscription was still valid despite the transaction itself not being received by the application.
If anyone can confirm this or provide additional insight, I'd be very appreciative.

How to restore and verify auto-renewable subscriptions?

First off there seem to be many questions on SO regarding this topic but most seem outdated and not considering APIs that are available in iOS 9.
1) How are purchases restored?
When a user taps the Restore Purchase button for auto-renewable subscriptions on >=iOS 9, should a SKReceiptRefreshRequest or SKPaymentQueue.defaultQueue().restoreCompletedTransactions() be called? Currently I verify the receipt file to determine the active subscription and parse for the latest_receipt_info, is a receipt file also created on the device when restoreCompletedTransactions is called?
2) How can an auto-renewable subscription be verified?
To verify that an auto-renewable subscription on >= iOS 9 is still active and not cancelled I would call SKReceiptRefreshRequest every time the user launches the app. But the docs say:
Include some mechanism in your app to let the user restore their
purchases, such as a Restore Purchases button. Restoring purchases
prompts for the user’s App Store credentials, which interrupts the
flow of your app: because of this, don’t automatically restore
purchases, especially not every time your app is launched.
Should I call it only once a day or is there another way to verify the subscription?
Update:
For 2) just found this here:
Perform receipt validation immediately after your app is launched,
before displaying any user interface or spawning any child processes.
Implement this check in the main function, before the
NSApplicationMain function is called. For additional security, you may
repeat this check periodically while your application is running.
Apparently the receipt can be validated without user interaction. It is even recommended to do it on one's own server for security purpose. So as long as the receipt is validated by the App Store server there is no need for a receipt refresh request.

How to use a single appStoreReceiptURL to verify multiple StoreKit transactions?

We are using server side validation to verify our iTunes in app purchases. When a transaction is sent to the SKPaymentTransactionObserver, we grab the receipt from appStoreReceiptURL in order to validate it.
if let receiptPath = NSBundle.mainBundle().appStoreReceiptURL?.path where
NSFileManager.defaultManager().fileExistsAtPath(receiptPath),
let receiptData = NSData(contentsOfURL:NSBundle.mainBundle().appStoreReceiptURL!) {
return receiptData
}
But in some cases, such as when we are restoring purchases, receive multiple transactions to the method:
public func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
It seems rather odd to use the same receipt data to verify each transaction. Does the single receipt contain data about each transaction?
Does the single receipt contain data about each transaction?
Yes. The receipt accessed from appStoreReceiptURL is a single receipt for all persistable transactions for the user and app.
Docs on In-app purchase receipt
The in-app purchase receipt for a consumable product 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.
The in-app purchase receipt for a non-consumable product, auto-renewable subscription, non-renewing subscription, or free subscription remains in the receipt indefinitely.
I.e. all in-app purchases including subscription renewals and restored purchases and excluding consumed purchases create a new transaction and are stored in the receipt.
Docs on Working with Subscriptions:
After a subscription is successfully renewed, Store Kit adds a transaction for the renewal to the transaction queue. Your app checks the transaction queue on launch and handles the renewal the same way as any other transaction.
Docs on Restoring Purchased Products:
Restoring completed transactions creates a new transaction for every completed transaction the user made.
Note that the original transaction id will be the same for restored purchases and subscription renewals.
The receipt is essentially a file stored on the device that the app updates when ever a new transaction is added.

Resources