androidnode.jsvalidationin-app-billingreceipt

How to validate android billing receipt server side


I'd like to make server side validation of a purchase receipt made in an android application. I'm using a node.js backend.

I'm currently trying to do it using google-play-purchase-validator node module (https://www.npmjs.org/package/google-play-purchase-validator) which seems the most up-to-date module to do it (performing a real-time request to google purchase api).

In the Google developper console, i created a Google Service Accounts, and then obtained an email and key to be used in the module, I also connect this Services Accounts with my application as explained in this post Use service account to verify Google InAppPurchase). Unfortunatly it doesn't work. It seems that the jwt token generated by the node module isn't correctly signed (I get the following error : failed to sign JWT, the key is probably invalid).

Here's some code :

var Verifier = require('google-play-purchase-validator');
var options = {
  email:'myemail@developer.gserviceaccount.com',
  key: 'myprivatekey',
};
var verifier = new Verifier(options);
verifier.verify(item.Receipt, function cb(err, response) {
if (err) {
    console.log("there was an error validating the receipt");
    console.log(err);
}
else{
    console.log("sucessfully validated the receipt");
    console.log(response);
}

The private key comes from a .pem file i was able to generate from a .p12 file provided by google using the following command :

openssl pkcs12 -in downloaded-key-file.p12 -out your-key-file.pem -nodes

The google-play-purchase-validator module is based on some others modules (google-oauth-jwt, request, crypto). I tried to debug a little, everything seems to be done correctly.

Any idea where i may be wrong ?

Paul


Solution

  • This answer will be only useful once you have followed steps of Above accepted answer. i am using googleapis npm package to validate consumable purchase.

        import { google } from 'googleapis';
        
        const auth = new google.auth.GoogleAuth({
                    credentials: {
                        client_email: 'email from json file',
                        private_key:
                            'private key from json file',
                    },
                    scopes: ['https://www.googleapis.com/auth/androidpublisher'],
                });
                const authClient = await auth.getClient();
                google.options({ auth: authClient });
                try {
    // packageName,productId,token you can get from request sent from android
                    const purchaseResponse: AndroidPurchaseResponse = await google
                        .androidpublisher({
                            version: 'v3',
                        }).purchases.products.get({
                            packageName: 'packageName',
                            productId: 'productId',
                            token: 'purchaseToken',
                        });
        
                    if (purchaseResponse.data.purchaseState !== 0) {
                        throw new BadRequestException('Purchase is either Pending or Cancelled!');
                    }
                    if (purchaseResponse.data.consumptionState !== 0) {
                        throw new BadRequestException('Purchase is already consumed!');
                    }
                    if (purchaseResponse.data.orderId !== requestDto.orderId) {
                        throw new BadRequestException('Invalid orderId');
                    }
                    return purchaseResponse;
                } catch (e) {
                    throw new BadRequestException(e);
                }