- PWA Play Billing Sample Setup
- Introduction
- Checkout and setup
- Firebase setup
- Request an origin trial token
- First deploy
- Google Play Console setup
- Second Deploy
- Google Play Developer API
- Real-Time Developer Notifications (RTDN)
- Final deploy
- Testing
This sample app demonstrates how you can publish your Progressive Web App (PWA) using Trusted Web Activities (TWA) on Google Play Store and receive payments with Google Play Billing using Digital Goods API and PaymentRequest API.
The sample uses Firebase for static hosting and backend server operations for simplicity. However, you can use any other service to publish on Play Store and use Play Billing as well.
$ git clone https://github.com/chromeos/pwa-play-billing
$ cd pwa-play-billing
$ npm install
$ cd functions
$ npm install
$ cd ..
$ npm start
- In order to start with Firebase, please visit the Firebase Console and create a Firebase account.
- Once your Firebase account is created, revisit the console and create a new project by clicking the “Add project” button
- Create a unique name for your project. Below the name input box will be the project ID, write it down.
- The next page is regarding activating Google Analytics for your Firebase project. Turn this option off as this is just a test application and we don’t need analytics activated.
- Firebase will then set up all the required resources for your project. When it’s done, you will see a “Your new project is ready” message with a continue option. Go ahead and click “Continue”.
The sample requires users to be signed-in with an account before they can purchase and consume in-app purchases. User accounts also allow a user to access their purchases across multiple devices (purchases made via the Play Store on Chrome OS, can still be accessed on an Android device).
- To set up authentication, go to your project page in the Firebase Console
- In the left-hand menu, click to expand the “Build” section and choose “Authentication”.
- From there, click on the Sign-in method tab (If you see a “Get Started” button, clicking on it will take you to the same page as well).
- Click on the Google provider in the Sign-in providers list.
- Turn the switch on to enable the Google Provider.
- Give the project a unique name as this will be what is presented to your users to identify your app.
- Choose an email address for the project support email.
- Click Save to confirm changes.
The sample code loads the authentication method without needing to pass in the Web SDK configuration information because Firebase Hosted Configuration handles this automatically. To learn more about authentication, please visit the Firebase documentation here.
- In the left hand navigation menu for your project in the Firebase console, click on the “Hosting” option. At the top of the page should be a Get Started link, click on it.
-
The Get Started button will provide the command to install the Firebase CLI. That command is:
$ npm install -g firebase-tools
-
After installing the Firebase CLI, you will need to login to Firebase. You can use the following command:
$ firebase login
-
Finally, in the downloaded project, in the .firebaserc file and the firebase.json file, replace
<FIREBASE_PROJECT_ID>
with your project ID you noted down earlier when you first created the Firebase project.// firebase.json { "hosting": { "site": "<FIREBASE_PROJECT_ID>", "public": "src", ...
// .firebaserc { "projects": { "default": "<FIREBASE_PROJECT_ID>" }
The frontend code can be found here.
A large portion of granting purchase entitlements is controlled from the backend. The sample uses Firebase Functions to implement the backend functionality, as well as the real time developer notifications (RTDN).
Note: To use Firebase Functions, we need to enable a Blaze billing plan. The Blaze billing plan offers a free starting quota to test this project without the need to incur costs. As you experiment and grow, your costs may grow as well, so it would be beneficial to test the project as necessary and address any cleanup operations that may be necessary to reduce costs.
-
To upgrade your project’s billing plan, click on the Functions option in the left hand navigation menu for your project in the Firebase Console.
-
Click on “Upgrade project”
-
Confirm that the selected plan is Blaze.
Note: If you don’t have a billing account yet, Firebase will take you to the flow to create a billing account. Complete the steps to create a billing account, and go back to Step 1.
- Click Purchase to switch to the Blaze billing plan.
- You can also set a budget alert to avoid unexpected bills in the confirmation page.
This sample uses Firebase Cloud Firestore to store user information.
- To set up Firestore, visit the Firestore tab in the Firebase Console and click Create Database.
- In the first step, choose “production mode” to keep the data private and click Next. The Admin SDK in the sample functions code handles the communications with the database.
- In the second step, choose a Cloud Firestore location. Pick something close to your location where your functions are deployed. Then click Enable.
There are three key collections of data as shown below (Note that the document id was omitted from the tables as it is auto-generated). The sample’s backend code automatically populates these tables with Firebase Functions. If you’d like to generate your own SKUs via the Google Play Console, make sure to add them to the SKUs collection in Firestore.
SKUS | ||
Field | Type | Values |
sku | string | Value of a SKU defined in the Google Play Console |
type | string | One of {repeatable, onetime, subscription}. These values represent the purchase behavior. |
tokens | ||
Field | Type | Values |
isValid | boolean | Verifies that this token provided was a valid token and can be used to identify purchases (prevents fraud) |
purchaseToken | string | A unique token identifier supplied by Google Play |
userDatabaseId | string | This references the users collection unique identifier for the user |
users | ||
Field | Type | Values |
accountName | string | Name on the account |
string | Email address | |
hasBasicSub | boolean | True if the user has the basic subscription |
lastQueryTime | number | The last time that the user was queried from the database |
numCoins | number | The number of coins that the user has. Coins are repeatable purchase items that accumulate. |
photoEntitlements | Array<string> | One-time purchase items that the user has. |
theme | string | The purchase made by using the repeatable purchase item. (i.e., the user purchased a red theme using coins that they purchased). |
On line 23 of index.html, you will see an option to insert an origin trial token. Visit this link to request an origin trial token for the Digital Goods API. Once you have your origin trial token, you can go and insert it on line 23 in the content attribute which currently holds the value $ORIGIN_TRIAL_TOKEN
.
<!-- Origin Trial -->
<meta http-equiv="origin-trial" content="$ORIGIN_TRIAL_TOKEN" />
To learn more about setting up origin trials, please see this guide.
After setting up Firebase, you can deploy just the hosting content first:
$ npm ci // Install the project
$ npm run deploy:hosting // Deploys only to hosting
You can now visit your PWA at https://<project_id>.web.app
Note that some functionality is still missing. We’ll set them up in the following sections and redeploy the app.
To list your TWA in the Google Play Store, you’ll need to create a developer account. There is a one-time $25 registration fee.
After your developer account is set up, go to “All apps” in the left navigation menu and select “Create app”. Once you fill out the required fields to create your app, you’ll be brought to the “Dashboard” where you’ll find step-by-step task guides to test, setup, and release your app. For more information on creating a new app, visit Play Console help page.
Bubblewrap is a command-line tool that will wrap your PWA in a Trusted Web Activity and output AAB and APK files that can be uploaded to the Play Console. To use Bubblewrap you need Node.js 10.0 or above.
During your first time running Bubblewrap, it will give you the option to download and install the dependencies, or you may set them up manually yourself. We recommend checking out the quickstart guide for more details.
$ npm i -g @bubblewrap/cli
$ mkdir <new-project-dir>
$ cd <new-project-dir>
$ bubblewrap init --manifest https://<project.id>.web.app/manifest.json
This init command will parse the manifest and prompt you to enter or confirm values for your Android project. Fill out the “Application name” and “Short name” as you would like your app name to appear (note that you are restricted to 12 characters for “short name”).
If you have an existing listing in the Play Store:
- For “Application ID”, use your existing package name when asked.
- Make sure to enter a version code higher than its existing versions.
- When prompted for “Key store location” and “Key name”, pass in the same key store location and key name that you used before. Otherwise the Play Console will reject your new upload because you’ve signed it with different keys.
If you’re planning to create a new listing:
- For “Application ID”, choose a new unique package name.
- Leave the version code as is (default value of 1).
- For “Key store location” and “Key name”, you may use the default or enter your preferred path and name. Bubblewrap will then let you know that it couldn’t find a key store at the provided path. Enter “Yes” to let it create a new signing key. Remember the passwords you entered for your new key store and key as you’ll be prompted for them later on when you build.
When the prompt asks you whether to enable Play Billing, respond “Yes”.
$ bubblewrap build
After successfully building your project, in your project directory, you’ll see the APK (app-release-signed.apk
) and AAB (app-release-bundle.aab
) you can upload to your Play Console (next section).
Note that if you skipped the step for updating your Digital Asset Links above, you may receive a warning message which is safe to ignore for now.
Read more about setting up Digital Asset Links at: https://developers.google.com/web/android/trusted-web-activity/quick-start#creating-your-asset-link-file
Failed to run the PWA Quality Criteria checks. Skipping.
Follow the tasks in the “Set up your app” section in the dashboard before moving on.
- We recommend you to publish to Internal Testing Track to speed up the time to start testing in the Play Store. You can access the Internal Testing Track by choosing on Internal testing on the left hand side menu.
- Click on the “Create new release” button.
- If you haven’t uploaded a package before, Play Console will offer you to opt in for Play App Signing. This is highly recommended. Click Continue to opt in.
- Click on the Upload button and choose the
app-release-bundle.aab
package generated by Bubblewrap. - Fill in “Release name” and “Release notes” fields and click Save.
- Click on “Review release”.
- Review the release and click on “Start rollout to Internal testing”. If you see a warning with the following message, you can ignore it for now. We’ll configure testers in the next step.
Note: This release will not be available to any users because you haven't specified any testers for it yet. Tip: configure your testing track to ensure that the release is available to your testers.
- Click on Testers tab under Internal testing page
- Either click on “Create email list” to create a new testers list. Add email addresses of your testers into this list and save. You can also edit an existing list by clicking on the right arrow button.
- Enable your testers list by clicking on the checkbox next to it.
- Save changes.
- Click on the Copy link button under the “How testers join your test” to retrieve the URL to your testing track. Share this link with your testers. Testers need to follow this link and opt-in for receiving the testing version first to install your app.
Now that the app is uploaded to Play Store, you should be able to opt-in for the testing track by following the test URL (copied above), and install the app through Play Store and run it on your device. Note that some functionality is still missing, and we’ll configure them in the following steps.
This section will cover the steps to set up your in-app products and subscriptions which will be purchasable via the Digital Goods API.
In the left-hand navigation menu, scroll down until you see the “Monetize” section and expand the “Products” menu. If you don’t see the “Monetize” section, make sure you’re in the app menu and not the general user menu. To see the app menu, go to “All apps” and select the corresponding app.
If you see “Missing requirements for accessing this page”, follow the instructions to set up a merchant account and come back to this page later.
- Click on “In-app products”.
- Click on the “Create product” button.
- Enter
coins_100
as the Product ID. This cannot be changed later. - Fill in the name and description fields. Set a price, for example $1. You can always edit these fields later.
- Click Save and then Activate.
Repeat this process for these Product IDs:
coins_200
coins_1000
onetime
- Click on “Subscriptions”.
- Click on the “Create subscription” button.
- Enter
basic_sub
as the Product ID. This cannot be changed later. - Fill in the name and description fields.
- Set a billing period (e.g weekly) and price, for example $10. Note that the billing period cannot be changed later.
- Click Save and then Activate.
If you’d like to add your own SKUs, be sure to note down the “Product ID”s as that’s how they’ll be identified by the Digital Goods API. In the case of our sample code, we obtain the product IDs from the SKUs collection we populated in Firestore (see above). So make sure the SKUs you’ve added in the Play Console matches the information in the SKUs collection in Firestore (and vice versa). If not, please adjust either accordingly so they are aligned.
Now that you’ve successfully created your TWA and uploaded it to the Play Console, it’s time to update the missing information and redeploy your PWA again.
After your first deploy, you might have noticed that when checking your PWA at https://<project.id>.web.app
, clicking on the user profile icon will show a menu. One of the menu items is “View In Play Store” and clicking on it will take you to a broken link. Now that you’ve uploaded your app to the Play Store and released it to the internal testing track, you can update this link! In ./src/js/components/profile-menu.html replace <PLAY_PACKAGE_NAME>
with your app’s package name for easy access to install the TWA.
// profile-menu.html
...
_showPlayStore() {
window.open('https://play.google.com/store/apps/details?id=<PLAY_PACKAGE_NAME>','_blank');
...
To link your web application to the Android app, you will also need to reference in your web app’s manifest.json (./src/manifest.json file) the Android package name. Replace the two instances of <PLAY_PACKAGE_NAME>
with your app’s package name.
// manifest.json
{
...
"android_package_name": "<PLAY_PACKAGE_NAME>",
"prefer_related_applications": true,
"related_applications": [
{
"id": "<PLAY_PACKAGE_NAME>",
"platform": "chromeos_play",
}
],
...
In order to publish your app to the Google Play Store and have your app be connected to the Play Store for purchasing, users need to use Digital Asset Links to validate that relationship. In our sample, this is done by publishing a Digital Asset Links JSON file at https://<project.id>.web.app/.well-known/assetlinks.json
.
Update the ./src/.well-known/assetlinks.json file by replacing <PLAY_PACKAGE_NAME>
with your app’s package name and <SIGNING_KEY_CERT>
with your signing key’s SHA-256 fingerprint. You can find the SHA-256 certificate fingerprint in “App Integrity” under the “Setup” section in the left hand menu of the Play Console or use this link and choose your developer account and then your app to be redirected.
// assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "<PLAY_PACKAGE_NAME>",
"sha256_cert_fingerprints": ["<SIGNING_KEY_CERT>"]
}
}
]
For information on Digital Asset Links, please check out the following YouTube video:
Validating your Trusted Web Activity’s Digital Asset Links.
After updating the manifest.json and assetlinks.json files, remember to re-deploy your project to update your PWA.
$ npm run deploy
Double-check the manifest and asset links at https://<project.id>.web.app/manifest.json
and https://<project.id>.web.app/.well-known/assetlinks.json
to make sure they’ve updated.
Like in our sample, it is highly recommended that you verify purchases and tokens in your backend server with the Google Play Developer API, alongside using the Digital Goods API in your PWA. There are two main configuration steps before you can use the API in your backend code.
To link your Google Cloud project in the Play Console, go to the “API access” section in the left-hand navigation general user menu (not the app menu). This is under “Settings” > “Developer account” > “API access”.
You can either “Link an existing project” or “Create a new project”. If you don’t see your existing project in the drop-down menu, first verify that the email you’ve used for your developer Play Console account is listed as an “Owner” in the API Console and that it has the Google Play Android Developer API enabled in the API Library (it may take up to a couple hours after you’ve done these for your project to be listed in the Play Console).
If you create a new project, the API will automatically be enabled and linked for you. You can always unlink and link a new Google Cloud project at any time.
Backend services authenticate through a service account to access Google Play Developer API. To set up a service account as your API access client, scroll down to the “Service accounts” section on the same “API access” page.
If you had linked an existing API project with existing service accounts, you may see your available service accounts listed (you may need to click “Refresh service accounts”). If you don’t see your expected service accounts, check the service account permissions in the Google Cloud Platform (make sure you are in the right project). Your service account should have a suitable role; for our purposes “Service Account User” is sufficient. Then go back to the Play Console and refresh service accounts to see it listed.
If you don’t have a service account, follow these instructions to create a new service account.
- Click “Create new service account”
- Follow the instructions and go to the Google Cloud Platform and click “+CREATE SERVICE ACCOUNT”.
- Fill in the details and click “Create”.
- Then complete step “2. Grant this service account access to project”. Though it is labeled optional, it isn’t in our case. We recommend adding the “Service Accounts” > “Service Account User” role.
- Step 3 is optional as labeled. Click Done to save your service account.
- Now go back to the Play Console, and click “Refresh service accounts” to see your new service account listed. Then click “Grant access” to give it permissions.
- Make sure “Manage orders and subscriptions permission” is checked.
- Click “Invite user”.
Go back to the Google Cloud Platform. You may see that for the service account we just created/used, it says “No keys” under “Key ID”.
To create a new key:
-
Under the “Actions” three-dot menu, select “Manage keys”.
-
In the window opened, click on “Add key” and choose “Create new key”.
-
Choose your preferred format and click “Create”.
-
It will then download a file that contains your private key. Store it somewhere safe and secure and don’t lose it! If you misplace it, you won’t be able to recover it but you can create another new key.
-
Use your private key to fill out the missing service account credentials in the functions/src/config.ts file.
-
Fill in the
serviceAccountEmail
const with theclient_email
from the private key file you downloaded. -
Copy the entire private key string (
private_key
) as is into theserviceAccountPrivateKey
const.// config.ts ... // service account credentials export const serviceAccountEmail = ''; export const serviceAccountPrivateKey = ''; ...
-
RTDN, which utilizes Google Pub/Sub, lets your backend server receive notifications from Google Play about any user’s entitlement changes. Specifically, RTDN is useful for subscriptions and essential if you have a cross-platform app.
To set up RTDN, first sign in to the Google API Console, and make sure you are using the same project that you linked to the Play Console earlier when configuring Google Play Developer API.
Navigate to “APIs & Services” in the left hand menu. Then click “+ ENABLE APIS AND SERVICES”, search for “Cloud Pub/Sub API” and enable it.
Then to create your Pub/Sub topic, go to the Pub/Sub topics page in the Cloud Console and click “+ CREATE TOPIC”.
Fill in the “Topic ID” and note down the full topic name for later.
Then click on “ADD MEMBER” to add google-play-developer-notifications@system.gserviceaccount.com with the “Pub/Sub Publisher” role which allows it to publish messages to the topic you’ve created.
Now that you’ve created a topic and added the Google Play developer notifications account, you’ll need to set up RTDN in your app in the Play Console. In the left-hand navigation menu, scroll down to the “Monetize” section and select “Monetization setup”. Add your topic name which we noted down earlier from the Pub/Sub console. Click “Save changes”.
In our sample, we use Firebase Cloud Functions as a serverless framework in place of a backend server to handle HTTP requests. Additionally, this is also how we receive the RTDN events and trigger corresponding events via Pub/Sub triggers. You’ll see in functions/src/index.ts
a similar piece of code:
export const rtdnListener = functions.pubsub
.topic(topicID).onPublish(async (data, context) => {
// Read and handle the incoming Realtime Developer notification
});
Earlier you added your service account credentials into the functions/src/config.ts file. Now, replace <PLAY_PACKAGE_NAME>
with your Android app package name and <RTDN_TOPIC_ID>
with your RTDN topic ID you created earlier
Note that you should just the topic ID (e.g. "play-rtdn") and not the full topic name.
// config.ts
...
// app package name
export const packageName = '<PLAY_PACKAGE_NAME>';
// RTDN pub/sub topic ID
export const topicID = '<RTDN_TOPIC_ID>';
...
If you have your own secure backend server, you should implement consuming the messages sent to your RTDN topic there, by using the Pub/Sub client libraries. For full Pub/Sub documentation, check out https://cloud.google.com/pubsub/docs/.
After updating the service account credentials and RTDN topic name, remember to re-deploy your project to update your PWA.
$ npm run deploy
Note: To resolve prettier errors, you can run the following command:
$ npm run prettier:fix
The Digital Goods API will be available on Chrome OS stable starting with version 89. In the meantime, it is possible to test the Digital Goods API by following these steps:
- Enable the Chrome OS dev channel
- Enable the following flags in Chrome by navigating to chrome://flags and searching for the flag by name.
#enable-experimental-web-platform-features
#enable-web-payments-experimental-features
#enable-debug-for-store-billing
- Install your app from the Play Store on the device.
With application licensing, you can set up a list of Gmail accounts as License Testers to test your in-app billing & subscription integration. License testers have access to test payment methods that avoid charging the testers real money for purchases. You can also use test payment methods to simulate certain situations, such as when a payment is declined.
To add license testing accounts:
- Go to “All apps” in the left navigation menu and click on “License testing” under “Settings”.
- Fill in the “Add license testers” field.
- Click “Save changes”
See Test your Google Play Billing Library integration article for more information on License Testers and various test cases for different in-app products.