Garrett Vargas

@garrettvargas

How to add In-Skill Products to your Alexa Skill

Since Amazon announced the ability to offer in-skill purchases for developers creating Alexa skills, I’ve added four pieces of premium content to two different skills. I recently published an article about why you should consider adding premium content, and in this article, I follow up with step-by-step code samples to show you exactly how you can do that via the ASK SDK.

Types of In-Skill Products

Before getting into the code, I want to review the types of in-skill products (or ISPs) that Amazon has made available for Alexa, the different ways you can surface them, and some considerations to keep in mind about when and how often you prompt your customer with reminders about your content.

There are three types of ISPs that your skill can offer:

  • Entitlements: These are products that are basically an on/off switch. They typically gate a feature within your skill — once the customer has purchased this product, they have on-going access to the feature. These are typically features like expansion packs, additional levels, or premium sound effects.
  • Subscription: A subscription provides access to a feature or content but unlike an entitlement, the customer pays for the feature on a monthly or annual basis. Once cancelled, the user loses access to the premium feature. You would typically offer a subscription for a feature that has ongoing, evolving content such as access to a library of songs or videos. You can also offer free trial periods with subscriptions.
  • Consumables: A consumable is a product that can be purchased, used, and purchased again. Examples would include extra lives or a set number of hints in a game.

Once you’ve decided which type of product you want to offer, you should think about how you are going to message that product to your customers. You can do this on the home page description for your skill, but since many customers don’t read that content thoroughly, you’re going to need to make them aware of the products you offer via your voice content. The best way to make users aware of this content and to increase your conversion rate is to do it during a natural part of the conversation, where the premium content might be useful:

  • If you’re offering an expansion pack with extra levels to a game, you may want to offer it when a returning user who has completed all built-in levels launches your skill.
  • If you are offering a different game as part of a multi-game skill, you may want to offer it when a user attempts to switch from one game to another.
  • If you are offering extra lives or hints, you may want to offer them at the point that a user has used up all of their current lives or hints as a way to be able to continue playing.
  • It’s also a best practice to have an intent that allows the customer to explore what you have to offer — responding to phrases like “What do you have available to buy?”
  • If a customer has refused an offer to purchase premium content, you should go into an exponential backoff mode — offering the content less and less frequently on subsequent uses of the skill. I recommend you don’t completely stop offering your premium content however. I have seen customers who refused an upsell ultimately accept the offer.
Selecting a new machine in Slot Machine

For the coding sample, I am going to be referring to one of the premium games that I offer for purchase in my Slot Machine skill. Slot Machine allows customers to play multiple machines, and they can say “choose a different game” to switch from one machine to another. My premium content can be presented to the user in one of three places — when they first launch the skill, when they switch to a different machine, and after continuously playing the same machine for a given amount of time.

Using the CLI to Add Your Product

In order to add an in-skill product to your Alexa skill, you have to use the ASK CLI. If you haven’t used the CLI, follow steps 1–3 from these instructions to set it up. Once you have it set up, you’ll want to clone the skill to which you want to add a premium product. Do this with the clone command

ask clone

This will provide you with a list of your Alexa skills. Use the arrow keys to select the skill you want to clone and hit enter. Cloning will copy the skill manifest, metadata, and code if you are using a Lambda function to your machine. After you have this local copy, change to the root directory of the newly cloned skill and use this command to set up your in-skill product.

ask add isp

This will present you with a list of product types you can add

List of in-skill product types you can choose (Use arrow keys)
> Consumable
Entitlement
Subscription

Select the one that you’d like to use, and accept the default template that you’re presented with. After doing this, you’ll be prompted to type in the name of your new in-skill product. Suppose you call it extragame. This will create a new file called extragame.json in an isp folder in your skill.

Open this file, and you’ll see several details that you need to fill out to describe your skill. Specifically, you’ll need to provide a name, description, icons (in 108x108 and 512x512 format), and some release details that describe the pricing and release date for your product offering. It’s also worth noting that you will need to provide a link to a privacy policy for your skill. Not to worry, you can find several examples of policies you can leverage by searching online for “Alexa skill privacy policy.”

Once you’ve filled in this information, you can deploy your skill using ask deploy which will upload the new product settings for your skill.

Checking for Product Availability

Now that you’ve uploaded a product setting, your need to add support in your code. The first thing you should do is use the MonetizationServiceClient to check which products are available when the user launches your skill. The following code shows how you can do this and save the availability of each product within your session attributes:

const ms = handlerInput.serviceClientFactory.getMonetizationServiceClient();
return ms.getInSkillProducts(event.request.locale)
.then((inSkillProductInfo) => {
let state;
attributes.paid = {};
  if (inSkillProductInfo) {                  
inSkillProductInfo.inSkillProducts.forEach((product) => {
if (product.entitled === 'ENTITLED') {
state = 'PURCHASED';
} else if (product.purchasable == 'PURCHASABLE') {
state = 'AVAILABLE';
}

if (state) {
attributes.paid[product.referenceName] = {
productId: product.productId,
state: state,
};
}
}
});
})
.catch((error) => {
// Ignore errors
});

Now within your attributes.paid object, you have a list of all products, with a state set to either PURCHASED or AVAILABLE. You can check against this object when you attempt to sell a product offer to see whether they have already purchased it, or whether it is available for them to purchase.

Setting an Upsell Response

