Search code examples
androidnode.jsgoogle-playandroid-inapp-purchase

GooglePlay in app purchase server side product purchase validation failed with 400 error when using 'android.test.purchased'


I created an Android app that supports in-app-purchase. In order to develop in app purchase function, I need to setup in-app-purchase in GooglePlay console.

In order to create in-app-purchase in GooglePlay console, I need to upload app first. However, my app needs in-app-purchase to complete the development.

Therefore, I uploaded my incomplete app to GooglePlay in closed Alpha track. From some research, I learnt I also need to publish the app as otherwise in app purchase won't work.

However, the situation is Google takes long time to review the app and the status of the app is stuck in "being reviewed" for over 3 days. I don't know if the review will be passed as the app is incomplete and it is not functional.

OK, after some research, I learnt that I can use test in-app-purchase item such as android.test.purchase and android.test.canceled

Therefore, I started testing my in-app-purchase function where my mobile app calls my backend to validate the purchase result and then I start receiving 400 error from Google on my server side.

At the following is my server side code that validates the purchase that received from mobile app:

let performGooglePlayValidationLogic = async function(userid, sku, purchaseToken) {
    let auth = new google.auth.GoogleAuth({
        credentials: {
            client_email: '[email protected]',
            private_key: '-----BEGIN PRIVATE KEY-----XXXXXXXXXXXXX-----END PRIVATE KEY-----\n'
        },
        scopes: ['https://www.googleapis.com/auth/androidpublisher'],
    });
    const authClient = await auth.getClient();
    google.options({ auth: authClient });
    let purchaseResponse = {};
    let purchases = google.androidpublisher({version: 'v3',}).purchases;
    try {
        //products
            purchaseResponse = await purchases.products.get({
                packageName: 'com.mycompany.myapp',
                productId: sku,
                token: purchaseToken,
            });
        if (purchaseResponse.data.purchaseState !== 0) {
            throw new BadRequestException('Purchase is either Pending or Cancelled!');
        } else if (purchaseResponse.data.consumptionState !== 0) {
            throw new BadRequestException('Purchase is already consumed!');
        } else {

At the following is the parameter that I passed into this method (The value are received from mobile app)

sku: android.test.purchased
purchaseToken: inapp:com.mycompany.myapp:android.test.purchased

Then I received exception at line purchaseResponse = await purchases.products.get({:

status:400
statusText:'Bad Request'
url: https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.mycompany.myapp/purchases/products/android.test.purchased/tokens/inapp%3Acom.mycompany.myapp%3Aandroid.test.purchased'

I am not sure if this 400 error is caused by my app is still under review so package name com.mycompany.myapp might yet be available, or it is because I did something wrong?

From my research it looks like 400 error is most likely cause by an invalid purchase token, which in my case it is inapp:com.mycompany.myapp:android.test.purchased I do feel it looks suspicious but it is received from my mobile app where is issued by GooglePlay.


Solution

  • ok after 2 day's investigation, it has been discovered that:

    The purchasetoken (inapp:com.mycompany.myapp:android.test.purchased) generated by inapppurchase item android.test.purchased cannot be used on server side validation as androidpublisher.googleapis.com will return error 400 for such purchasetoken.

    I hope my 2 day's work can help others with saving their time.