Google Play: How to verify one-time purchase of app itself (not in-app product or subscription)?

120 Views Asked by At

I have an app that we have sold for many years as a one-time purchase. There are no in-app products of any kind.

We are working on an update that will offer subscription products. I would like to offer users who previously purchased the app outright an incentive to switch to our subscription plan. In order to do this, I'd like to allow the user to send their purchase token to our server for us to verify.

I can see where Google exposes an API method to verify subscriptions:

https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptions/get

...and one to verify in-app products:

https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products/get

But I'm not seeing anywhere where it's possible to verify that the actual app itself has been purchased.

Any pointers would be greatly appreciated!

2

There are 2 best solutions below

8
VonC On BEST ANSWER

I'm not seeing anywhere where it's possible to verify that the actual app itself has been purchased.

Probably because the verification of app purchases, particularly for apps sold as a one-time purchase without in-app items or subscriptions, would involve a different approach.

Google's Licensing API is designed to help you enforce licensing policies for apps that are published on Google Play. While the Licensing API is primarily aimed at preventing unauthorized use, it can also be utilized to check if the app has been purchased by the user. You would need to integrate the Licensing API into your app, and set up your server to validate the license status.

Since you want to introduce a subscription model, consider converting your app to a free download with an IAP option for the full features.
For existing users who have already purchased the app, you can grant them the full features via an in-app purchase entitlement. This migration would allow you to use the existing in-app purchase verification APIs for both old and new users.

To identify previous purchasers:

  • implement a one-time initialization process in your app update to check if the full version was already purchased. This can be done using DataStore, which replaces SharedPreferences.
  • for these users, automatically grant the equivalent in-app product entitlement.
  • going forward, use the standard in-app purchase verification process for both old and new users. On app launch, check the license status.
 App (Client)             Your Server              Google Play Server
      |                         |                            |
      |---- Request Token ----->|                            |
      |                         |---- Verify Token --------->|
      |                         |                            |
      |                         |<--- Verification Result ---|
      |<--- Entitlement Info ---|                            |

You say that I can use DataStore to check whether the app was already purchased, but DataStore seems to simply be a general-purpose key-value store. Does it come pre-populated with purchase information, or were you simply suggesting I store state here?

The latter: neither SharedPreferences nor DataStore come pre-populated with purchase information or have a built-in way to directly check for app purchases. They are general-purpose storage solutions meant to save app data across sessions.

The idea remains to use them as a mechanism to track the app's state or specific conditions set by your logic, especially after manually handling the migration process for existing users to the new subscription model.

Meaning:

  • When you update your app to include subscriptions, you also introduce a one-time initialization check for users who update the app.
  • For users who have previously purchased your app (before it was updated to include subscriptions or in-app purchases), you would manually set a flag or entitlement in SharedPreferences or DataStore during this one-time initialization. That flag indicates that they are entitled to the full features of the app without needing a subscription or additional purchase.

Upon app launch (or at the relevant point in your app), check the SharedPreferences or DataStore for the presence of this entitlement flag. If the flag is set, treat the user as having full access to the app's features. That is a manual process you implement as part of transitioning your app's monetization model.

That does not involve verifying the app purchase directly at the time of the check. Instead, it relies on you having correctly identified and flagged users as having previously purchased the app at the time of the app update. You might have to securely verify the user's purchase status before the app update, and then securely set the flag during the app's first launch post-update.


Up until now, I have used LVL merely to verify that the app was properly purchased. Can I verify that it was purchased for a price greater than 0, or before a given date?

The LVL can indeed be used to check if the app has been legitimately purchased from Google Play. That verification includes sending the license check response from Google Play to your server for further verification if needed. However, LVL does not directly provide information about the purchase price or the purchase date.

Directly verifying that the app was purchased for a price greater than $0, or before a specific date, using LVL or Google Play's API, is not straightforward because these APIs are not designed to return historical purchase data or pricing information.

If you wish to verify purchase details such as the price paid or the purchase date, you might need to maintain records of the original purchase transactions on your server. That could involve storing transaction details at the time of purchase, which can then be queried during the migration process.


One case this doesn't address is that of a user who has deleted the app after purchasing, but then reinstalls the app after our conversion to a free app that offers subscriptions.
How can I know such a user previously purchased?

True: the local storage solutions like DataStore or SharedPreferences will not retain information once the app is uninstalled. That necessitates a solution that can persist outside of the local device storage.

You would need a server-side verification with user accounts: implement user accounts in your app where users can register and login. When a user purchases the app (while it is still a one-time purchase), record this transaction against their user account on your server.
Upon reinstalling the app, regardless of the device, the user can log in to their account, allowing your server to verify their purchase history and restore the appropriate access rights. That would require a reliable backend system to manage user accounts and transaction records but offers a seamless experience for users across devices and reinstallation scenarios.

Another option would be to use Google Play's capabilities to query a user's purchase history for your app. That is more complex than working with the LVL, but can be achieved by integrating Google Play's billing library and querying for past purchases when the app is reinstalled. You would need to implement logic in your app to query for these transactions and, based on the response, restore access to the full features of the app.

You might consider a combination of the above strategies for a robust solution: use server-side verification with user accounts as the primary method for maintaining purchase records and restoring access. Supplement this with Google Play receipts and order history checks for users who might not have created an account but can prove purchase via Google Play.


But, as Brian Rak mentions in the comments:

I have to be able to verify the prior purchase of a user who is reinstalling the app.

I considered an update to the paid app that encouraged users to register their purchases, and that could be a partial solution.
But it still means that a customer who misses that window is unsupported.
I have customers stretching back 15 years, and I'm trying to avoid a massive user support issue, because I would have no good way to verify their claims

And it seems that there is indeed no supported way to verify that a given user installing the (now free) app previously paid for it.

1
Martin Zeitler On

The documentation explains it:

Generally speaking, you'd need an RTDN subscriber, which can be eg. a Firebase Cloud Function, even in PHP, which is able to subscribe to GCP Pub/Sub - the purchase tokens received have to be stored and have to be used to verify these purchases on the own backend. The request would be package-name and purchase token, the response would be yes or no.