Managing In-Skill Purchases
The ASK SDK for Java includes a service client and helper classes for discovering available and purchased in-skill products and for initiating purchase and cancellation requests from within a skill.
In-Skill Purchase Service
The ASK SDK for Java provides a MonetizationServiceClient
that invokes inSkillProducts API to retrieve all in-skill products associated with the current skill along with indications if each in-skill product is purchasable and/or already purchased by the current customer. The following methods are provided:
getInSkillProducts(String acceptLanguage, String purchasable, String entitled, String productType, String nextToken, BigDecimal maxResults)
getInSkillProduct(String acceptLanguage, String productId)
getInSkillProductsTransactions(String acceptLanguage, String productId, String status, OffsetDateTime fromLastModifiedTime, OffsetDateTime toLastModifiedTime, String nextToken, BigDecimal maxResults)
getVoicePurchaseSetting()
acceptLanguage
can be retrieved from the request atinput.getRequestEnvelope().getRequest().getLocale()
.purchasable
can be provided asnull
to retrieve all in-skill products and as PURCHASABLE or NOT_PURCHASABLE to filter the response on purchasability.entitled
can be provided asnull
to retrieve all in-skill products and asENTITLED
orNOT_ENTITLED
to filter the response on entitlement status.productType
can be provided asnull
to retrieve in-skill products of all types or asENTITLEMENT
,CONSUMABLE
, orSUBSCRIPTION
to filter by product type.nextToken
is required for paginated queries.maxResults
allows skills to control records retrieved per API call. The default page size is 50 records.productId
specifies the in-skill product to be retrieved.status
can bePENDING_APPROVAL_BY_PARENT
,APPROVED_BY_PARENT
,DENIED_BY_PARENT
,EXPIRED_NO_ACTION_BY_PARENT
, orERROR
to filter the response on transaction status. Astatus
value ofnull
retrieves all transactions.fromLastModifiedTime
andtoLastModifiedTime
can be provided in UTC ISO 8601 format to filter transactions based on the last-modified timestamp. Example:yyyy-MM-ddTHH:mm:ss.SSSZ
. A value ofnull
retrieves all transactions.
getInSkillProducts()
The getInSkillProducts
method retrieves all associated in-skill products for the current skill along with purchasability and entitlement status for each product for the current skill and customer.
import com.amazon.ask.model.services.monetization.MonetizationServiceClient;
import com.amazon.ask.model.services.ServiceException;
import com.amazon.ask.model.services.monetization.InSkillProduct;
import com.amazon.ask.model.services.monetization.InSkillProductsResponse;
@Override
public Optional<Response> handle(HandlerInput input) {
try {
MonetizationServiceClient client = input.getServiceClientFactory().getMonetizationService();
String acceptLanguage = input.getRequestEnvelope().getRequest().getLocale();
// To fetch all products
InSkillProductsResponse responseProducts = client.getInSkillProducts(acceptLanguage, null, null, null, null, null);
System.out.println("There are " + responseProducts.getInSkillProducts().size() + " buyable products");
}
catch (ServiceException e) {
System.out.println("Exception occurred in calling getInSkillProducts API. Error code: " + e.getStatusCode());
}
}
The API response contains an array of in-skill product records.
{
"inSkillProducts":[
{
"productId": "amzn1.adg.product....",
"referenceName": "<Product Reference Name as defined by the developer>",
"type": "SUBSCRIPTION", // Or ENTITLEMENT
"name": "<locale specific product name as defined by the developer>",
"summary": "<locale specific product summary, as provided by the developer>",
"entitled": "ENTITLED", // Or NOT_ENTITLED
"purchasable": "PURCHASABLE", // Or NOT_PURCHASABLE
"purchaseMode": "TEST" // Or LIVE
"activeEntitlementCount": 1
}
],
"isTruncated": true,
"nextToken": "string"
}
getInSkillProduct()
The getInSkillProduct
method retrieves the product record for a single in-skill product identified by a productId
.
import com.amazon.ask.model.services.monetization.MonetizationServiceClient;
import com.amazon.ask.model.services.ServiceException;
import com.amazon.ask.model.services.monetization.InSkillProduct;
import com.amazon.ask.model.services.monetization.InSkillProductsResponse;
@Override
public Optional<Response> handle(HandlerInput input) {
try {
MonetizationServiceClient client = input.getServiceClientFactory().getMonetizationService();
String acceptLanguage = input.getRequestEnvelope().getRequest().getLocale();
// To fetch a specific in-skill product by product Id
String productId = "<your product id with the format 'amzn1.adg.product....'>";
InSkillProduct responseProduct = client.getInSkillProduct(acceptLanguage, productId);
if(responseProduct!=null) {
System.out.println("Found the product with ID" + productId);
}
}
catch (ServiceException e) {
System.out.println("Exception occurred in calling getInSkillProduct API. Error code: " + e.getStatusCode());
}
}
The API response contains a single in-skill product record.
{
"productId": "amzn1.adg.product....",
"referenceName": "<Product Reference Name as defined by the developer>",
"type": "SUBSCRIPTION", // Or ENTITLEMENT
"name": "<locale specific product name as defined by the developer>",
"summary": "<locale specific product summary, as provided by the developer>",
"entitled": "ENTITLED", // Or NOT_ENTITLED
"purchasable": "PURCHASABLE", // Or NOT_PURCHASABLE
"purchaseMode": "TEST" // Or LIVE
"activeEntitlementCount": 1
}
More information on these APIs and their usage for skill implementation is available here: Add In-Skill Purchases to a Custom Skill.
getInSkillProductsTransactions()
The getInSkillProductsTransactions
method provides information about in-skill product transactions, such as if a previous purchase request was denied. You can query transactions by transaction status or date. This API only returns recent results for transactions in kid skills.
import com.amazon.ask.model.services.monetization.MonetizationServiceClient;
import com.amazon.ask.model.services.ServiceException;
import com.amazon.ask.model.services.monetization.InSkillProductTransactionsResponse;
import com.amazon.ask.model.services.monetization.Status;
@Override
public Optional<Response> handle(HandlerInput input) {
try {
MonetizationServiceClient client = input.getServiceClientFactory().getMonetizationService();
String locale = input.getRequestEnvelope().getRequest().getLocale();
// To fetch all transactions
InSkillProductTransactionsResponse responseProducts = client.getInSkillProductsTransactions(locale, null, null, null, null, null, null);
int totalTransaction = responseProducts.getResults().size();
int pendingTransactions = responseProducts.getResults().stream().filter(record -> record.getStatus().equals(Status.PENDING_APPROVAL_BY_PARENT)).collect(Collectors.toList()).size();
int approvedTransactions = responseProducts.getResults().stream().filter(record -> record.getStatus().equals(Status.APPROVED_BY_PARENT)).collect(Collectors.toList()).size();
int deniedTransactions = responseProducts.getResults().stream().filter(record -> record.getStatus().equals(Status.DENIED_BY_PARENT)).collect(Collectors.toList()).size();
System.out.println("Found total " + totalTransaction + " transaction of which " + pendingTransactions + " are pending, " + approvedTransactions + " are approved and " + deniedTransactions + " are denied.");
} catch (ServiceException e) {
System.out.println("Exception occurred in calling getInSkillProductsTransactions API. Error code: " + e.getStatusCode());
}
}
A successful response contains a list of transactions sorted by lastModifiedTime
. The latest transaction is at index 0. By default, the list truncates to 50 transactions.
{
"results": [
{
"status": "PENDING_APPROVAL_BY_PARENT",
"productId": "amzn1.adg.product.unique-id-4",
"createdTime": "2018-11-23T12:23:10.52Z",
"lastModifiedTime": "2018-11-23T12:23:10.52Z"
},
{
"status": "PENDING_APPROVAL_BY_PARENT",
"productId": "amzn1.adg.product.unique-id-3",
"createdTime": "2018-11-23T11:21:10.52Z",
"lastModifiedTime": "2018-11-23T11:21:10.52Z"
},
{
"status": "EXPIRED_NO_ACTION_BY_PARENT",
"productId": "amzn1.adg.product.unique-id-3",
"createdTime": "2018-11-20T12:23:10.52Z",
"lastModifiedTime": "2018-11-21T12:23:10.52Z"
},
{
"status": "DENIED_BY_PARENT",
"productId": "amzn1.adg.product.unique-id-2",
"createdTime": "2018-11-15T11:03:07.52Z",
"lastModifiedTime": "2018-11-15T11:03:08.52Z"
},
{
"status": "APPROVED_BY_PARENT",
"productId": "amzn1.adg.product.unique-id-1",
"createdTime": "2018-11-14T22:21:00.52Z",
"lastModifiedTime": "2018-11-14T22:22:00.52Z"
}
],
"metadata": {
"resultSet": {
"nextToken": "EXAMPLE123456789ABCDEFGHI"
}
}
}
getVoicePurchaseSetting()
The getVoicePurchaseSetting
method gets the purchase control setting that the account holder chose in the Alexa app.
import com.amazon.ask.model.services.monetization.MonetizationServiceClient;
import com.amazon.ask.model.services.ServiceException;
@Override
public Optional<Response> handle(HandlerInput input) {
try {
MonetizationServiceClient client = input.getServiceClientFactory().getMonetizationService();
Boolean responseProduct = client.getVoicePurchaseSetting();
System.out.println("Customer's purchase preference value is " + responseProduct);
} catch (ServiceException e) {
System.out.println("Exception occurred in calling getVoicePurchaseSetting API. Error code: " + e.getStatusCode());
}
}
The API response contains a Boolean value. The value is true
if the account holder has enabled voice purchasing in the Alexa app.
For more information about this API, see Build Premium Experiences for Kid Skills in the US.
In-Skill Purchase Interface
The ASK SDK for Java provides the SendRequestDirective
for skills to initiate in-skill purchase and cancellation requests through Alexa. Amazon systems then manage the voice interaction with customers, handle the purchase transaction and return a status response back to the requesting skill. Three different actions
are supported using this interface:
Upsell
Buy
Cancel
More details about these actions
and recommended use cases is available here: Add In-Skill Purchases to a Custom Skill.
Upsell
Skills should initiate the Upsell action to present an in-skill contextually when the user did not explicitly ask for it. E.g. During or after the free content has been served. A productId
and upsell message is required to initiate the Upsell action. The upsell message allows developers to specify how Alexa can present the in-skill product to the user before presenting the pricing offer.
// Additional include in your handler source file
import com.amazon.ask.model.interfaces.connections.SendRequestDirective;
// Prepare the directive payload
Map<String,Object> mapObject = new HashMap<String,Object>();
Map<String, Object> inskillProduct = new HashMap<>();
inskillProduct.put("productId", "< your product id in the format amzn1.adg.product....>"); // Replace productId with your productId
mapObject.put("upsellMessage","Will you like to buy this product?");
mapObject.put("InSkillProduct", inskillProduct);
// Prepare the directive request
SendRequestDirective directive = SendRequestDirective.builder()
.withPayload(mapObject)
.withName("Upsell")
.withToken("correlationToken")
.build();
Optional<Response> response = input.getResponseBuilder()
.addDirective(directive)
.withShouldEndSession(true)
.build();
// Return directive from Skill context to trigger the action request
return response;
Buy
Skills should initiate the Buy action when a customer asks to buy a specific in-skill product. A productId
is required to initiate the Buy action.
// Additional include in your handler source
import com.amazon.ask.model.interfaces.connections.SendRequestDirective;
// Prepare the directive payload
Map<String,Object> mapObject = new HashMap<String,Object>();
Map<String, Object> inskillProduct = new HashMap<>();
inskillProduct.put("productId", "< your product id in the format amzn1.adg.product....>"); // Replace productId with your productId
mapObject.put("InSkillProduct", inskillProduct);
// Prepare the directive request
SendRequestDirective directive = SendRequestDirective.builder()
.withPayload(mapObject)
.withName("Buy")
.withToken("sometoken")
.build();
Optional<Response> response = input.getResponseBuilder()
.addDirective(directive)
.withShouldEndSession(true)
.build();
// Return directive from Skill context to trigger the action request
return response;
Cancel
Skills should initiate the Cancel action when a customer asks to cancel an existing entitlement or Subscription for a supported in-skill product. A productId
is required to initiate the Cancel action.
// Additional include in your handler source
import com.amazon.ask.model.interfaces.connections.SendRequestDirective;
// Prepare the directive payload
Map<String,Object> mapObject = new HashMap<String,Object>();
Map<String, Object> inskillProduct = new HashMap<>();
inskillProduct.put("productId", "< your product id in the format amzn1.adg.product....>"); // Replace productId with your productId
mapObject.put("InSkillProduct", inskillProduct);
// Prepare the directive request
SendRequestDirective directive = SendRequestDirective.builder()
.withPayload(mapObject)
.withName("Cancel")
.withToken("sometoken")
.build();
Optional<Response> response = input.getResponseBuilder()
.addDirective(directive)
.withShouldEndSession(true)
.build();
// Return directive from Skill context to trigger the action request
return response;
Last updated: Nov 28, 2023