Transactions Validation

The local receipt strucutre validation has been is already performed by a plugin before you get a successful purchase event.

If you need to be 100% sure that transaction is valid, you need to use the server side validation. 

Note: No point to run this validation approach on a client side, since device "hosts" file can be already infected. 

General approatch for a server side validation is:

  • You've got a purchase complete callback
  • Get purchase info from a GooglePurchaseTemplate object
  • Send this info to your server (make sure your communication is sequred)
  • On your server side check if this purchase is valid via Google API
  • Retrun result to a client
  • Performing the required actions on a client side depends on your server responce.

Google provides receipt validation through the Google Play Developer API, within the API there are two endpoints you will be most interested in: Purchases.products: get and Purchases.subscriptions: get.

Purchases.products: get can be used to verify a non-auto-renewing product purchase, where Purchases.subscriptions: get is for verifying and re-verifying auto-renewing product subscriptions.

To use an endpoint you must know the packageNameproductIdpurchaseToken. All of these can be found in the payload you received on purchase. You also need an access_token which you can get by creating a Google API service account.

To get started with a service account, first, go to the Google play Developer console API access settings page and click the Create new project button:

You should now see a new Linked Project and a few new sections. In the Service Account section click the Create service account button.

You will be presented with an info box with instructions to create your service account. Click the link to Google Developers Console and a new tab will spawn.

Now click Create new Client ID, select Service account from the options and click Create Client ID.

A JSON file will download, this is your JSON Web Token you will use to exchange for an access_token,so keep it safe.

Next, switch tabs back to the Google play Developer console and click Done in the info box. You should see your new service account in the list. Click on Grant access next to the service account email.

Next under the Choose a role for this user, select Finance and click Add user.

Now you have set up your service account and it has all the necessary accesses to perform receipt validations. Next up is exchanging your JWT for an access_token.

The access_token expires after one hour of exchange, so you need some server code to handle this. And Google provides several libraries in many languages to handle this (list not exhaustive):

I won't go into detail because there is a plenty of documentation on how to use these libraries, but I will mention you want to use the as the OAuth2 scope, the client_email from the JWT as the issuer and the public key you can get from the private_key and the passphrase notasecret will be used for the signing_key.

Once you have the access_token, you're good to go (at least for the next hour when you want to request a new one following the same process in the above paragraph).

To check the status of a consumable (non-auto-renewing) purchase make a http get request to:

If you get a 200 http response code, everything goes as planned and your purchase is valid. A 404 means your token is invalid so the purchase has been most likely a fraud attempt. A 401 means your access token is invalid and a 403 means your service account has insufficient access, check that you have enabled Finance for the access account in the Google Play Developer console.

The response from 200 looks similar to this:

  "kind": "androidpublisher#productPurchase",
  "purchaseTimeMillis": long,
  "purchaseState": integer,
  "consumptionState": integer,
  "developerPayload": string

For an explanation of each property see

Subscriptions are similar however the endpoint looks like this:

And the response should contain these properties:

  "kind": "androidpublisher#subscriptionPurchase",
  "startTimeMillis": long,
  "expiryTimeMillis": long,
  "autoRenewing": boolean

See for the property descriptions and note that startTimeMillis and expiryTimeMillis will be a subject to change depending on the duration of the subscription.

Article by Marc Greenstock. Source.