I have a couple questions regarding the process of detecting that a subscription has been auto-renewed.
Currently I have my sandbox test environment all set up, I am able to purchase a 1 month subscription and all of that works nice and dandy.
My questions are as follows:
Do I need to verify the initial receipt (or really any subsequent receipts) of the in app purchases? If so, for what reasons?
Does my app get notified via a paymentQueue:updatedTransactions: call when the auto-renew has happened? Both in the sandbox environment and in the production environment.
So far, I have not seen the StoreKit notify me of a renewed subscription (in the sandbox environment) but I have also read that it is finicky and might not.
Any general tips with Auto-Renewable subscriptions that I should take into account?
Thanks for your help and time!
1. Regarding Receipt Validation
Receipt validation is your means of performing extra security checks before you unlock the users content. Think of a receipt as a lotto ticket. A client comes to you with the "Winning Ticket". Depending on how valuable you consider the reward to your Lottery you can either give said individual their winnings, or you can contact Apple and check whether the user actually purchased this ticket through them.
The client could have got this lotto ticket through some malicious means be it a jail broken device etc and so its really up to you to decide whether you want to verify the receipt or not.
2. Regarding Renewal Notification
The official documentation states the following:
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. Note that if your app is already running
when the subscription renews, the transaction observer is not called;
your app finds out about the renewal the next time it’s launched.
So you should definitely be getting receipt renewal notifications in your app. Don't forget that you should always add yourself as the observer of StoreKit's paymentQueue in order for this to work, something like this in your app delegates didFinishLaunching would work:
//where YourObserver is an object that is alive for the apps lifecycle
[[SKPaymentQueue defaultQueue] addTransactionObserver:YourObserver];
Related
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.
Found this statement in apple documentation:
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.
If I only do this on start:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
Nothing happens - subscription is not renewed (using test mode).
if I add
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
Then it runs over complete history of purchases which is also not what I want.
What is the proper way to handle it?
One thing you need to understand, when testing auto renewable subscription in sandbox environment, sometimes the subscription doesn't get renewed and you don't get renewal calls from the Store.
This is normal in sandbox and calling restoreCompletedTransactions in sandbox environment sometimes it trigger the renewal on the Store. The duration for the subscription itself is different in the sandbox: Here is a table of duration in sandbox:
Just add [[SKPaymentQueue defaultQueue] addTransactionObserver:self] when your app launches and in production it will get renewed and you will get calls.
Apple in-app purchase storkit document:-
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.
Which means,
If user's subscription is auto renewed and at that time our app is not running, Apple added that renewed transaction in transaction queue. So when you will opened app, we can get those renewed transaction details from transaction queue.
There is another statement mentioned in storekit document like:-
Note that if your app is already running when the subscription renews, the transaction observer is not called; your app finds out about the renewal the next time it’s launched.
Solution: How to handle auto-renewal subscription?
According to Apple storekit best practices, we should add below line in AppDelegate's "didFinishLaunchingWithOptions":-
SKPaymentQueue.defaultQueue().addTransactionObserver(SubscriptionObserverClassReference)
& for verifying subscription, Two ways available:
1) Using Server: Set cron job that verify user subscription arounds user's subscription expiration date. So we can identify user subscription renewed or not based on expiration date.
2) Using StoreKit Observer:- Every time when user subscription is renewed, we get renewed transaction information. So based on that we can take action accordingly.
The commentors and documentation were right: it is sufficient to add transaction observer to the PaymentQueue on start. In my case the renewal has not been triggered for the Sandbox User I used the whole time. I created the new one and it worked!
For my case, in sandbox testing for subscription, it will only auto renew the subscription for 5 only on the FIRST subscription purchase. Subsequent subscription purchase will stop renewing after purchase. The only way to test is to recreate a new sandbox user and test again. Rinse and repeat!
I'm trying to work out the best way to get updated receipt expiration when an iOS auto-renewing subscription renews. I know that what's supposed to happen is that it will be delivered via the SKPaymentQueue transaction observers. However,
This only happens when the app launches. My app is vertical market style app that might run for hours or days, so checking at launch isn't enough.
Transaction observer delivery doesn't seem to be reliable. In testing I've had subscriptions that should have renewed but where no new info was delivered to the transaction observer even after relaunching.
But of course it's also possible to get updated info on demand by revalidating the receipt with Apple. Given the above two issues, I'm thinking I'll need to run a timer that just periodically
Checks if the subscription should renew "soon", i.e. within some TBD period in the future (maybe within the next day).
If so, attempts to get updated info from Apple by revalidating the receipt.
How do other people handle this? The timer approach seems ridiculous but I haven't come up with anything better yet.
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.
I have a few questions that will help me understand things better if answered:
Is there a way to differentiate between a fresh subscription and the renewal of a previously-purchased one?
Does the subscription go through the renewal process immediately after the expires_date is hit? It seems that sometimes (in the Sandbox, at least) my subscriptions will renew 30-60 seconds before the expires_date.
Does the renewal always happen at a consistent time after the expires_date is hit? For instance, if I launch my app and expires_date has been passed, when will the renewal occur (assuming the user didn't cancel)? Or rather, when will my app know that the renewal has occurred on Apple's end?
Scenario: app is launched and expires_date has passed for one of my subscriptions. Should I send a receipt to Apple to see if that subscription was renewed, or should I wait a couple seconds to see if the renewal process occurs?
Thanks!
I will attempt to answer my own question:
To do this, I store expires_date of the subscription in NSUserDefaults once the initial purchase is completed. When the subscription expires, I remove the object from NSUserDefaults. This allows me to determine if any subsequent subscriptions are a renewal (expires_date exists in NSUserDefaults - update with new expires_date upon renewal completion) or a fresh purchase (expires_date does not exist, either because it was removed upon a previous subscription expiration or the product has never been purchased).
This question is irrelevant - what I do is compare expires_date stored in NSUserDefaults to current_date every time the app enters the foreground. If current_date has passed expires_date, I call my server to verify the receipt with Apple. Apple returns the status of the subscription to my server (0 means the subscription is valid, 21006 means it has expired, and all others are trivial as far as my app is concerned) and the server forwards it to my app. In this manner, it doesn't matter how long it takes my app to be alerted of a renewal (aka the subscription has already technically renewed through Apple but my app doesn't yet know) because I know for certain whether the subscription has expired or not.
In the sandbox, renewals seem to be very untrustworthy. Sometimes my subscriptions will renew five or six times (the standard, according to Apple), sometimes once, and sometimes not at all. For the record, the 7 day subscriptions don't renew a lot more often than the 30 day subscriptions. It would be nice to have the same reliability in the sandbox as we do in the live servers so I can write code accordingly (and stress less), but I digress. The only sure-fire and consistent way I've discovered to make the subscriptions alert my app that they've renewed is when expires_date has passed && I force close/relaunch my app. Sometimes toggling between background and foreground states works, but in my experience this is much less reliable - I have a feeling that this would work [better] in the live servers though.
What I do is check with my server (which in turn checks with Apple) when I notice that expires_date has passed. So it doesn't matter if the renew occurs before/after/never, because my server tells me for certain the status of the subscription.
I hope this information helps others who have the same questions I did!