How to treat the SKPaymentTransactionObserver events in my iOS App if I previously need my user to be logged up and logged in - ios

Some time ago, I developed and published an iOS App in which I offer subscriptions that I validate on server side.
On server side I correctly register the access privileges of a user according to the subscription that server receive from the App (Apple Store). First server validating the receipt from the App with the Apple Store and check everything is correct.
Before presenting the list of subscriptions in the App the user must register and log in and then choose a subscription. The user's email is sent to the server along with their purchase receipt. We need user email to identify the user in our database and assign the subscription.
We now need to add to the App processing OFFER CODES that a user may have redeemed at the Apple Store, outside of the App.
Apple advises to create the SKPaymentTransactionObserver at the beginning of the App startup to receive these possible subscriptions from outside the App.
But if my App needs the user to be registered and logged in, that event could arrive from the payment queue (PaymentQueue) but since the user is not logged in, the purchase cannot be assigned.
I've been searching for a long time and I'm disoriented among so much information.
Could someone who knows something guide me on the right path on how to implement these situations?
Any minimal clue that helps me continue will be very valid, someone who has already faced this, I would greatly appreciate it.

Related

Ways to buy an iOS subscription outside of an app

Im working on an app which will support subscribing to a premium version.
Whenever a user buys a subscription I can verify the receipt on my backend, persist important data and grant entitlement which is good.
However, when a user buys the subscription for the first time I rely on the Firebase User ID in order to query my DB.
When I receive App Store server notifications I query by the original_transaction_id.
Now comes the problem, I do not know whether a subscription can be bought initially outside of my app. (I only sell them in-app, but am not sure if apple provides a way to buy them outside of the app.).
If it is possible it would cause a problem.

iOS Handling local SKPaymentTransaction when using backend server

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.

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.

iOS In-app purchase in multi user app

I just run into a problem with in-app auto-renewable subscription. The app contains this kind of subscription and the app can be used by multiple users but the subscription is bind to the apple id is used on the device. So if a different user log in to the app than the system say he has a valid subscription. If I log the subscribed users on my backend server than if a user without subscription log into the app can not make a new subscription according to the Apple's response because the apple id used on the device. An other problem if a keep track of the subscriptions on my backend server if the user unsubscribe on the apple's webpage I can't notify the server about if.
What do I do wrong? What is the right workflow for this case?
I hope do you understand my dilemma.
Thanks!
Your use case is absolutely valid and that's exactly how the subscriptions In-App purchase work with any platform (Apple/Google).
In case of Subscriptions in-app purchase the content delivery is entirely the responsibility of the app provider and not the platform. You have no direct way of identifying if the app user has been switched to a different user as you can't access the current logged in user on iTunes account on the device.
You need to manage this use case on your own by keeping some data locally on the device and maintain user purchase history but still that wouldn't solve the purpose 100%. When user will go for purchase of the subscription it will show the service as already subscribed unless the iTunes user account is also switched on the device.
You can keep track of the unsubscribed state from the backend as when the subscription is successful you will get a receipt from iTunes which you can use to save in your DB. You need to run a backend job on the server side to validate the saved receipt to check the updated receipt which will give you details if the subscription has been expired or not.

Accessing StoreKit receipts directly from a server

I'm building an iOS app, which provides a service which our website already provides. It's a subscription based service, where all features are enabled when you're subscribed. To get people to sign up for regular payments on the app, it seems like I'll have to go through Apple's in app purchasing API, with auto-renewing subscriptions. That's fine, but the problem is the service users will purchase through the app must also be available on the site. But reading through walkthroughs and the developer guide, it looks like the only way to process transactions is through the app itself, which then can be set up to pass the info to the server. But then I'm imagining this scenario:
User purchases a subscription on the iOS app, goes back and forth between using the site and the iOS app. On the day the user is to be charged, and the days after, they're using just the website, for whatever reason. The server doesn't know they've been billed though, and so features are disabled. In order for the server to find out the user has been billed by Apple, the user has to open the app on their iphone or ipad and sync it with both Apple and my server.
Is there any way for my server to ping Apple directly and get information about the purchases made for my app for a given user? I haven't been able to find anything; the two suggested layouts, with or without server, both use the iOS app as the sole communicator with Apple, aside from having the server verify that information is valid. If it's not possible for the server to get this information first hand, what other possible solution could there be?
Try to not use Apple's built in payment system, and risk getting rejected (with the app duplicating a service that's been available for years online, do we no longer have to use their in app purchasing system anyway?)
If a user is paying through Apple, have the server give them a generous buffer between expected payment dates and when features are disabled (makes the problem happen less frequently but doesn't solve it)
When their account seems expired to the server but it doesn't expect that, email the user a message saying they have to open the app or else their account will not be credited for time purchased (seems like a strange and not really great thing to have to ask a user to do in order to use the service)
This is about all I've been able to think up so far. Anyone have any advice on these solutions, or know of others, or know who I could talk to to try and figure this out?
Yes, you can check on the status of a user's account from your server. There are a few caveats:
First and foremost, Auto-Renewing Subscriptions are reserved for periodicals such as magazines and newspapers. If your app doesn't resemble those, Apple may reject it (as they did mine) and request that you use Non-Renewing Subscriptions (read: Manually-Renewing Subscriptions).
Second: This scenario would require you to store all receipts that you receive on the app, on your server.
Finally: I don't know how your username/password system works, but the user would have to login with the same credentials on your app as they do on your website.
Here's how you check the status of a user's account: Store at least one receipt per user on your server. When you want to check the status, follow Apple's procedure for Verifying App Store Receipts. Send them that one receipt and they'll respond with the latest receipt and the expiration date. Now you'll know, at any given moment, if a person's account is current or not.

Resources