trusted-web-activity

Why does the Digital Goods API's getService method reject with clientAppUnavailable in my TWA?


We have a TWA/PWA in the Play store. We want to start selling Play subscriptions through our app.

We followed the Chrome Developer guides to implementing the necessary steps in the Android project and the PWA. We also checked our code against the Android Browser Helper demo and the code generated by the latest Bubblewrap version. (We did not use Bubblewrap to generate our project, however.) As far as we can see, our functionality is equivalent. We tried bundling the same versions of the androidbrowserhelper:androidbrowserhelper and androidbrowserhelper:billing dependencies as Bubblewrap. We also tried downgrading to those used in TWA Play Billing Demo.

With these prerequisites (hopefully) in place in the Android wrapper, we configured a subscription product (with a base plan) in the Play console.

Now we are trying to test fetching product details using the Digitial Goods API, following the examples linked above. We can successfully detect the availability of the API in our context. We also find the call to window.getDigitalGoodsService('https://play.google.com/billing') successfully resolves. We await the service object before calling service.getDetails([ 'our_sku' ]), but this call rejects with an error clientAppUnavailable.

We cannot find any clarification related to this error, either in resources about Android Browser Helper nor the (Android) Google Play Billing Library.

The following snippets illustrate the relevant code (IMO) in our codebases.

build.gradle

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.4.0'

    implementation 'com.google.androidbrowserhelper:billing:1.0.0-alpha09'

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'io.sentry:sentry-android:6.1.2'

    implementation platform('com.google.firebase:firebase-bom:30.1.0')
    implementation 'com.google.firebase:firebase-messaging'
    implementation 'com.google.firebase:firebase-firestore'
}

AndroidManifest.xml

<service
            android:name=".DigitalGoodsDelegationService"
            android:enabled="true"
            android:exported="true">

            <intent-filter>
                <action android:name="android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

DigitalGoodsDelegationService.java

import com.google.androidbrowserhelper.playbilling.digitalgoods.DigitalGoodsRequestHandler;
import com.google.androidbrowserhelper.trusted.DelegationService;

public class DigitalGoodsDelegationService extends DelegationService{
    @Override
    public void onCreate() {
        super.onCreate();
        registerExtraCommandHandler(new DigitalGoodsRequestHandler(getApplicationContext()));
    }
}

PWA

const SERVICE_ID = 'https://play.google.com/billing';

window.getDigitalGoodsService(SERVICE_ID)
    .then((service) => {
        if (!service) throw new Error(ERROR_MESSAGES.noPlayBilling);

        // eslint-disable-next-line no-console
        console.log('Got service');
        return service.getDetails(SKUS);
    })
    .then(products => {
        if (!products || !products.length) {
            throw new Error(ERROR_MESSAGES.noProducts);
        }

        ...
    })
    .catch(err => {
        ...
    });

Solution

  • We resolved the error by compiling against an upgraded version of the Android API. (Before, compileSdkVersion was set to 30 in our module-level build.gradle. We upgraded this to 31.)