Search code examples
androidgoogle-cloud-platform

Cannot create a service account with permissions for Google Play Android Developer API


I want to validate in-app purchases from the backend in my mobile app by calling Google Play Android Developer API. But I am unable to create a service account in GCP to which I can provide correct permissions. My mobile app is in production.

The Google Play Android Developer API is enabled in my GCP project. It was enabled a few days ago. It displays Enabled when I locate it in the console.

Apparently I need to add these roles to the service account but I just cannot find them - roles/androidpublisher.developer and roles/serviceusage.serviceUsageConsumer.

I did create a service account and gave it these permissions - Consumer Procurement Order Administrator, Consumer Procurement Order Viewer, Financial Services Admin, Financial Services Viewer

Then I added the service account email as a User in my Play Store account as an Admin user. But it's clearly not enough. And the issue is clearly on the GCP side because in the Play Console this user is an admin. The queries to Play Store are being denied because of 401 error.

This is how I initialize my client. It has the key file and everything. That part is fine. I am using v3

const { google } = require('googleapis');
const { GoogleAuth } = require('google-auth-library');

const auth = new GoogleAuth({
    keyFile: path.resolve(__dirname, '..', path.basename(keyFilePath)),
    scopes: ['https://www.googleapis.com/auth/androidpublisher'],
});

this.androidPublisher = google.androidpublisher({
   version: 'v3',
   auth,
});

This is the call I am making -

      const response = await this.androidPublisher.purchases.products.get({
        packageName,
        productId,
        token: purchaseToken,
      });
  errors: [
    {
      message: 'The current user has insufficient permissions to perform the requested operation.',
      domain: 'androidpublisher',
      reason: 'permissionDenied'
    }
  ],

Edit:

I am using @google-cloud/recaptcha-enterprise too with a different service account. That service account picks up its credentials file from the env variable GOOGLE_APPLICATION_CREDENTIALS. I want to use a different service account in this case so I had to generate a separate credentials JSON file and supply its path using a different property name.

If I disable the service Google Play Android Developer API the request fails with the reason that the API is not enabled. So the API is getting recognized but I just cannot attach the right roles to this sa. I gave it Owner permission but even that failed with a 401 error.

I go to the service page for Google Play Android Developer API and generate sa from there and the sa has no permissions. WTF.


Solution

  • This is what I had to do - dump Google's library and work with JWT straight. No more adjustments were needed. Service account permissions were fine, the email was properly added in the Play Console. Just the Google Library was f***ing things up. Google Cloud has been a disaster to work with. Here's part of the code -

          // Create JWT client directly
          this.client = new JWT({
            email: credentials.client_email,
            key: credentials.private_key,
            scopes: ['https://www.googleapis.com/auth/androidpublisher']
          });
    
          // Test initial token generation
          const token = await this.client.authorize();
          if (!token?.access_token) {
            throw new Error('Failed to generate initial access token');
          }
    
            const url = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${packageName}/${endpoint}`;
            
            const options = {
              method,
              headers: {
                'Authorization': `Bearer ${tokenData.access_token}`,
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'x-goog-api-client': 'gdcl/6.0.0',
                'x-android-package': packageName,
              }
            };
    
            const req = https.request(url, options, (res) => {