iOS Handling local SKPaymentTransaction when using backend server - ios

I'm creating an iOS app with an auto-renewal subscription. I've been reading a lot of tutorials and documentation but I'm a bit confused about how to handle certain scenarios.
Here's how my app works:
User installs app
User creates account within signup flow
User is asked to select a plan and pay within signup flow
The payment receipt is uploaded to my server and I activate their account in my database.
My server polls the /verifyReceipt endpoint on regular basis to renew the user's account or deactivate it depending on what the latest info from apple. (or use Apple's new Status Update Notification, both serve the same purpose to get me the latest subscription info on my server)
After a month when the subscription renews I know a transaction will appear on the SKPaymentQueue on the user's device. Because of this a lot of tutorials/documentation recommend having your AppDelegate implement the SKPaymentTransactionObserver protocol so that you can handle a transaction at any time.
But, I didn't use AppDelegate. I used the view controller in signup where the user picks their plans to implement SKPaymentTransactionObserver.
My reasoning is that since I'm getting info on the backend do I need to care about the transactions that will show up in the queue in the client each month when the subscription renews? Can't I just ignore these transactions, or will I need to call queue.finishTransaction on them?
I also read some things about restoring transactions when the user deletes the app and re-installs or gets a new phone. Again, do I need to worry about this? Because I should still know about the subscription on the backend and all the user has to do when they get a new phone is log in to their account for my service and it'll check the backend to see if their subscription is active.
I guess my larger question is: When you have a backend to handle IAP auto-renewal subscriptions, can you ignore some of the stuff happening on the client with the payment queue because that feature was built for apps that don't have a backend.

It's best practice to implement the observer immediately in the AppDelegate in case something goes wrong between the user being charged by Apple and you upgrading their account - if they close the app or it crashes you may lose that transaction.
Also, I think I've had cases where I forgot to call finishTransaction and that annoying iTunes login prompt kept popping up, not sure if that was a Sandbox only event though.
Like #Paulw11 said. Don't rely on the Status Notifications. At time of writing, they don't provide enough info to update a user's status, namely any sort of user identifier. Refreshing receipts from the backend is the way to go. If a new receipt is posted to the SKPaymentQueue (say on a renewal), you can handle it like any other receipt refresh for the user on your server.
Here's a good blog post that provides more detail on what should be happening exactly on the server: iOS Subscriptions are Hard
For your restore logic, you don't need to use the StoreKit restore method if you've implemented your own restore functionality through an account based system. If that's the route you want to go, you should definitely listen to SKPaymentQueue in the AppDelegate to avoid as many edge cases as possible where you could lose track of someone's subscription status. The good 'ol "Restore Purchases" button is a great way to fix some slightly flawed in-app purchase code :)
I guess my larger question is: When you have a backend to handle IAP
auto-renewal subscriptions, can you ignore some of the stuff happening
on the client with the payment queue because that feature was built
for apps that don't have a backend.
Don't ignore the payment queue. You can ignore the "Restore Transactions" if you have your own account based restore system.

Related

IOS auto-renew subscriptions notification handling

