Google Play server-side purchase validation

617266fd9ede41249cb25fcd android tutorial 5 receipt validation

Server-side validation is a process of validating purchase authenticity through sending a request to the server. In the case of Android apps, the device makes a request to Google servers to find out whether the purchase has actually taken place and whether it’s valid. This process helps detect fraudulent or invalid purchases and prevents you from getting inaccurate revenue data.

In this guide, we’ll discuss how to configure server-side validation for Android apps.

This is the fifth article in our series about implementing in-app purchases for Android apps. I recommend you look through the others as well:

  1. Android in-app purchases, part 1: configuration and adding to the project
  2. Android in-app purchases, part 2: processing purchases with the Google Play Billing Library.
  3. Android in-app purchases, part 3: retrieving active purchases and subscription change.
  4. Android in-app purchases, part 4: error codes from the Billing Library and testing.
  5. Android in-app purchases, part 5: server-side purchase validation.

Why validate Android in-app purchases

It should be noted that server-side validation isn’t mandatory — in-app purchases will still work without it. There are some significant benefits to it, though:

  1. Advanced payment analytics, which is especially important for subscriptions since everything that happens after the activation isn’t processed by the device. With no server-side purchase processing, you won’t be able to retrieve the current subscription status and know whether the user has renewed the subscription or canceled it, whether there are any payment issues, and so on.
  2. Being able to verify the purchase’s authenticity. You’ll be sure that the transaction isn’t fraudulent, and that the user has actually paid for your product.
  3. Cross-platform subscriptions. If you can check the user’s subscription status in real-time, you can synchronize it with other platforms. For example, the user who purchased the subscription from an iOS device will be able to use it on Android, the Web, and other platforms.
  4. Being able to control content access from the server side, which protects you from users trying to access the data with no subscription by simply executing requests to the server.

Speaking from our experience, the first advantage alone is enough to set up server-side purchase processing.

Looking to fortify your Android in-app purchases and enhance subscription transparency? Discover the difference with Adapty! Schedule a free, no-obligation demo call to explore how our advanced features can help you gain precise control over your content and elevate your subscription revenue, while you focus on delivering exceptional value to your users.

Grow your app’s revenue by 30% See all the possibilities Adapty can bring to your Android app.

Google Play payment validation

We can summarize Android payment validation with this scheme:

616ecfda9345ee7e2b53d608 vkx4uyzcwdhqdwubf7srxr8us7k37emw9fybjgkduzdvuq6whxgsne4zn9bqt2g052sg2va6lgr0zi5wue22vnkls6wsql1mdozqnmrcrlk rb86shh7dag2fzdho gpavemctll3ds1600

Authentication for Google Play Developer API requests

To work with Google Play Developer API, you’ll first need to generate a key to sign requests. First, you’ll have to link your Google Play Console account (where you manage your app) to your Google Cloud account (where you’ll generate a key for request signing). Once everything’s configured, you’ll have to grant the user purchase management rights. It’d take a dedicated article to describe this process. Luckily, we’ve already covered it in a step-by-step guide found in Adapty documentation.

Note that usually you’ll have to wait for 24 hours or more after generating a key for it to start working. To avoid that, just update the description for any in-app product or subscription, which will instantly activate the key.

We use the official google-api-python-client library to work with Google Play Developer API. This library is available for the majority of popular languages, and I recommend using it as it supports all the methods you might need.

Validation for subscription transactions

Unlike iOS server-side validation, in Android, both subscription and other product validation are implemented using a variety of methods. Therefore, when validating a transaction, you need to know whether you’re dealing with a product or a subscription. In practice, this means you’ll need to transfer this data from the mobile app, as well as keep the flag in the database in case token re-validation will be necessary.

The second important difference is that while each transaction has its own token in Android, all iOS transactions use an app-specific shared secret to store the entire transaction history. This means that if you want to be able to restore the user’s purchases at any moment, you’ll need to store all purchase tokens, as opposed to picking out an arbitrary single one.

To validate the subscription, you’ll need to invoke the purchases.subscriptions.get method. Basically, it’s a GET request call:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications//purchases/subscriptions//tokens/ 

