![André Cipriani Bandarra](https://web.dev/images/authors/andreban.jpg) André Cipriani Bandarra [X](https://twitter.com/andreban) [GitHub](https://github.com/andreban) [Mastodon](https://mastodon.social/@andreban) [Bluesky](https://bsky.app/profile/bandarra.me) [Homepage](https://bandarra.me)

<br />

This guide is for web apps packaged as a [Trusted Web Activity](https://developer.chrome.com/docs/android/trusted-web-activity). For
Android apps written in Kotlin or Java, see the
[Google Play Billing documentation](https://developer.android.com/google/play/billing).

Apps on Google Play that sell digital goods or subscriptions must use Google
Play Billing. In a Trusted Web Activity, you integrate with it using the
following web APIs, available in Chrome 101 and later on Android and ChromeOS:

- **[Digital Goods API](https://github.com/WICG/digital-goods/blob/main/explainer.md):** Retrieves product details and the user's purchases.
- **[Payment Request API](https://developer.mozilla.org/docs/Web/API/Payment_Request_API):** Starts the purchase flow.

## Requirements

To set up Google Play Billing, you need the following:

- A [Google Play Developer account](https://support.google.com/googleplay/android-developer/answer/6112435) and a [Google Payments merchant account](https://support.google.com/paymentscenter/answer/7161426) that are [linked to each other](https://support.google.com/googleplay/android-developer/answer/3092739).
- A [Play Store listing](https://support.google.com/googleplay/android-developer/answer/9859152) with a [release on the public, closed testing, or internal testing track](https://support.google.com/googleplay/android-developer/answer/9845334).
- Your app's [products and subscriptions configured in the Play Console](https://developer.android.com/google/play/billing/getting-ready#products).
- A Trusted Web Activity project with a working [Digital Asset Links configuration](https://developer.chrome.com/docs/android/trusted-web-activity/quick-start#creating-your-asset-link-file).

Don't charge users to download your app from the Play Store. Your PWA is still
available on the web, so a paid download doesn't restrict access. Instead, sell
features or content with in-app purchases or subscriptions.

## Enable Play Billing in your Android project

If you build your app with [Bubblewrap](https://developer.chrome.com/docs/android/trusted-web-activity/quick-start), enable the `playBilling` feature in
`twa-manifest.json` at the root of your project:

    "features": {
      "playBilling": {
        "enabled": true
      }
    }

Then run `bubblewrap update` followed by `bubblewrap build`, and upload the new
Android package to the Play Store.

If you build your app in Android Studio, add the
[Android Browser Helper Play Billing library](https://github.com/GoogleChrome/android-browser-helper/tree/main/playbilling). For the required
`DelegationService` and `AndroidManifest.xml` changes, see the
[`twa-play-billing` demo](https://github.com/GoogleChrome/android-browser-helper/tree/main/demos/twa-play-billing).

## Check for Google Play Billing support

Call `getDigitalGoodsService()` with the Play Store identifier,
`https://play.google.com/billing`. If the method is missing or the call fails,
use another payment flow:

    if ('getDigitalGoodsService' in window) {
      try {
        const service =
            await window.getDigitalGoodsService('https://play.google.com/billing');
        // Google Play Billing is available.
      } catch (error) {
        // Google Play Billing isn't available. Use another payment flow.
      }
    }

## Display product details

Use `getDetails()` to retrieve the title, description, and price for your
product IDs, so that the prices you display match the Play Store:

    const details = await service.getDetails(['shiny_sword', 'gem']);
    for (const item of details) {
      const localizedPrice = new Intl.NumberFormat(
          navigator.language,
          {style: 'currency', currency: item.price.currency}
      ).format(item.price.value);

      renderProductDetails(
          item.itemId, item.title, localizedPrice, item.description);
    }

The Digital Goods API can't list your product IDs. To query them from your
backend, use the [Google Play Developer API](https://developers.google.com/android-publisher/api-ref/rest/v3/inappproducts/list).

## Make a purchase

Create a `PaymentRequest` with `https://play.google.com/billing` as the payment
method and the product ID as `sku` in `data`. Google Play Billing uses the price
from the Play Console, so `total` is required but ignored.

After `show()` resolves, send the purchase token to your backend to
[verify and acknowledge the purchase](https://developer.android.com/google/play/billing/security#verify). If you don't acknowledge a purchase
within three days, Google Play refunds the user and
[revokes the purchase](https://developer.android.com/google/play/billing/integrate#process).

    async function makePurchase(service, sku) {
      const paymentMethods = [{
        supportedMethods: 'https://play.google.com/billing',
        data: {sku},
      }];
      // Required by the Payment Request API, but ignored by Google Play Billing.
      const paymentDetails = {
        total: {label: 'Total', amount: {currency: 'USD', value: '0'}},
      };

      const request = new PaymentRequest(paymentMethods, paymentDetails);
      try {
        const response = await request.show();
        const {purchaseToken} = response.details;

        if (await acknowledgePurchaseOnBackend(purchaseToken, sku)) {
          // Optional: consume the purchase so the user can buy the item again.
          await service.consume(purchaseToken);
          await response.complete('success');
        } else {
          await response.complete('fail');
        }
      } catch (error) {
        // An AbortError usually means the user canceled the purchase.
      }
    }

## Check a user's purchases

Use `listPurchases()` to find items the user owns, such as active
subscriptions, unconsumed purchases, or redeemed promo codes. Verify each
purchase on your backend, and acknowledge any you haven't acknowledged:

    const purchases = await service.listPurchases();
    for (const p of purchases) {
      await verifyOrAcknowledgePurchaseOnBackend(p.purchaseToken, p.itemId);
      // Update the UI with items the user is entitled to.
    }

## Test your integration

To test on an Android device before you publish to the Play Store, follow these
steps:

1. Use Android 9 or later with [developer options enabled](https://developer.android.com/studio/debug/dev-options).
2. In Chrome, go to `chrome://flags` and enable `#enable-debug-for-store-billing`.
3. Serve your site over HTTPS. The Digital Goods API isn't available over HTTP.

When your app is installed from the Play Store, you don't need the flag. To test
with test accounts and test products, see
[Test your Google Play Billing Library integration](https://developer.android.com/google/play/billing/test).

## Next steps

- Verify purchases on your backend with a [Google Play Developer API client library](https://developers.google.com/android-publisher/libraries).
- Learn how to [sell subscriptions](https://developer.android.com/google/play/billing/subscriptions).
- Set up [real-time developer notifications](https://developer.android.com/google/play/billing/getting-ready#configure-rtdn) to track subscription changes.