Published: December 2, 2020
Besides allowing your app to sell digital goods and subscriptions on the Play Store, Google Play Billing offers tools for managing your catalog, prices and subscriptions, useful reports, and a checkout flow powered by the Play Store that is already familiar to your users. It's a requirement for applications published on the Play Store that sell digital goods.
The Google Play Billing API has its own terminology and includes client and backend components. This section covers only a small part of the API that is specific to using the Digital Goods API and Trusted Web Activity. Make sure to read the Google Play Billing documentation and understand its concepts before integrating it into a production application.
The basic flow
To provide digital goods with the Play Store, configure your catalog on the Play Store and connect the Play Store as a payment method from your PWA.
You can do so in the Play Store interface as follows:
- Click Products in the Play Console menu. View your existing in-app products and subscriptions.
- Click Create product to add a new product.
- Add a product ID, name, description, and a price. Create meaningful and easy-to-remember product IDs, as you need them later. IDs can't be changed once created.
- If creating a subscription, you also have to specify a billing period. You can list your subscription benefits and add features, such as free trials, introductory prices, a grace period, and a resubscribe option.
- Click Activate to make the product available.
If you prefer, you can add your products with the Play Developers API.
Once your catalog is configured, the next step is to configure the checkout flow from the PWA. Use a combination of the Digital Goods API and the Payment Request API.
Fetch a product price with the Digital Goods API
When using Google Play Billing, ensure that the price displayed to users matches the price from the store listing. Manually keeping those prices in sync would be impossible, so the Digital Goods API provides a way for the web application to query the underlying payment provider for prices:
// The SKU for the product, as defined in the Play Store interface
async function populatePrice(sku) {
try {
// Check if the Digital Goods API is supported by the browser.
if (window.getDigitalGoodsService) {
// The Digital Goods API can be supported by other Payments provider.
// In this case, we're retrieving the Google Play Billing provider.
const service =
await window.getDigitalGoodsService("https://play.google.com/billing");
// Fetch product details using the `getDetails()` method.
const details = await service.getDetails([sku]);
if (details.length === 0) {
console.log(`Could not get SKU: "${sku}".`);
return false;
}
// The details contain both the price and the currenncy.
item = details[0];
const value = item.price.value;
const currency = item.price.currency;
const formattedPrice = new Intl.NumberFormat(navigator.language, {
style: 'currency', currency: currency }).format(value);
// Display the price to the user.
document.getElementById("price").innerHTML = formattedPrice;
} else {
console.error("Could not get price for SKU \"" + sku + "\".");
}
} catch (error) {
console.log(error);
}
return false;
}
You can detect support for the Digital Goods API by checking if getDigitalGoodsService()
is
available on the window
object.
Then call window.getDigitalGoodsService()
with the Google Play Billing identifier as a parameter.
This returns a service instance for Google Play Billing and other vendors can implement support
for the Digital Goods API and have different identifiers.
Finally, call getDetails()
on the reference to the Google Play Billing object passing the SKU for
the item as a parameter. The method returns a detail object containing both the price and the
currency for the item that can be displayed to the user.
Start the purchase flow
The Payment Request API enables purchase flows on the web and is also used for the Google Play Billing integration. Check out this How Payment Request API Works to learn more if you are new to the Payment Request API.
To use the API with Google Play Billing, you need to add a payment instrument,
which has a supported method called https://play.google.com/billing
.
Add the SKU as part of the data for the instrument:
const supportedInstruments = [{
supportedMethods: "https://play.google.com/billing",
data: {
sku: sku
}
}];
Then, build a PaymentRequest
object as usual and use the API as usual
const request = new PaymentRequest(supportedInstruments, details);
Acknowledge the purchase
Once the transaction is complete, use the Digital Goods API to acknowledge the
payment. The response object from the PaymentRequest
contains a token you can
use to acknowledge the transaction:
const response = await request.show();
const token = response.details.token;
const service = await window.getDigitalGoodsService("https://play.google.com/billing");
await service.acknowledge(token, 'onetime');
The Digital Goods API and the Payment Request API don't have knowledge on the user's identity. As a result, it's up to you to associate the purchase to the user in your backend and ensure they have access to the purchased items. When associating the purchase to a user, remember to save the purchase token, as you may need it to verify if the purchase has been cancelled or refunded, or if a subscription is still active. Check out the Real Time Developer Notifications API and the Google Play Developer API as they provide endpoints for handling those cases in your backend.
Check for existing entitlements
A user may have redeemed a promo code or may have an existing subscription to your product. In
order to validate that the user has the appropriate entitlements, you can call the
listPurchases()
command on the digital goods service. This returns all purchases that your
customer has made in your app. This would also be the place to acknowledge any unacknowledged
purchases to ensure that the user correctly redeems their entitlements.
const purchases = await itemService.listPurchases();
for (p of purchases) {
if (!p.acknowledged) {
await itemService.acknowledge(p.purchaseToken, 'onetime');
}
}