Search code examples
typescriptkotlingoogle-cloud-functionsgoogle-play-developer-apiin-app-subscription

How to check if Google Play Subscription is active on client-side or server-side


Basically what I want is to be able to check the linked subscription in the user account (firebase auth with firestore) is active even if the user signed in using an iPhone or a Web application using play developer API with firebase functions (server-side) or specific platform code (client-side) such as android kotlin.

More info

I'm using saving user subscription on firestore in the user account (Firebase auth) using Pub/Sub using cloud functions and I'm saving the same subscription data in local DB.

What I tried

To check the linked subscription in the user account is active, I'm checking that expiryTimeMillis is after the current date or autoRenewing is enabled:

suspend fun isPremiumActive(userId: String): Boolean {
  val userSubscriptionData = localDB.getUserSubscription(userId)
        if (userSubscriptionData == null)
            return false
        else
            return Date(userSubscriptionData.expiryTimeMillis).after(Date()) ||
                    userSubscriptionData.autoRenewing
    }

but using java Date has a lot of downsides since it's affected by device date.

I thought about using the server timestamp but that would be hard since user subscription is subjected to timezone (as far as I know).

So is there any other way to check if the subscription is currently active? Am I missing out on something?

Some use-case

User may sign in (in the app using firebase auth) and that account has a linked subscription so I'm expected to serve premium features if the linked subscription is valid even if the device doesn't have the same account to pay a subscription or even the user is using an iPhone, so that's why I can't call:

billingClient.queryPurchases()

Update

Does this typescript code considered safe to check subscription is active?

const serverTime = admin.firestore.Timestamp.now().toDate()
const subscriptionTime = new Date(subscripton.exipryTimeInMilis)
if (subscripton.autoRenewing || subscriptionTime > serverTime)
    console.log(`subscription is active`);

Solution

  • You're very much on the right track, but you're off on one subtle point:

    I thought about using the server timestamp but that would be hard since user subscription is subjected to timezone (as far as I know).

    This isn't quite correct. The expiryTimeMillis is defined in milliseconds since the Epoch (also known as Unix timestamps), which is independent of time zones. As the linked article notes, the Unix epoch is 00:00:00 UTC on 1 January 1970. That same time is 4:00:00 PM December 31, 1960 Pacific Standard Time, but both of those times in their respective time zones have the same timestamp: 0.

    Your intuition, therefore, is correct: You should check this on the server side, using the server's timestamp. Any attempt to check on the device will be unreliable for exactly the reasons you describe: it's subject to the device's time and whatever shenanigans the user might engage in with their device's time, whether it's traveling forward in time to get more gemstones in their favorite game, or traveling backwards to try to extend an expired subscription (maybe yours!).

    This will also solve the problem of checking if the device isn't signed into the Google account with the subscription, or using an iPhone, because you can associate the account in your system with the subscription in your backend.