Good day! I'm trying to implement notifications for auto-renew subscriptions and some parts of the whole system are not clear for me. Could you please help?
First of all, when user creates a subscription I get a receipt from user's device, verify it and create a record in database with all needed information. Now, the notifications are coming to server.
INITIAL_BUY notification - When does it come and why? I just got a receipt from device, verified it and I'm happy. Why do I need it?
Correct me if I'm wrong, this part is unclear for me, When user UPGRADES subscription level I get CANCEL notification for oldSubscription and a receipt with a new purchase for newSubscription, right?
So theoretical question is - if on my server side content available in oldSubscription and content available in newSubscription are different entities than when I get CANCEL for oldSubscription I just deny user's access to oldSubscription content and when I get receipt from client I create newSubscription with access to new content, right?
DOWNGRADE and CROSSGRADE. As far as I see I get DID_CHANGE_RENEWAL_PREF or DID_CHANGE_RENEWAL_STATUS. So I will get auto_renew_status in notification for this subscription and when payed period will expire I will get a receipt from user device with info about new purchase? Or I have to figure out how to handle DOWNGRADE and CROSSGRADE only by DID_CHANGE_RENEWAL_PREF and DID_CHANGE_RENEWAL_STATUS notifications?
Thank you
The problem with server receipt validation is that there is not only one way. There are endless possibility to implement this and it depends on your use case of the usage of subscriptions and how you want the communication between your app and server to happen.
In your second paragraph is the first example of those possibilities: your server will get the receipt from the device only if you implemented it that way. Also, maybe you update your database solely based on the notifications instead of the send receipt. You see, it is all up to you ;-)
Regarding you questions:
1)
Occurs at the user’s initial purchase of the subscription.
See documentation
Theoretically this is correct, but your described use case isn't an upgrade. Would be odd, if the better subscription wouldn't contain the content of the lower one.
There isn't much to handle with down- or crossgrade. The notifications only tell you that there will be a change with the next auto-renewal. Because, the user is still eligible to access the better content until the next auto-renewal. After the better subscription expired you will receive a normal DID_RENEW with the down- or crsssgrade product id.

How to find user's lost in-app purchases on iOS?

So I followed the official manual and implemented this:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
user pays in app
app gets receipt info from Apple
this receipt info is sent to my server
my server verified receipt by calling Apple API and activates membership for client
The obvious problem is that 3. can fail. I have clients complaining they paid, they are sending me SS of the amount being deducted, but my server was never notified. And I have no way to find these users. Is there some CP where I can search by customer e-mail or transaction ID to check if this is Photoshopped screenshot or valid one?
Is there some API that can be called to list transactions by product and e-mail of client?
https://appstoreconnect.apple.com/ - Apple CP is useless for this
There is no method to correlate the customers details with your transaction details. Only Apple can do this.
My first suspicion is that you may have a logic problem in your purchasing process. If implemented correctly, a transient failure at step 3 doesn't matter.
You should:
Create your transaction queue observer a soon as your app starts. This will enable any pending transactions to be delivered to your observer
When you get a purchase transaction in your observer you verify with your server
Only once you have a response from your server that the purchase has been recorded successfully do you call finishTransaction.
This way if something goes wrong with your server or the app crashes the transaction is still pending in the queue.
If you are using auto-renewing and/or non-consumable IAP then I
strongly suggest you provide a "restore purchases" button in your
UI. This makes it simple for the user if something goes wrong or when
they move to a new device.
If you have users who claim that they did not get what they paid for then you can refer them to Apple App Store support who can refund the transaction.
If step three fails you can fall back to local verification and then let the user through for this session (or some number of sessions before you require it to succeed). Unfortunately local authentication is a pain in the ass because the receipt is encrypted. See this link for an example: https://github.com/andrewcbancroft/SwiftyLocalReceiptValidator . You can also report failures of step 3 to your analytics tool so you can see who is actually affected by this issue (obviously this only works if the analytic eventually get an internet connection.

How to monitor subscription status upon app launch (swift)

I'm building a simple dating app that allows a user to become premium with an auto-renewing subscription. The user's value for their "isPremium" attribute is false upon creating an account, and becomes true once they purchase a subscription via an in-app purchase. But I have to implement one more thing to finish this premium feature, and this is where I run into problems!
How do I monitor the app's current user's subscription status when the app launches? I want to keep their "isPremium" attribute true if their subscription is still ongoing, but immediately change it to false if it has expired.
I'm aware this has probably already been asked on here, but responses are mixed and mostly in Obj-C (I'm building this app in Swift). Would the best move be a backend server in Python to validate receipts? Or is there a simpler, more direct way of getting an expiration date from a subscription product object or a Cocoapod that handles this backend work? Please help! Thanks
The best move is a backend server like you mentioned. This way you store the receipt file there and periodically refresh it with Apple, so you always know the subscription status for any user.
On the device, you request the subscription status from your server and cache it (~5min). Every time the user resumes or opens your app check if the cache is stale and refresh it. This way you can prevent expired users from retaining access for more than 5mins yet keep it fast and snappy on app launch.
Or is there a simpler, more direct way of getting an expiration date
from a subscription product object or a Cocoapod that handles this
backend work? Please help!
If you're looking for an out of the box solution, RevenueCat is a solid option and handles all of the backend work. They have a free tier as well. https://github.com/revenuecat/purchases-ios