The code to offer a product for purchase can be a little tricky. Basically, you are going to hand-off the purchase request to Alexa, exiting your user session in the process. Alexa will handle the interaction and payment flow with the customer, including offering any discounts that Amazon may be running at the time. Your code will then be called back with a new session and the result of whether the customer purchased your product or not.

TheConnections.SendRequest response to Alexa kicks off this process. Fortunately, you can pass a token as part of this response which will be returned to you as part of the new session request. I’ve found this to be handy and use this to let me know both which product I was offering (remember, I support multiple products), and which handler sent me the request (since I can make this upsell offer in multiple locations). This allows me to seamlessly drop the user back into the interaction flow. If they declined a purchase response, I know what they were attempting to do before I interrupted them with an offer, so I can drop them right back where they were. If they accepted the purchase, I also have the context so can drop them in the appropriate part of the code. For example, if the user is being sold a holiday-themed slot machine during the process of selecting a new game, I'd use a token namedholiday.select

Here’s what that code looks like:

// Create an upsell directive
const directive = {
'type': ' Connections.SendRequest’,
‘name’: ‘Upsell’,
‘payload’: {
‘InSkillProduct’: {
productId: attributes.paid[upsellProduct].productId,
},
‘upsellMessage’: 'We have a holiday themed game available for purchase, want to hear more?',
},
‘token’: 'holiday.select',
};
return handlerInput.responseBuilder
.addDirective(directive)
.withShouldEndSession(true)
.getResponse();

Note that the upsellMessage field needs to be in plaintext. Any SSML tags that you set for different voices or speech variation will be ignored when read by Alexa. From a customer interaction standpoint, what they will hear is something like this — note that after the user says yes to the upsellMessage set by your code, the interaction is handled directly by Alexa:

Alexa, select a new machine
We have a holiday themed game available for purchase, want to hear more?
Yes
This will add the Holiday Game to the list of slot machines you can play. Prime members save $0.19. Without Prime, your price is $0.99 plus tax. Would you like to buy it?
Yes
Please say your 4-digit code to confirm the purchase.

If you are handling an explicit purchase as opposed to an upsell (i.e. responding to a request like “Alexa, buy the holiday game”), then you set name in the directive to ‘Buy’ and remove the upsellMessage field. You’ll still want to set the token so you can handle the response when they return to your skill.

And don’t forget the cancellation message! Again, like the purchase case, you’ll need to support an utterance that allows the customer to refund their purchase (“Alexa, refund the holiday game”). For this message, you set name in the directive to ‘Cancel’ and again remove the upsellMessage field.

Processing the User Response

Whether the user says yes or no to your upsell request, or if there’s some other error during the purchasing flow, your skill will be invoked with a new session with a request type of Connections.Response You can then take the user to whatever the natural continuation point for them is. If they hear this message while selecting a new machine and make the purchase, I drop them into that machine selected and ready to play. If they decline the purchase, I tell them what other machines we have available so they can continue the process of selecting a new machine. If there was some sort of error during purchase, I put them into the same flow as if they declined the purchase. Alexa will handle telling them there was an error, so no need for our code to repeat that. The handler for the response looks like this:

canHandle: function(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return (request.type === 'Connections.Response');
},
handle: function(handlerInput) {
const event = handlerInput.requestEnvelope;
const attributes = handlerInput.attributesManager.getSessionAttributes();
  // options[0] will be the name of the game we were selling
// options[1] will be the intent they were sold in
const options = event.request.token.split('.');
const accepted = (event.request.payload &&
((event.request.payload.purchaseResult == 'ACCEPTED') ||
(event.request.payload.purchaseResult == 'ALREADY_PURCHASED')));
let nextAction = options[1];
  if ((event.request.name === 'Upsell') && !accepted) {
// Don't upsell them again on the next round
attributes.noUpsell = true;
}
  // Did they accept?
if (accepted) {
// If this is was a cancellation remove it
if (event.request.name === 'Cancel') {
attributes[options[0]] = undefined;
if (attributes.paid && attributes.paid[options[0]]) {
attributes.paid[options[0]].state = 'AVAILABLE';
}
} else {
// We'll auto-select
// Make sure we put it into the list of paid products as purchased
if (attributes.paid && attributes.paid[options[0]]) {
attributes.paid[options[0]].state = 'PURCHASED';
}
nextAction = 'autoselect';
}
}
  // And go to the appropriate next step
// Select, SelectYes, Spin, and Launch are intent handlers
if (nextAction === 'select') {
return Select.handle(handlerInput);
} else if (nextAction === 'autoselect') {
attributes.gameToSelect = options[0];
return SelectYes.handle(handlerInput);
} else if (nextAction === 'spin') {
return Spin.handle(handlerInput);
} else {
// Just drop them directly into a game
return Launch.handle(handlerInput);
}

You’ll notice that in addition to setting up the new machine if selected and handing off to the appropriate next intent for processing, there is code that sets a noUpsell flag if the user was given an upsell message and declined it. This is to avoid an infinite loop, since I drop the user back into the same path they were previously in. Before presenting an upsell message, my code checks whether this flag is set to avoid an infinite loop. This also helps ensure that customers are only heard an upsell message once during their session, giving them a better customer experience.

I hope you found this guide useful, and good luck adding premium content to your own Alexa skills!

More by Garrett Vargas

Topics of interest

More Related Stories