All parameters are required:

First, let’s look at the error messages you need to take care of to make sure everything works as intended:

Subscription transaction

If the validation was successful, you’ll receive transaction data as your response.

Transaction data (for a subscription):

 "expiryTimeMillis": "1631116261362", "paymentState": 1, "acknowledgementState": 1, "kind": "androidpublisher#subscriptionPurchase", "orderId": "GPA.3382-9215-9042-70164", "startTimeMillis": "1630504367892", "autoRenewing": true, "priceCurrencyCode": "USD", "priceAmountMicros": "1990000", "countryCode": "US", "developerPayload": "" >

To understand whether the user can access the premium options offered by the app — that is, whether they have an active subscription — you need to:

  1. Check the startTimeMillis and expiryTimeMillis parameters. The current time should be between these.
  2. What’s more, you have to make sure the paymentState parameter doesn’t have the ‘0’ value. This would mean the subscription purchase’s still pending, therefore, there’s no need to grant the user premium function access just yet.
  3. If the transaction has the autoResumeTimeMillis property, then the subscription is paused. This means that the user shouldn’t be granted premium function access before the specified date.

Let’s see the key properties of a subscription transaction:

Subscription acknowledgement

As it was already mentioned above, in case the subscription isn’t acknowledged within 3 days after the purchase is made, it’ll get canceled and refunded automatically. To be honest, I don’t really understand the logic behind it, and I’ve never encountered it in any other payment processing systems, the iOS one included. Still, if you receive a transaction containing acknowledgementState=0, you are to acknowledge the subscription.

To do so, you’ll need to invoke the purchases.subscriptions.acknowledge method. This method executes a POST request

https://androidpublisher.googleapis.com/androidpublisher/v3/applications//purchases/subscriptions//tokens/:acknowledge

The parameters are the same as for request validation. If the request is successfully executed, the subscription will get acknowledged, which means you won’t lose your money.

If the subscription hasn’t been fully purchased yet, there’s no need to acknowledge it.

Subscription renewal cancellation, revocation, refund, and deferral

Apart from subscription validation and acknowledgement, Google Play Developer API can also be used for other subscription operations. It should be noted that these are quite rare and, with the exception of renewal, are supported by Google Play Console. I’ll still list them to give you a general understanding of API solutions scope. All these requests have the same parameters required as the previously mentioned methods, namely, packageName, subscriptionId, and token.

Product (not subscription) validation

Product validation is similar to subscription validation. You need to invoke the purchases.products.get method to execute the GET request.

https://androidpublisher.googleapis.com/androidpublisher/v3/applications//purchases/products//tokens/ 

We’re already familiar with all these parameters from looking at the examples outlined above.

Transaction data (for a product):

 "purchaseTimeMillis": "1630529397125", "purchaseState": 0, "consumptionState": 0, "developerPayload": "", "orderId": "GPA.3374-2691-3583-90384", "acknowledgementState": 1, "kind": "androidpublisher#productPurchase", "regionCode": "RU" >

Product transactions include much fewer properties than subscription transactions. Let’s take a look at some important ones:

As you can see, product validation is quite similar to subscription validation. There are some points to take note of, though:

Just as subscription purchases, product purchases need to be acknowledged. To do so, invoke the purchases.products.acknowledge method which will execute a POST request

https://androidpublisher.googleapis.com/androidpublisher/v3/applications//purchases/products//tokens/:acknowledge

If the purchase hasn’t yet been completed, there’s no need to acknowledge it.

Refund tracking for subscriptions and products

High-quality analytics is impossible without accounting for refunds. Unfortunately, refund data is neither present in the transaction nor gets prompted as a separate event, as it works in iOS. To receive the list of refunded transactions, you’ll need to invoke the purchases.voidedpurchases.list on a regular basis — for example, once per day. This method will execute a GET request:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications//purchases/voidedpurchases

In response to the request, you’ll receive the list of all refunded transactions. I recommend searching for transactions in the database by the orderId parameter, as opposed to the purchaseToken one. First, this will take less time. Second, all subscription renewals will share the same token, and you’ll just need to fetch the most recent one.