Linking iOS In-App Subscription to MySQL database

I have followed some tutorials on implementing In-App purchases and subscriptions for iOS (especially Ray Wenderlich) but I have doubts on how to integrate our iOS app with our website so that if someone subscribes to the app they can access the content on the web as well.
Our website and app offer access to videos. All subscribers have access to all videos. Our web works with PHP and MySQL. When someone subscribes on the web, a record for their account is created in our MySQL database and they are flagged as subscribers so that they can access the content.
We want to implement in-app subscription in our app, but we need for a record to be created in our database when the subscription takes place.
The question is, should we do this upon receipt validation (we'll be doing this in PHP)? Also, is there a way to know when a user unsubscribes through iOS so that the database can be updated accordingly?
Our closest example of how we would like it to work is Gaia.
Should we do this upon receipt validation?
Yes, you should wait until you validate the receipt to mark the subscription as active for the user.
Is there a way to know when a user unsubscribes through iOS so that
the database can be updated accordingly?
The correct way to implement this is to store the entire IAP receipt on your server and periodically refresh it with Apple to get the current subscription status. Just because somebody started a subscription doesn't mean it will still be active the next time you check it (e.g. they may have turned off auto-renew or been issued a refund).
This blog posts explains some of the nuances in further detail: iOS Subscriptions Are Hard
Is there a way to know when a user unsubscribes through iOS so that
the database can be updated accordingly?
Use status update notification.
A statusUpdateNotification is a server-to-server notification service
for auto-renewable subscriptions. A notification specifies the status
of a subscription at the time the notification is sent.
As status update notification is not a reliable service, Apple recommend to use this in combination of other method such as polling the verifyreceipt end point. creating a scheduler in server will be best option along with notification.

Auto-renewable subcriptions: handle purchase when user is logged out

I've read a lot of SO answers about handling auto-renewable subscriptions with your own user login system, but there is an issue that I am still not sure how to handle: what to do when the transaction observer is triggered while the user is logged out.
Apple recommends to implement a transaction observer right in the App Delegate:
Adding your app's observer at launch ensures that it will persist
during all launches of your app, thus allowing your app to receive all
the payment queue notifications.
Say the user starts a purchase, but it doesn't complete immediately (for instance because it needs to be approved by their parents, or the app crashes, etc.). The user logs out and opens the app again, and since we are observing transactions in App Delegate, we might receive a transaction immediately when launching the app, before he logs in. We are then unaware of which user to associate the subscription to.
Two ideas:
Non-ideal: should I store the fact that there is a subscription pending and assume that the first user to log in will be the right one, and then associate it in some way to them once they log in? And if so, where would I keep the receipt? KeyChain, UserDefaults? This sounds pretty clunky.
Another way that sounds better: can I store some information about the transaction when it is initiated, and then use one of these fields to actually know exactly whose user's subscription purchase just finished?
(Not particularly relevant, but FYI I am using SwiftyStoreKit).
This is how I would approach this:
When the user tries to purchase a subscription, I first have them login or create an account.
Once the user is logged in, I send their appStoreReceipt to my server and store it there. I check and make sure the user is able to purchase. (At this point they should have the subscription they are trying to purchase)
Once I get a response from the server that the user should be able to purchase I go ahead and start the in-app-purchase
When the in-app-purchase process is done, I send the updated appStoreReceipt to my server and upgrade their account.
The problem you are worried about is what if the user gets done with step 3 but never gets to step 4.
Well, since I have their receipt stored on my server (from step 2), I can just ask Apple to give the latest version of their receipt, and if it shows up that they did purchase, I upgrade their account. You can choose when the right time is to do this check, it can be every time the app launched, or every time the user logs.
Hope this helps.

Resources