Implement Alexa Shopping Actions in Your Skill

Your skill starts the Alexa Shopping flows by using the skill connections interface. To add the product to the customer's Amazon Shopping Cart, you invoke the AddToShoppingCart task. To purchase a given product, you invoke the BuyShoppingProducts task. Both tasks start a multi-turn interaction between Alexa and the customer to complete the shopping experience.

To use Alexa Shopping Actions in your skill, customers must enable voice purchasing in their Alexa app.

Implement the add-to-cart flow

The AddToShoppingCart action adds a recommended product to the customer's Cart. The customer can review this item later by visiting the Amazon retail website. The customer doesn't make the purchasing decision during the skill session.

Complete the following steps to implement the Alexa Shopping AddToShoppingCart action in your skill code.

Send a directive to add a product to a shopping cart

After you ask the customer for permission to shop for the product, you can send the Connections.StartConnection directive to Alexa with an AddToShoppingCart payload.

The following example shows a request to add the specified product to the cart.

Copied to clipboard.

/**
 * Handler to handle RecommendAndShopForProducts.
 */
const RecommendAndShopForProductsHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndShopForProducts');
    },
    handle(handlerInput) {
        console.log("RecommendAndShopForProducts received.");

        const speechText = "<Product intro>" +
        " Would you like to add this to your cart on Amazon?"

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
        }
};

/**
 * Handler to handle YesAndNoIntentHandler.
 */
const YesAndNoIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'AMAZON.YesIntent'
                || request.intent.name === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        const intentName = handlerInput.requestEnvelope.request.intent.name;
        console.log("User said : " + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Staging item with Amazon"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.AddToShoppingCart/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'B01962MDHA'
                     }
                   ]
                },
              'token': 'AddToShoppingCartToken'
          };

            return handlerInput.responseBuilder
            .speak(actionText)
            .addDirective(actionTask)
            .getResponse();
        }

        const speechText = "All right. Please come back if you need more product recommendations."

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
        }
};

Handle a session resumed request

Your skill receives the result of a purchase as a SessionResumedRequest. The result includes the token given in the AddToShoppingCart request. Implement a handler to receive the SessionResumedRequest response. Resume the skill based on the result included in the payload. You can use the token to load the stored skill session, alter the skill experience, and handle errors.

The following example shows how to handle a session resumed request response.

Copied to clipboard.

/**
 * Handler to handle SessionResumedRequest.
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // Session attributes and ID are same as previous IntentRequest
        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        console.log("sessionAttributes : " + JSON.stringify(sessionAttributes));
        const token = handlerInput.requestEnvelope.request.cause.token;

        let request = handlerInput.requestEnvelope.request;
        let speechText = "Sorry, I had trouble doing what you asked. Please try again.";

        if (request.cause) {
             const token = request.cause.token;
             const status = request.cause.status;
             const code = status.code;
             const message = status.message;
             const payload = request.cause.result;

             console.info(`[Shopping Response] ${JSON.stringify(request)}`);
             console.info(`[INFO] Shopping Action Result: Code - ${code}, Message - ${message}, Payload - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "Looks like there was an issue. Let's get back to the skill.";
                         } else {
                             speechText = "I'm sorry, shopping indicated an issue while performing your request. Please try again later.";
                             console.info(`[INFO] Shopping Action had an issue while performing the request. ${payload.message}`);
                        }
                    } else if (token === "AddToShoppingCartToken") {
                        console.info(`[INFO] Shopping Action: Add to cart action was a success for ${token}.`);
                        speechText = "Thank you for adding the product to cart.";
                    }
            break;
            default :
                console.info(`[INFO] Shopping Action: There was a problem performing the shopping action.`);
                speechText = "There was a problem adding the item to your cart.";
            }
        }

        return handlerInput.responseBuilder.speak(speechText).getResponse();
    },
};

Implement the purchase flow

The BuyShoppingProducts action helps the customer purchase a given product. The action takes the customer through a multi-turn interaction that presents the product and final price, and asks for confirmation to purchase the product. The purchasing workflow incorporates various checks, including valid payment method, delivery address, and voice PIN authorization associated with the customer account.

To use the BuyShoppingProducts action inside your skill, the customer must have a default 1-click setting enabled on their Amazon account. If the customer doesn't have this option enabled, Alexa Shopping informs the customer to enable the required feature, and then returns the appropriate information in a response with status code InvalidCustomerSettings.

Complete the following steps to implement the Alexa Shopping BuyShoppingProducts action in your skill code.

Send a directive to buy a product

After your skill receives the permission from the customer to shop for the product, you send the Connections.StartConnection directive to Alexa with a BuyShoppingProducts payload.

The follow example shows a request to start the product purchase flow.

Copied to clipboard.

/**
 * Handler to handle RecommendAndShopForProducts.
 */
const RecommendAndShopForProductsHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndShopForProducts');
    },
    handle(handlerInput) {
        console.log("RecommendAndShopForProducts received.");

        const speechText = "<Product intro>" +
        " Would you like to know how to purchase this on Amazon?"

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
        }
};

/**
 * Handler to handle YesAndNoIntentHandler.
 */
const YesAndNoIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'AMAZON.YesIntent'
                || request.intent.name === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        const intentName = handlerInput.requestEnvelope.request.intent.name;
        console.log("User said : " + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Staging item with Amazon"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.BuyShoppingProducts/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'B01962MDHA'
                     }
                   ]
                },
              'token': 'PurchaseProductToken'
          };

            return handlerInput.responseBuilder
            .speak(actionText)
            .addDirective(actionTask)
            .getResponse();
        }

        const speechText = "All right. Please come back if " +
                              "you need more product recommendations."

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
        }
};

Handle a session resumed request

Your skill receives the result of a purchase as a SessionResumedRequest. The result includes the token given in the BuyShoppingProducts request. Implement a handler to receive the SessionResumedRequest response. Resume the skill based on the result included in the payload. You can use the token to load the stored skill session, alter the skill experience, and handle errors.

The following example shows how to handle a session resumed request response.

Copied to clipboard.

/**
 * Handler to handle SessionResumedRequest.
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // Session attributes and ID are same as previous IntentRequest
        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        console.log("sessionAttributes : " + JSON.stringify(sessionAttributes));
        const token = handlerInput.requestEnvelope.request.cause.token;

        let request = handlerInput.requestEnvelope.request;
        let speechText = "Sorry, I had trouble doing what you asked. Please try again.";

         if (request.cause) {
             const token = request.cause.token;
             const status = request.cause.status;
             const code = status.code;
             const message = status.message;
             const payload = request.cause.result;

             console.info(`[Shopping Response] ${JSON.stringify(request)}`);

             console.info(`[INFO] Shopping Action Result: Code - ${code}, Message - ${message}, Payload - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "Looks like there was an issue. Let's get back to the skill.";
                         } else {
                             speechText = "I'm sorry, shopping indicated an issue while performing your request. Please try again later.";
                             console.info(`[INFO] Shopping Action had an issue while performing the request. ${payload.message}`);
                        }
                     } else if (token === "PurchaseProductToken") {
                             console.info(`[INFO] Shopping Action: Buy action was a success for ${token}.`);
                             speechText = "Thank you for purchasing this product.";
                         }
                    break;
            default :
                console.info(`[INFO] Shopping Action: There was a problem performing the shopping action.`);
                speechText = "There was a problem purchasing this product.";
            }
        }

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
    },
};

Handle a refund request

To handle refund requests, direct the customer to review the order in their Amazon account. No Alexa Shopping Actions task exists to process a refund.

Copied to clipboard.

 const RefundHandler = {
     canHandle(handlerInput) {
         return util.parseIntent(handlerInput) === 'RefundIntent';
     },
     handle(handlerInput) {

         return handlerInput.responseBuilder
         .speak("Please go to the orders section of your Amazon account to review or cancel your order.")
         .getResponse();
     },
 }