iOS Subscription latest_receipt_info - ios

I'm building a react native app that has a 1 month subscription component. I have integrated into the apple API to get the receipts but I have one question I can't seem to figure out in test mode. Probably a newbie question but here we go...
When a user has an auto-renewing subscription and I call the Apple API there is the latest_receipt_info.
Will I get a new one every month? if so when does it come through?
So will all I have to do is call the Apple API, grab the latest transaction from latest_receipt_info and look at the "expires_date" field. If we are still before this date then I assume the subscription is still active? Can it be this simple to see if a subscription is active or not?
I've seen talks online about a "cancellation_date_ms" field but I can't seem to find it in sandbox mode, but why would this be needed if I just use the logic I stated above?

You can get an updated receipt any time by calling Apple with receipt data from the application, even if you send older data Apple always sends you the latest receipt.
You can check for the expiration date within, however it is the case if a user gets a refund the expiration date may still be in the future - in that case you'd be sent the cancellation_date_ms as well, so you should look out for that.
To get a better understanding of what all can be in latest_receipt_info, read through the entries in here, search for responseBody.latest_receipt_info:
https://www.namiml.com/blog/app-store-verify-receipt-definitive-guide

Related

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.

In-App purchase - Receipt validation - Expires date wrong

Goodmorning everyone ! I have a little question that I would like to ask you without going to waste an incident with Apple ...
In practice I am validating the receipt of payment for an in-app purchase, specifically a one-month subscription that unlocks some features of my app.
I parse the json correctly and apparently it seems to work fine but this does not always happen! In fact it often happens that the expiration date that I go to read from the json is not correct as if the subscription was not included in the receipt and I fished only those of the day before or in any case the previous ones.
This happens in sandbox and having checked the code and reviewed well, the parse of the document I could not find a solution because often the validation of the subscription is successful. I also searched online to see if it was a common problem but I did not find a solution or a discussion. My subscription does not include trial periods.
In the specific case this morning the first test was successful, but the validation was not valid. Checking the date shown was that of the previous day and then a previous subscription made yesterday. Once the subscription to the second test expired, the subscription was successful and also validated.

Best strategy to Get In-App Purchase Auto-Renewable Subscription end date

I have been reading a lot about auto-renewable subscriptions but so far I could not get a satisfactory answer to my questions.
Imagine a user subscribed to my app for 1 month and the very first view of the app has some content that only visible with subscription. Now imagine it's the first day of second's month subscription (after auto-renewed). If I don't query the expiry date from apple server, user can't see subscription items, because the expiry date of first month already passed and we need to get the new expiry date.
My questions are:
1- Is there a way to get the end date of the subscription offline and without disturbing the user (with password prompt etc.)? How user will get subscription content if it is the first day of auto-renewed subscription (second month and so on) and if user don't have internet? Will he see locked content if he has no internet? If so this is sounds weird to me.
2- If it is not possible to get expiry date of auto-renewed subscription offline, what is the best approach to get expiry date of auto-renewed subscription without asking or prompting something to user? Because we want to show the very first page of the app with subscription content smoothly. What comes to my mind is to make user wait during launching and check expiry date from server.
I am sure many developers are still concerned with those issues.
It's been a long time since the last time that I integrated an auto renewable subscription and it harder than I expected, but I can give you some general hints and some more specific for your question.
Starting from your question.
Not exactly, you can get the last updated receipt by asking getting the receipt from the receiptURL, here is where I've found a really gray area. I never had a chance to understand exactly when a receipt is updated automatically, probably this happen right after Apple charged you (of course this update needs a connection). This doesn't seems to happen when you have restored a purchase on another device.
Yes there are, when you have a chance to validate the receipt you can store the expire date somewhere (maybe in a secure way) and only before one or two days after that day try to validate it again. If you want you can apply also a grace period after.
Other tips:
you MUST create a way to restore the purchase or your app will be rejected
on sandbox at the first launch the receipt will not exist, but in production an app will always have a receipt, maybe without subscription info, but there is. Thus if you want to simulate some use case in sandbox environment is better if you restore the purchase right after the launch of the application.
restore purchase always requires username and pwd and refresh receipt seems too
Maybe some of this info are a little bit outdated, I made the integration with iOS7 and never had a chance to update it.

iOS In App Purchase with Rails Backend

I'm having some confusions when creating in app purchase strategy for my iOS app. Some background info below;
I have an iOS app and a web app which both will work in subscription model. The backend has been developed in Ruby on Rails.
Currently I'm trying to develop the iOS auto-renewal in app purchase. I came across couple of gems which helps to verify in app purchase receipts (e.g Venice). I get that the whole point of verifying the receipts from the backend rather then the client is to make it secure and to be able to keep a copy of the receipt in your own server.
I can foresee a huge problem as explained below;
A user can create an account using the iOS app and subscribe to the service by paying X dollars monthly using x#x.com apple id. By doing this I will have the record of this user in my backend including the expiry date of his subscription which will enable for me to track if he renewed his subscription or not. The problem arises when this user logs out from the app and creates another user. Since his apple id is still x#x.com and as account email address or id doesn't matter at all when making a purchase from apple, the expiry date will still be a month ahead since he just subscribed with his previous account the new user will be identified as an already paid customer. Boom! now his friend can login with this account using the web app and enjoy it without paying a quid.
If this makes sense for you, there must be a solution for this hack. I know that Netflix uses Apple's IAP, Spotify used for a very long time.
Also gems like Venice, they don't put a thorough documentation on their github page therefore I don't know if this problem is solved by them out of the box. Just wanted to check with you guys and I'm sure many of you thought about this.
If you're attributing the auto-renewing purchase to a user in your database you will also want to attribute the original_transaction_id that is found in the receipt. Store this ID with the expires_date and ignore or error any receipts containing the same original_transaction_id the API receives that are already attributed to a different user in the database.
The original_transaction_id will never change for the iTunes user, even if they unsubscribe and re-subscribe at a later date it will remain the same. It will also remain the same if they upgrade/downgrade the duration of the subscription when the product IDs are of the same subscription family.
If you're looking for an alternative to Venice take a look at the itunes_receipt_validator gem (full disclosure: I am the author). It validates the signatures and decodes of both the deprecated transaction receipts and the newer grand unified receipts locally without connecting to Apple's API, but also provides the functionality to retrieve the updated receipt and receipt info from the Apple API.

Subscription renewal not showing in latest_receipt_info

Our backend does server side receipt verification. In order to check the last expiration date for a renewable subscription, we look at the information in latest_receipt_info. This approach works most of the times.
However, we have some cases of users that, even with active subscriptions, latest_receipt_info only contains the initial subscription, which has already expired. I wonder if someone has already experienced this issue of having missing transactions. It looks extremely weird to me, because Apple is not returning the right information. These users were charged properly, but the Apple response does not reflect that. Am I missing something? Documentation is far from being great... Thanks!

Resources