2024 subscription benchmarks and insights

Get your free copy of our latest subscription report to stay ahead in 2024.

Server notifications for Google Play transactions

Server notifications (real-time developer notifications) help you learn about the events that occurred on Google’s side, on your server, and almost live. Once these are configured, you’ll be notified about new purchases, renewals, payment issues, etc. This can help you collect better analytics, as well as make the subscriber status management much easier.

To start receiving server notifications, you need to create a Google Cloud Pub/Sub topic, which will send notifications to your desired address. This topic should then be indicated in the Monetization setup section of the Google Play Console. For a detailed guide with screenshots included, see Adapty docs.

 "message":  "data": "eyJ2ZXJzaW9uIjoiMS4wIiwicGFja2FnZU5hbWUiOiJjb20uYWRhcHR5LnNhbXBsZV9hcHAiLCJldmVudFRpbWVNaWxsaXMiOiIxNjMwNTI5Mzk3MTI1Iiwic3Vic2NyaXB0aW9uTm90aWZpY2F0aW9uIjp7InZlcnNpb24iOiIxLjAiLCJub3RpZmljYXRpb25UeXBlIjo2LCJwdXJjaGFzZVRva2VuIjoiY2o3anAuQU8tSjFPelIxMjMiLCJzdWJzY3JpcHRpb25JZCI6ImNvbS5hZGFwdHkuc2FtcGxlX2FwcC53ZWVrbHlfc3ViIn19", "messageId": "2829603729517390", "message_id": "2829603729517390", "publishTime": "2021-09-01T20:49:59.124Z", "publish_time": "2021-08-04T20:49:59.124Z" >, "subscription": "projects/935083/subscriptions/adapty-rtdn" >

We’re mostly concerned with the data key that contains transaction data encoded with base64. The messageId key can be used for message deduplication, so that you don’t need to process duplicate messages.

Here’s a transaction in a server notification:

 "version": "1.0", "packageName": "com.adapty.sample_app", "eventTimeMillis": "1630529397125", "subscriptionNotification":  "version": "1.0", "notificationType": 6, "purchaseToken": "cj7jp.AO-J1OzR123", "subscriptionId": "com.adapty.sample_app.weekly_sub" > >

The packageName key helps you understand which app this event belongs to. The subscriptionId key tells you which subscription is involved, and purchaseToken helps you find the specific transaction. With subscriptions, you’ll always be looking for the last transaction in the renewal chain, as it’s the one this event will belong to. The notificationType key contains the event type. In my opinion, these are the most handy ones for subscriptions:

In products (not subscriptions), you’ll receive oneTimeProductNotification in place of the subscriptionNotification key. It will also contain the sku key instead of the subscriptionId key. Moreover, you’ll only ever receive 2 types of events for products:

Conclusion

Server-side validation supercharges the analytics you’ll be able to collect for your app. It makes it harder for fraudsters to access the premium content and can be used to implement cross-platform subscriptions. However, server-side validation can take quite a while to implement, especially if high data accuracy is a must. To provide high-quality data, you’d need to account for a multitude of side cases, such as subscription upgrade, subscription crossgrade, trial periods, promo and introductory offers, grace period, refunds, etc. You’d also have to know of and account for all the policy minutiae, such as Google only charging a 15% (as opposed to 30%) commission on subscriptions that get renewed for more than a year.

Increase your app’s revenue by exploring Adapty! If you’re an app developer or owner looking to skyrocket your conversion rates and in-app subscriptions, don’t miss the chance to schedule a free demo call with us. We’ll walk you through our platform, showcasing our array of features and benefits, enabling you to integrate in-app purchases within an hour, all with no obligation! Experience firsthand how Adapty’s robust A/B testing for paywalls can help you double your subscription revenue in just three months and run monetization experiments in a faster and more cost-effective manner.

Unlock 2024 subscription secrets

Access our free 2024 in-app subscription report to view essential benchmarks and market trends.
Includes cheat sheets!