You can add dialog to your Action that sells your in-app products in the Google Play store, using the digital purchases API.
This guide walks you through the process of developing an Actions project that incorporates transactions for your digital goods managed through the Google Play store.
Types of digital goods
Your Actions can perform transactions for the following types of digital goods in the Google Play store:
- One-time purchases only charge the user once. Goods sold as one-time
purchases are called "managed products" by the Google Play Console, and can
be either “consumed” or “non-consumed” as follows:
- Non-consumed goods can only be purchased once per user, such as a paid upgrade for an ad-free experience or a game expansion with additional content. By default, one-time purchases are not consumed.
- Consumed goods can be purchased multiple times, such as a quantity
of in-game currency. After the item is purchased, send a
consume
request to make the item available for purchase again.
- Subscriptions automatically charge the user on a recurring schedule. These include purchases like a monthly fee for premium features.
Transaction flow
In your Action's conversation, you'll need to perform the following steps to facilitate a digital transaction:
- Gather information: Before your Action can handle a transaction, you'll
first need to see what items are currently available for the user to
purchase.
- Validate transaction requirements (optional): Check to see if the user meets the necessary conditions to purchase digital goods, to avoid a bad user expeience where the order fails at the end of the transaction.
- Check for available Play store SKUs: Query the Play store with the name of a package you own and a list of associated product IDs (SKUs), and retrieve a list of which SKUs are available for this user to purchase.
- Access the user's existing goods (optional): Reference the user's existing entitlements to see what items they've already purchased and optionally tailor their ordering experience.
- Build the order: With the list of available SKUs, present the user an interactive list of purchasable items in the form of a carousel or list rich response.
- Complete the purchase: When the user selects an item, send a purchase request to the Play store containing the SKU of that item and the ID of the user who wishes to purchase it. The user then completes the transaction using a pre-defined flow.
- Describe the purchase status: After the user completes the transaction flow, you receive a status code. The user sees a response from you that describes whether the transaction was successful or failed.
- Make the purchase repeatable (optional): If the user’s purchase was consumable, send a consume request to the Play store to make the SKU available for future purchase.
Build your project
The following sections explain how to build an Actions project that handles transactions for digital goods.
For an example conversational Action that handles digital goods transactions, review our Node.JS sample.
Setup
Before you can start developing your transactional flow, complete the following setup steps.
Review guidelines
Keep in mind that additional policies apply to Actions with transactions. It can take us a few weeks to review Actions with transactions, so factor that time in when planning your release schedule. To ease the review process, make sure you comply with the policies and guidelines for transactions before submitting your Action for review.
Actions that sell digital goods can currently only be deployed in the following countries:
Australia Brazil Canada Denmark France Indonesia Italy |
Japan Mexico Netherlands Norway Poland Russia Singapore |
Spain Sweden Thailand Turkey United Kingdom United States |
Play store setup
Since digital transactions are for selling goods that you own in the Google Play store, you'll need to have a Google Play Developer account and a payments profile.
To start selling digital goods in the Google Play store, read the Play console and payments center docs for instructions on registering for a Google Play Developer account and creating a payments profile.
Create a project
When creating your Actions project, you must specify that you want to perform transactions.
To get started, you need to perform the following steps in the Actions console :
- Click Add/import project to create a new project or use an existing project.
- Navigate to Deploy > Directory information.
- Under Additional information > Transactions, check the box that says "Do your Actions use the Digital Purchase API to perform transactions of digital goods?".
Set up a service account for the digital purchases API
To send requests to the digital purchases API, you need to download a JSON service account key associated with your Actions console project, then exchange the service account key for a bearer token that can be passed into the Authorization header of the HTTP request.
To retrieve your service account key, perform the following steps:
- Follow this link, swapping "example-project-1" for your project ID:
https://console.developers.google.com/apis/api/actions.googleapis.com/overview?project=example-project-1
- You can find your Actions project ID in the Actions console project settings by clicking the gear icon in the left navigation.
- If you see an Enable button, click it.
- Follow this link, swapping "example-project-1" for your project ID: https://console.developers.google.com/apis/credentials?project=example-project-1
- Click Credentials > Create credentials > Service account key.
- Under Service Account, click New Service Account.
- Give the service account a name like
digitaltransactions
. - Set Role to Project > Owner.
- Select the JSON key type.
- Click Create.
- A JSON service account key will be downloaded to the local machine.
In your fulfillment, you'll exchange this service key for a bearer token using
the Google APIs client library and the
https://www.googleapis.com/auth/actions.purchases.digital
scope. You can find
installation steps and examples on the API client library
GitHub page.
Link an Android app property
Any Android apps whose goods you want users to purchase need to be associated with your Actions project as a connected property.
To connect an app for digital transactions in your Actions project, perform the following steps:
- In the Actions console, navigate to Advanced Options > Brand verification.
- If you haven't connected any properties, you'll first need to connect a
website:
- Click the </> button.
- Enter the URL for the website you want to connect and click Connect. Google sends you an email confirming the request and an email to the website owner (verified in Search Console) asking them to confirm the association.
- Once you have at least one connected website, perform the following steps to
connect your Android app:
- Click Connect App.
- Follow the instructions shown.
- Enable Access Play purchases.
1. Gather information
First, your Action gathers information about the user and what items they can purchase.
1a. Validate transaction requirements (optional)
User experience
The user expresses an interest in purchasing one of your items in the Play store, such as a premium version of the current Action's experience. To prevent the user from going through a complete transaction flow when they aren't eligible for digital goods transactions, your Action first checks to make sure the user meets the transaction requirements.
Check and handle user eligibility
Before starting the transaction flow, request the DIGITAL_PURCHASE_CHECK
helper
intent to make sure the user is eligible to purchase digital goods. For example,
the user must be in a supported country.
The following code silently validates transaction requirements at the start of the conversation:
app.intent('Default Welcome Intent', async (conv, { SKU }) => {
// Immediately invoke digital purchase check intent to confirm
// purchase eligbility.
conv.ask(new DigitalPurchaseCheck());
});
The result appears in the conversation arguments as DIGITAL_PURCHASE_CHECK_RESULT
.
Check this result and either continue the transaction as normal or pivot away
depending on the value.
app.intent('Digital Purchase Check', async (conv) => {
const arg = conv.arguments.get('DIGITAL_PURCHASE_CHECK_RESULT');
if (!arg || !arg.resultType) {
conv.close('Digital Purchase check failed. Please check logs."');
return;
}
// User does not meet necessary conditions for completing a digital purchase
if (arg.resultType === 'CANNOT_PURCHASE' || arg.resultType === 'RESULT_TYPE_UNSPECIFIED') {
conv.close(`It looks like you aren't able to make digital purchases. Sorry about that.`);
return;
}
conv.ask('Welcome to Digital Goods Sample. Would you like to see what I have for sale?');
});
1b. Check for available Play store SKUs
User experience
The user wants to make a purchase, so your Action retrieves a list of your available items in the Play store for that user to buy.
Create a digital purchases API client
In order to use the digital purchases API, you need to create a JWT client using
the https://www.googleapis.com/auth/actions.purchases.digital
scope and the
service key you generated earlier:
const serviceAccount = { /* your service account info (load from env) */};
const request = require('request');
const {google} = require('googleapis');
const jwtClient = new google.auth.JWT(
serviceAccount.client_email, null, serviceAccount.private_key,
['https://www.googleapis.com/auth/actions.purchases.digital'],
null
);
Request available SKUs
Your Action needs to query the Play store to find out what items are currently available for purchase. You'll provide the package name of an app you want to query, along with the SKUs associated with that package.
You can request the list of available SKUs by sending a POST
request to the
digital purchases API:
return jwtClient.authorize((err, tokens) => {
if (err) {
throw new Error(`Auth error: ${err}`);
}
const packageName = 'com.example.projectname';
request.post(`https://actions.googleapis.com/v3/packages/${packageName}/skus:batchGet`, {
'auth': {
'bearer': tokens.access_token,
},
'json': true,
// use the ids that you want to pull info from here.
'body': {
'conversationId': conversationId,
'skuType': 'SKU_TYPE_IN_APP',
'ids': ['consumable.0', 'consumable.1', 'subscription.monthly.0']
},
}, (err, httpResponse, body) => {
if (err) {
throw new Error(`API request error: ${err}`);
}
console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`);
console.log(JSON.stringify(body));
});
});
});
The response body will contain arrays of SKU objects. Each SKU object has attributes that you'll use to present the item and submit the purchase, as shown in the following code snippet:
body = {
skus: [
skuId: {
skuType: one of "APP", "SUBSCRIPTION", or "UNSPECIFIED"
id: string,
packageName: string
}
formattedPrice: string,
title: string,
description:string,
subscriptionDetails: {
subscriptionPeriod: string,
freeTrialPeriod: string,
formattedIntroductoryPrice: string
introductoryPrice: Money
introductoryPricePeriod: string
}
]
}
1c. Reference the user's existing goods (optional)
When a user sends a request to your webhook, that request's JSON will include a list of purchases they've made for any apps you've linked to this project:
{
"user": {
"userId": "xxxx",
"locale": "en-US",
"lastSeen": "2018-02-09T01:49:23Z",
"packageEntitlements": [
{
"packageName": "com.abcd.edfg.hijk",
"entitlements": [
{
"sku": "com.abcd.edfg.hijk",
"skuType": "APP"
}
]
},
{
"packageName": "com.abcd.edfg.lmno",
"entitlements": [
{
"sku": "lmno_jun_2017",
"skuType": "SUBSCRIPTION",
"inAppDetails": {
"inAppPurchaseData": {
"autoRenewing": true,
"purchaseState": 0,
"productId": "lmno_jun_2017",
"purchaseToken": "12345",
"developerPayload": "HSUSER_IW82",
"packageName": "com.abcd.edfg.lmno",
"orderId": "GPA.233.2.32.3300783",
"purchaseTime": 1517385876421
},
"inAppDataSignature": "V+Q=="
}
}
]
}
]
},
"conversation": {
"conversationId": "1518141160297",
"type": "NEW"
},
"inputs": [
{
"intent": "actions.intent.MAIN",
"rawInputs": [
{
"inputType": "VOICE",
"query": "Talk to My Test App"
}
]
}
],
...
}
In your transactional flow, you could use this information to dynamically exclude items that the user has already purchased, or end the transaction altogether if they've already purchased every available item.
2. Build the order
With the SKUs available for purchase, your Action prompts the user to select an item.
User experience
The user is presented with a list of items they can select for purchase. On a smart speaker, they are read options and prompted to respond. If they are on a screened device, they can tap one of the items.
Build fulfillment
Use the array of SKUs to create a rich response describing the item(s) available for purchase. The code below uses a list response because its contents are simple and can support up to 30 items.
The following code adds the title and description fields of each SKU to a list that will be shown to the user:
skus.forEach((sku) => {
const key = `${sku.skuId.skuType},${sku.skuId.id}`
list.items[key] = {
title: sku.title,
description: `${sku.description} | ${sku.formattedPrice}`,
};
});
3. Complete the purchase
Your Action uses the digital purchases API to initiate a purchase with the Play store.
User experience
Once the user has selected an item, the purchase process will be initiated with the Play store. If the conversation isn't happening on the user's phone, they'll be prompted to complete the transaction there. The phone then walks the user through the rest of the transaction (selecting a payment method and purchase confirmation).
Build fulfillment
Call the CompletePurchase()
function with details about the SKU that was
picked. This function call will handle the rest of the purchase flow for you.
Assuming you used a list, the actions.intent.OPTION
event for handling the
user's item selection will contain an option
parameter describing which SKU
was picked:
app.intent('actions.intent.OPTION', (conv, params, option) => {
let [skuType, id] = option.split(',');
conv.ask(new CompletePurchase({
skuId: {
skuType: skuType,
id: id,
packageName: <PACKAGE_NAME>,
},
}));
});
4. Describe the purchase status
Once the Play store finishes the user's transaction flow, your Action will receive a status code and notify the user accordingly.
User experience
After the user confirms the purchase on their phone, they receive a response from your Action that lets them know the purchase was successful. Then, your Action ends the conversation.
Fulfillment
When a user completes their purchase, they trigger the
actions.intent.COMPLETE_PURCHASE
Actions SDK intent, or the
actions_intent_COMPLETE_PURCHASE
if you're using Dialogflow. This event
includes a purchaseStatus
field that describes the result of the user's
purchase flow. This status isn't automatically sent to the user, so your
webhook should handle each status accordingly.
Your fulfillment should handle the following statuses:
PURCHASE_STATUS_OK
: The purchase was successful. Since the transaction is complete, you should either end the conversation or exit the transactional flow.PURCHASE_STATUS_ALREADY_OWNED
: The user already owns that item, so the purchase was unsuccessful. This error status can be avoided by checking the user's existing goods and tailoring the items shown so they don't have the option to re-purchase items they already own.PURCHASE_STATUS_ITEM_UNAVAILABLE
: The requested item is not available at the time of the user's purchase. This error status can be avoided by re-checking the available SKUs closer to the time of purchase.PURCHASE_STATUS_ITEM_CHANGE_REQUESTED
: The user decided to purchase something else. The best user experience here is to reprompt with your item selection so the user can make another decision right away.PURCHASE_STATUS_USER_CANCELLED
: The user cancelled the purchase flow. Since the user prematurely exited the flow, you should ask the user if they want to retry the transaction or exit the transaction altogether.PURCHASE_STATUS_ERROR
: The transaction failed for an unknown reason. You should handle this error status by letting the user know the transaction failed, and ask if they want to try again.PURCHASE_STATUS_UNSPECIFIED
: The transaction failed for an unknown reason, resulting in an unknown status. You should handle this error status by letting the user know the transaction failed, and ask if they want to try again.
The following code reads purchaseStatus
from the arbitrary actions.intent.COMPLETE_PURCHASE
intent, which is triggered by the COMPLETE_PURCHASE
event, and handles each
status accordingly:
// This intent name is arbitrary, the actions_intent_COMPLETE_PURCHASE event
// is what matters
app.intent('actions.intent.COMPLETE_PURCHASE', (conv) => {
const arg = conv.arguments.get('COMPLETE_PURCHASE_VALUE');
console.log('User Decision: ' + JSON.stringify(arg));
if (!arg || !arg.purchaseStatus) {
conv.close('Purchase failed. Please check logs.');
return;
}
if (arg.purchaseStatus === 'PURCHASE_STATUS_OK') {
conv.close('Purchase completed! You\'re all set!');
} else if (arg.purchaseStatus === 'PURCHASE_STATUS_ALREADY_OWNED') {
conv.close('Purchase failed. You\'ve already owned the item.');
} else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_UNAVAILABLE') {
conv.close('Purchase failed. Item is not available.');
} else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_CHANGE_REQUESTED') {
// Reprompt with your item selection dialog
} else {
conv.close('Purchase Failed:' + arg.purchaseStatus);
}
});
5. Make the purchase repeatable (optional)
By default, a one-time purchase can only be completed once for a given item. If the user purchased a consumable good like a quantity of in-game currency, your Action will send a consume request to the digital purchases API to let the Play store know that this item can be purchased again by this user.
User experience
The user finishes purchasing 100 gold coins for your game, then decides to purchase more. They go through your transaction flow again, and the 100-coin SKU is again presented as a purchase option.
Fulfillment
On a successful purchase, the actions_intent_COMPLETE_PURCHASE
event includes
a purchase_token
field. You can also find this token in the user’s request
JSON under the packageEntitlements
.
If the SKU should be available for purchase more than once, send a
consume
request to the digital purchases API with the purchase token. On a
successful consume
request, the user will be able to purchase that SKU again.
The following code sends a consume
request to the digital purchases API and
reports whether the request was successful. Keep in mind that you shouldn't
show purchase consumption to the user; the following example does this
for debug purposes.
request.post(`https://actions.googleapis.com/v3/conversations/${conv.request.conversation.conversationId}/entitlement:consume`, {
'auth': {
'bearer': tokens.access_token,
},
'json': true,
'body': {
// This purchase token is in both the purchase event and the user’s entitlements
// in their request JSON
"purchaseToken": entitlement.purchaseToken
},
}, (err, httpResponse, body) => {
if (err) {
throw new Error(`API request error: ${err}`);
}
console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`);
console.log(JSON.stringify(httpResponse));
console.log(JSON.stringify(body));
resolve(body);
});
// Make sure the consume request was successful
return consumePromise.then(body => {
const consumed = Object.keys(body).length === 0;
if (consumed) {
conv.close(`You successfully consumed ${id}`);
} else {
conv.close(`Failed to consume: ${id}`);
}
});