スキルにAlexaショッピングアクションを実装する

スキルにAlexaショッピングアクションを実装する

スキルは、Skill Connectionsインターフェースを使用してAlexaショッピングフローを開始し、購入の対話をAlexaに引き渡します。Alexaショッピングタスクが、ショッピングエクスペリエンスを完了するには、Alexaとユーザーの間でマルチターンの対話を始めます。タスクが完了すると、Alexaはスキルを呼び出してセッションを再開します。

以下の手順に従って、Alexaへの引き渡しを実装し、セッション再開リクエストを処理します。Alexaショッピングの概要については、AlexaスキルへのAlexaショッピングアクションの追加を参照してください。

商品ID

Alexaショッピングアクションは、Amazon内で一意の商品を表す共通のProductEntityオブジェクトに対して動作します。APIを呼び出す際、Amazon Standard Identification Number(ASIN)を指定することで商品を識別します。ASINは、英数字10文字の一意IDで、Amazon内の商品を識別するためにAmazonによって割り当てられています。

商品のASINは、商品詳細ページの次の2か所で確認できます。

  1. <ドメイン名>/dp/<ASIN>の形式で表記される商品詳細ページのURL
  2. 商品情報セクション

カートへの追加フローを実装する

スキルがAMAZON.AddToShoppingCartアクションを呼び出すと、Alexaはお勧めされた商品の購入の詳細を提示し、ユーザーのカートに追加するかどうかの確認を求めます。ユーザーは、後からAmazonリテールウェブサイトを開いて、この商品を確認できます。ユーザーが、スキルセッション中に購入の意思決定を行うことはありません。

以下の手順に従って、スキルコードにAlexaショッピングのAMAZON.AddToShoppingCartアクションを実装します。

カートに商品を追加するディレクティブを送信する

ユーザーから商品をカートに追加する許可を得たら、AMAZON.AddToShoppingCartペイロードを含むConnections.StartConnectionディレクティブをAlexaに送信できます。

以下は、指定された商品をカートに追加するリクエストの例です。

クリップボードにコピーされました。

/**
 * RecommendAndAddToCartを処理するハンドラー
 */
const RecommendAndAddToCartHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToCart');
    },
    handle(handlerInput) {
        console.log("AmazoRecommendAndAddToCartreceived.");

        const speechText = "<商品の紹介>" +
        " この商品をAmazonのカートに追加しますか?"

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

/**
 * 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("ユーザー発話:" + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Amazonで商品を準備しています"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.AddToShoppingCart/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                   ]
                },
              'token': 'AddToShoppingCartToken'
          };

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

        const speechText = "わかりました。またご利用ください。"

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

セッション再開リクエストを処理する

スキルは、AMAZON.AddToShoppingCartリクエストの結果をSessionResumedRequest応答として受け取ります。応答を受け取るハンドラーを実装します。

応答には、AMAZON.AddToShoppingCartリクエストで指定されたtokenが含まれます。tokenの値を使用して、アクションを呼び出す前のスキルセッションを復元します。ペイロードに含まれるresultに基づいてスキルを再開します。resultを使用して、スキルエクスペリエンスを変更し、エラーを処理できます。

以下は、セッション再開リクエスト応答を処理する方法の例です。

クリップボードにコピーされました。

/**
 * SessionResumedRequestを処理するハンドラー。
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // セッションアトリビュートとIDは、前回の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 = "すみません。リクエストに対応できませんでした。もう一度試してみてください。";

        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] ショッピングアクションの結果: コード - ${code}、メッセージ - ${message}、ペイロード - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "問題が発生しました。スキルに戻ります。";
                         } else {
                             speechText = "すみません。ショッピングでリクエストの処理中に問題が発生しました。後でもう一度試してください。";
                             console.info(`[INFO] ショッピングアクションでリクエストの処理中に問題が発生しました。 ${payload.message}`);
                        }
                    } else if (token === "AddToShoppingCartToken") {
                        console.info(`[INFO] ショッピングアクション: カートへの追加アクションは成功しました。トークン:${token}。`);
                        speechText = "商品をカートに追加しました。";
                    }
            break;
            default :
                console.info(`[INFO] ショッピングアクション: ショッピングアクションの実行中に問題が発生しました。`);
                speechText = "商品をカートに追加できませんでした。";
            }
        }

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

リストへの追加フローを実装する

スキルがAMAZON.AddToListアクションを呼び出すと、Alexaはお勧めされた商品の詳細を提示し、ユーザーのAmazonほしい物リストに追加するかどうかの確認を求めます。ユーザーは、後からAmazonリテールウェブサイトを開いて、この商品を確認できます。ユーザーは、支払い方法やお届け先住所といった設定を完了していなくても、商品をほしい物リストに追加することができます。

以下の手順に従って、スキルコードにAlexaショッピングのAMAZON.AddToListアクションを実装します。

リストに商品を追加するディレクティブを送信する

ユーザーから商品をほしい物リストに追加する許可を得たら、AMAZON.AddToListペイロードを含むConnections.StartConnectionディレクティブをAlexaに送信できます。

以下は、指定された商品をリストに追加するリクエストの例です。

クリップボードにコピーされました。

/**
 * RecommendAndAddToListを処理するハンドラー
 */
const RecommendAndAddToListHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToList');
    },
    handle(handlerInput) {
        console.log("AmazoRecommendAndAddToCartreceived.");

        const speechText = "<商品の紹介>" +
        " この商品をAmazonのほしい物リストに追加しますか?"

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

/**
 * 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("ユーザー発話:" + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Amazonで商品を準備しています"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.AddToList/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                 ],
                 "listType": "WISHLIST"
                },
              'token': 'AddToListToken'
          };

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

        const speechText = "わかりました。またご利用ください。"

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

セッション再開リクエストを処理する

スキルは、AMAZON.AddToListリクエストの結果をSessionResumedRequest応答として受け取ります。応答を受け取るハンドラーを実装します。

応答には、AMAZON.AddToListリクエストで指定されたtokenが含まれます。tokenの値を使用して、アクションを呼び出す前のスキルセッションを復元します。ペイロードに含まれるresultに基づいてスキルを再開します。resultを使用して、スキルエクスペリエンスを変更し、エラーを処理できます。

以下は、セッション再開リクエスト応答を処理する方法の例です。

クリップボードにコピーされました。

/**
 * SessionResumedRequestを処理するハンドラー。
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // セッションアトリビュートとIDは、前回の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 = "すみません。リクエストに対応できませんでした。もう一度試してみてください。";

        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(`[Add to List Response] ${JSON.stringify(request)}`);
             console.info(`[INFO] ショッピングアクションの結果: コード - ${code}、メッセージ - ${message}、ペイロード - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "問題が発生しました。スキルに戻ります。";
                         } else {
                             speechText = "すみません。ショッピングでリクエストの処理中に問題が発生しました。後でもう一度試してください。";
                             console.info(`[INFO] ショッピングアクションでリクエストの処理中に問題が発生しました。 ${payload.message}`);
                        }
                    } else if (token === "AddToListToken") {
                        console.info(`[INFO] ショッピングアクション: リストへの追加アクションは成功しました。トークン:${token}。`);
                        speechText = "商品をほしい物リストに追加しました。";
                    }
            break;
            default :
                console.info(`[INFO] ショッピングアクション: ショッピングアクションの実行中に問題が発生しました。`);
                speechText = "商品をほしい物リストに追加できませんでした。";
            }
        }

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

購入フローを実装する

AMAZON.BuyShoppingProductsアクションを使うと、ユーザーはお勧めされた商品を購入できます。このアクションを呼び出すと、Alexaはユーザーとのマルチターンの対話を開始して、商品と最終価格を提示し、商品の購入の確認を求めます。購入ワークフローに、ユーザーのアカウントに登録された有効な支払い方法、お届け先住所、音声によるPIN認証といった、さまざまなチェックを組み込みます。

Alexaスキルで商品を購入するには、ユーザーがAmazonアカウントでデフォルトの1-Click設定を有効にする必要があります。ユーザーがこのオプションを有効にしていない場合、Alexaショッピングはユーザーに必要な設定を有効にするよう伝え、InvalidCustomerSettingsのステータスコードを含む応答でスキルに結果を返します。

以下の手順に従って、スキルコードにAlexaショッピングのAMAZON.BuyShoppingProductsアクションを実装します。

商品を購入するディレクティブを送信する

スキルがユーザーから商品購入の許可を得たら、AMAZON.BuyShoppingProductsペイロードを含むConnections.StartConnectionディレクティブをAlexaに送信します。

以下は、商品購入フローを開始するリクエストの例です。

クリップボードにコピーされました。

/**
 * RecommendAndAddToListを処理するハンドラー
 */
const RecommendAndAddToListHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToList');
    },
    handle(handlerInput) {
        console.log("RecommendAndAddToList received.");

        const speechText = "<商品の紹介>" +
        " この商品をAmazonで購入する方法を知りたいですか?"

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

/**
 * 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("ユーザー発話:" + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Amazonで商品を準備しています"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.BuyShoppingProducts/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                   ]
                },
              'token': 'PurchaseProductToken'
          };

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

        const speechText = "わかりました。" +
                              "またご利用ください。"

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

セッション再開リクエストを処理する

スキルは、SessionResumedRequestとして購入結果を受け取ります。応答を受け取るハンドラーを実装します。

結果には、AMAZON.BuyShoppingProductsリクエストで指定されたtokenが含まれます。tokenの値を使用して、アクションを呼び出す前のスキルセッションを復元します。ペイロードに含まれるresultに基づいてスキルを再開します。

アクションが失敗すると、Alexaは、ユーザーにエラーと実行可能な復旧メカニズムを伝えます。Alexaショッピングアクションは、スキルへの応答のペイロード内にエラーを返します。ユーザーにエラーについて伝えないでください。ただし、resultを使ってスキルの実行フローを変更してください。

以下は、セッション再開リクエスト応答を処理する方法の例です。

クリップボードにコピーされました。

/**
 * SessionResumedRequestを処理するハンドラー。
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // セッションアトリビュートとIDは、前回の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 = "すみません。リクエストに対応できませんでした。もう一度試してみてください。";

         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] ショッピングアクションの結果: コード - ${code}、メッセージ - ${message}、ペイロード - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "問題が発生しました。スキルに戻ります。";
                         } else {
                             speechText = "すみません。ショッピングでリクエストの処理中に問題が発生しました。後でもう一度試してください。";
                             console.info(`[INFO] ショッピングアクションでリクエストの処理中に問題が発生しました。 ${payload.message}`);
                        }
                     } else if (token === "PurchaseProductToken") {
                             console.info(`[INFO] ショッピングアクション: 購入アクションは成功しました。トークン:${token}.`);
                             speechText = "商品を購入しました。ありがとうございます。";
                         }
                    break;
            default :
                console.info(`[INFO] ショッピングアクション: ショッピングアクションの実行中に問題が発生しました。`);
                speechText = "この商品を購入できませんでした。スキルに戻ります。";
            }
        }

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

返金リクエストを処理する

返金リクエストを処理するには、ユーザーにAmazonアカウントの注文を確認するよう案内します。返金を処理するAlexaショッピングアクションはありません。

クリップボードにコピーされました。

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

         return handlerInput.responseBuilder
         .speak("Amazonアカウントの注文セクションで注文の確認またはキャンセルを行ってください。")
         .getResponse();
     },
 }

商品のお勧めフローを実装する

スキルがAMAZON.RecommendShoppingProductsアクションを呼び出すと、Alexaはお勧めされた商品の詳細を提示します。その後、ユーザーは1つ以上の商品をカートに追加したり、商品を購入したり、スキルのセッションを続行したりすることができます。ユーザーは、支払い方法やお届け先住所といった設定を完了していなくても、商品をカートに追加することができます。

以下の手順に従って、スキルコードにAlexaショッピングのAMAZON.RecommendShoppingProductsアクションを実装します。

商品をお勧めするディレクティブを送信する

ユーザーからお勧め商品を詳しく紹介する許可を得たら、AMAZON.RecommendShoppingProductsペイロードを含むConnections.StartConnectionディレクティブをAlexaに送信できます。ここでは、エラーの場合のみ応答をリクエストします。

以下は、指定された商品をリストに追加するリクエストの例です。

クリップボードにコピーされました。

/**
* RecommendProductsを処理するハンドラー
*/
const RecommendProductsHandler = {
     canHandle(handlerInput) {
         const request = handlerInput.requestEnvelope.request;
         return request.type === 'IntentRequest'
             && (request.intent.name === 'RecommendProducts');
     },
     handle(handlerInput) {
         console.log("RecommendProducts received.");

         const speechText = "<商品の紹介>" +
         "詳しく知りたいですか?"

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

/**
* 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("ユーザー発話:" + intentName);

         if (intentName === 'AMAZON.YesIntent') {
             var actionText = "商品のお勧めをAlexaに転送します"
             let actionTask = {
               'type': 'Connections.StartConnection',
               'uri': 'connection://AMAZON.AddToList/1',
               'input': {
                    'products' : [
                      {
                        'asin' : 'ASN1'
                    },
                    {
                      'asin' : 'ASN2'
                    }
                  ]
                 },
               'token': 'RecommendProducts',
               'onCompletion': 'SEND_ERRORS_ONLY'
           };

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

         const speechText = "わかりました。またご利用ください。"

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

セッションエラーを処理する

Alexaは、Skill Connectionsが失敗した場合にのみ応答を送信します。スキルは、応答をSystem.ExceptionEncountered応答として受け取ります。詳しくは、System.ExceptionEncounteredを参照してください。エラーが発生したかどうかを知りたい場合、応答を受け取るハンドラーを実装します。

以下は、System.ExceptionEncountered応答を受け取る方法の例です。

クリップボードにコピーされました。

/**
* ExceptionEncounteredを処理するハンドラー
*/
const SystemExceptionEncounteredHandler = {
   canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'System.ExceptionEncountered';
   },
   handle(handlerInput) {
      console.dir(handlerInput.requestEnvelope.request);
      return handlerInput.responseBuilder
         .getResponse();
   },
};