Implementing iOS App Store Subscriptions without a backend? - ios

I'm implementing subscriptions in a React Native app right now and while I was building a function to check if the subscription was still active (I would call getPurchaseHistory to get the past transactions, then use Apple's verifyReceipt endpoint to decrypt the transactionReceipt to compare the current date with its expiry date) but I read not to do that on app's launch.
Then I discovered that the "go-to" way of handling receipts was to have a backend to store them. I thought I'd be able to implement it all without any backend because I was trying to save time but also avoid any backend downtime or latency.
Basically I'm wondering, is it possible to do it all without any backend at all?
I thought of storing the transaction receipt on the device. And use that to check if the subscription is still active. But what if they cancel the subscription?
Any suggestions? I can't believe that's impossible. There's got to be a way I'm not thinking of.

Related

Can we store iOS in app purchase receipt to our server

I am developing an in app purchase app (auto renewal) and purchase/cancellation should effect on all platform (Android, iOS, Web).
My question is what is best way to keep track the latest status of the purchase. I know there is a way called server to server notification using web hook, but I am thinking can we store the receipt data to server and validate this receipt time to time with iTunes apis?
Does receipt data change over the updates on subscription or it is same even after changing the device?
All I want to validate it at server side, because there is a possibility that user can uninstall the app or not using it.
You can validate the receipts at server end. There are two options.
Enabling server to server notifications.
Store all receipts in your databases and verify it with server for latest update.
You need to use meta data from the receipt under key "latest_receipt" to get the latest update in the subscription.
Below is the link for reference.
https://developer.apple.com/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store#//apple_ref/doc/uid/TP40010573-CH104-SW1
Initial receipt meta data does not change over the time. You can save initial receipt metadata in your database and use it for further updates like - renewal, cancellation, upgrade or downgrade, refund etc.
Yes, you can, moreover server-to-server validation & observing is most preferable way recommended by Apple. They provided JSON API and accepts your server end-point to post any changes directly to server.
For details read Choosing a Receipt Validation Technique and related in topic.
The common start point is In-App Purchase

Server-side validation of iOS and Android auto-renewable subscriptions using server-to-server notifications and Firebase Cloud Functions

I have successfully implemented subscription validation for Google Play, but I am struggling to understand the validation flow for iOS auto-renewable subscriptions and would like to ask for your help. Here is how high-level logic for Google Play:
New subscription validation
User purchases subscription in the app.
A SubscriptionRequest is created in my Firestore database, which includes the UserID and the token of the transaction.
A cloud function picks up that SubscriptionRequest and queries the relevant Google API using the token to get the subscription details. This is done using the googleapis Node.js libary.
The latest subscription details are saved in Firestore as a Subscription, including the token (as linkedPurchaseToken) and the UserID.
The expiry date of the subscription is evaluated and, if it is not expired, the User in Firebase is updated and the flag hasActiveSubscription is set accordingly (including Google Play identifier of the subscription, e.g. monthly_sub, or annual_sub, and the platform, in this case android).
Google Play Developer notifications
Notification is received via a Pub/Sub cloud function.
The corresponding subscription details will be fetched using the relevant Google API and the token from the notification.
If no Subscription with that token (as linkedPurchaseToken) exists in the database, we'll try to get find the existing Subscription in our database using the linkedPurchaseToken from the subscription details that were fetched in (2).
If still no Subscription can be found in the database, that obviously means that it's a new subscription, which will be handled exclusively via the New subscription validation process described above. The reason for this is that I am otherwise unable to link my UserID and the subscription.
If a Subscription is found, it is updated with the latest details.
The expiry date of the subscription is evaluated and, if it is not expired, the User in Firebase is updated and the flag hasActiveSubscription is set accordingly. [...]
This has been working exceptionally well and robust for quite some time.
As far as I can see the developerPayload, which could be used to pass on, e.g. the UserID, to determine who the subscription belongs to, is deprecated. (Source)
Do you think there is an easier way of doing this, possibly
only using Google Play Developer notifications?
I am receiving a notification at every step a subscription changes and I am simply updating my Subscription and hasActiveSubscription flag based on the expiry date. This is working well because I receive a notification at the moment the subscription expires (notification type SUBSCRIPTION_EXPIRED) and at any point the subscription gets extended, for example. (Source)
Is there anything missing in that validation logic or any potential risk?
These two questions so far are only to ensure I am not missing something essential. Again, from my experience this is working quite well.
All that is left for my app (based on Flutter, by the way) to be released on iOS is to implement the validation logic for iOS.
One thing that has made the google validation logic rather easy, is that there is the googleapis library, which essentially is giving me the model classes for all responses, such as the notifications or the subscription details. I have been unable to find something similar for Apple yet and I am not sure there is.
Is there any (official) library that is providing me with similar features as googleapis for Node.js?
For new subscriptions I am currently querying the verifyReceipt endpoint, which seems to be working well. However, Apple does not seem to say anywhere which fields need to be validated exactly, in order to provide users with access within the app. I am following the same logic, meaning: If I do receive a valid receipt from the endpoint and it is not expired, I grant access.
Is that logic sufficient for new subscriptions or am I missing something?
For Google so far I simply stored the subscription details that I received via querying the api, including the UserID and token. This is done mainly for laziness and because the document structure received is rather simple. The Apple responses are much more complex, so I am quite unsure about what to store (and poorly documented, if you ask me), so I am wondering:
Which details do I actually need, for both Google and Apple, especially if I rely on notifications for updating the subscription?
Regarding updates to the subscriptions, I am wondering how to work with server-to-server notifications from Apple.
When exactly are they being send and can I implement the same logic as described above for Android?
As I can't seem to find a good documentation or tutorial for this part:
Do you possibly know any good tutorials for these notifications?
Thank you very much for your support,
Matthias
It has been a while since i asked these questions and while technically the questions have not been answered, I would still like to share my solution with everyone.
The solution I have gone for is simply implementing RevenueCat, who focus on managing in app subscriptions for you, so that there is no need to worry about all those questions anymore.

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.

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