音声によるアカウントリンクの実装について(LWA)

音声によるアカウントリンクの実装について(LWA)

スキルが音声によるアカウントリンクをサポートする場合、Alexaとの以下の対話をスキルで処理する必要があります。

フローのどの時点でスキルがこれらのタスクを実行する必要があるかを確認するには、音声によるアカウントリンクフローを参照してください。実装の手順は、チュートリアル: 音声によるアカウントリンクを実装する(LWA)を参照してください。

音声によるアカウントリンク開始タスクをリクエストするハンドラーを実装する

音声によるアカウントリンクフローのステップ1では、スキルがSkill Connectionsを使って、Amazonが所有する音声によるアカウントリンクスキルにタスクを実行するようリクエストします。この場合、Amazonが所有する音声によるアカウントリンクスキルが実行するタスク(AMAZON.AskForAccountLinking)は、ユーザーのアカウントをリンクすることです。

Skill Connectionsの観点では、スキルがリクエスターであり、Amazonが所有する音声によるアカウントリンクスキルがプロバイダーとなります。

まず、アカウントリンクフローを開始するタイミングを判断する必要があります。次に、Amazonが所有する音声によるアカウントリンクスキルに制御を渡すには、以下の例のように、スキルがConnections.StartConnectionディレクティブを返すハンドラーを実装する必要があります。RESUME_SESSIONフラグは、Alexaがタスクの完了時に制御をスキルに戻すことを意味します。詳細については、プロバイダースキルのタスクをリクエストするを参照してください。

const LinkMyAccountRequestHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest'
      && request.intent.name === 'LinkMyAccount';
  },
  handle(handlerInput) {
    var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;
    if (accessToken == undefined){
       //アカウントはリンクされていません
        return handlerInput.responseBuilder
        .speak(<value_prompt>)
        .addDirective({
            'type': 'Connections.StartConnection',
            'uri': 'connection://AMAZON.AskForAccountLinking/1',
            'onCompletion': 'RESUME_SESSION',
            'input': {
            '@version': '1',
            '@type': 'AskForAccountLinking'
            },
            'token': 'accountLinking'
            // トークンは、セッション再開時にリクエストを識別するために使用できれば、どんなものでも構いません
        })
            .getResponse();
    }else{
      // 既にリンクされている場合のフローを処理します
    }
  }
};

音声によるアカウントリンクを開始するタスクの応答を取得するハンドラーを実装する

音声によるアカウントリンクフローのステップ3では、音声によるアカウントリンクフローが完了します。Skill ConnectionsリクエストのonCompletionフラグをRESUME_SESSIONに設定していたため、ユーザーとのスキルセッションが再開します。スキルは、以下のようなTasks.CompleteTaskディレクティブの形式で、Skill Connectionsの応答を受け取ります。

{
    "type": "Task.CompleteTask",
    "status": {
        "code": "string",
        "message": "string"
     }
    "token": "string"
    "result": {
      "status" : <account_linking_status_enum>
    }
}

「音声によるアカウントリンク」スキルから返されるstatusオブジェクトには、次のコードとメッセージが含まれる可能性があります。

ステータス メッセージ スキルに制御を渡す前のAlexa応答

200

User account was already linked

「タクシー予約にEメールアドレスにアクセスする許可をすでに与えているので、Amazonログイン情報でサインインしました。リンクされたアカウントはAlexaアプリの「スキル」セクションで管理できます。」

200

User account is now linked

「アニーさんのAmazonログイン情報でタクシー予約にサインインしました。リンクされたアカウントはAlexaアプリの「スキル」セクションで管理できます。」

200

User denied account linking request

「はい、Alexaアプリの「スキル」セクションでいつでもアカウントをリンクできます。」

200

User hasn't enabled skills personalization

「タクシー予約のアカウントは、Alexaアプリのスキルセクションでリンクできます。Alexaアプリの設定画面のプロフィールと家族で音声IDを設定すると、音声でアカウントをリンクすることもできます。」

200

User isn't the Amazon account owner

「すみません、音声でアカウントをリンクできるのは、プライマリーアカウント所有者のみです。Alexaアプリのスキルセクションでタクシー予約アカウントをリンクできます。」

200

User didn't answer the account linking request

「はい、Alexaアプリの「スキル」セクションでいつでもアカウントをリンクできます。」

400

Bad Request, request is invalid.

「タクシー予約のアカウントは、Alexaアプリのスキルセクションでリンクできます。」

403

Requester wasn't allowed to invoke the provider

「タクシー予約のアカウントは、Alexaアプリのスキルセクションでリンクできます。」

500

Internal Server error

「すみません、アカウントをリンクできませんでした。Alexaアプリのスキルセクションでいつでもアカウントをリンクできます。」

また、スキルは、Amazonが所有する音声によるアカウントリンクスキルからのSkill Connections応答を受け取るハンドラーを実装し、その応答を使って音声によるアカウントリンクフローの結果に基づいたユーザーとの会話を適切に続行する必要もあります。以下のベストプラクティスを考慮してください。

  • 同じスキルセッションでユーザーの音声によるアカウントリンクをもう一度開始しないでください。つまり、ユーザーを音声によるアカウントリンクエクスペリエンスに戻す際には、以下のハンドラーは使用しません。代わりに、アカウントリンクホームカードを送信して、ユーザーが後でアカウントリンクを行えるようにします。

  • 音声によるアカウントリンクフローでユーザーのリンクが成功しなかった場合、LinkAccountカードを送信して、ユーザーが後でAlexaアプリを使ってアカウントリンクできるようにする必要があります。アカウントリンクカードの詳細については、アカウントリンクに使用するカードを定義するを参照してください。

  • ユーザーのアカウントリンクに成功したら(つまりaccountLinkingTaskStatusLINKEDになったら)、アカウントリンクのaccessTokenを使ってLWAのuser_idを取得します。取得したLWAのuser_idを使って、ユーザーを新規ユーザーとしてシステムに登録する必要があるかどうかを判断します。

AMAZON.AskForAccountLinkingタスクでSkill Connectionsの応答を受け取るには、以下の形式でハンドラーを実装します。

const AccountLinkingSessionResumedHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
    return request.type === 'SessionResumedRequest' && request.cause.token === 'accountLinking'; // トークンはリクエストで渡したものと同じである必要があります
    },
    handle(handlerInput) {
       const status = handlerInput.requestEnvelope.request.cause.status;
       const code = status.code;
       const message = status.message;
       console.log(`AccountLinkingSessionResumedHandlerの受信したステータスコード ${code} メッセージ${message}`);

       const accountLinkingStatus = handlerInput.requestEnvelope.request.cause.result.status;

       if (code !== '200'){
        //リクエストでエラーが発生しました
        //応答
        return handlerInput.responseBuilder
            .speak(speechText)
            .withLinkAccountCard()
            .getResponse();
       }

       // ここでユーザーがアカウントリンクに成功したか失敗したかを確認します
        const accountLinkingTaskResult = handlerInput.requestEnvelope.request.cause.result;
        const accountLinkingTaskStatus = accountLinkingTaskResult.status;        
        if (accountLinkingTaskStatus === 'LINKED') {
         // アカウントはリンク済みか、リンクリクエストが成功しました
         // <HANDLE_LINKED>
         //応答
        var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;

        // 必要に応じてアカウントを検索するか、作成するロジックを追加します
        // 内部でLWAを呼び出してLWA IDを取得するリソースサーバーがある場合
        // makeHttpCall('resource_server',accessToken);

        // 操作にLWA_IDが必要な場合
        // https://developer.amazon.com/docs/login-with-amazon/obtain-customer-profile.html#call-profile-endpoint
        // makeHttpCall('https://api.fe.amazon.com/user/profile?access_token=*',accessToken);
        // 必要に応じて上記のリクエストからのLWA_IDを使ってアカウントの検索または作成を行います

        // ユーザーの連絡先情報が必要な場合はAlexa Customer Profile APIを使用します
        // https://developer.amazon.com/en-US/docs/alexa/custom-skills/request-customer-contact-information-for-use-in-your-skill.html

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

        } else if (accountLinkingTaskStatus === 'DENIED') {
        // ユーザーがリクエストされたアカウントリンクの許可またはスキルへの情報共有を拒否したため、アカウントリンクフローが失敗しました

        // <HANDLE_DENIED>
        //応答
        return handlerInput.responseBuilder
            .speak(speechText)
            .withLinkAccountCard()
            .getResponse();

        } else if (accountLinkingTaskStatus === 'NOT_ANSWERED') {
          // ユーザーがスキルの質問に答えませんでした
          // <HANDLE_NOT_ANSWERED>
          //応答
        return handlerInput.responseBuilder
            .speak(speechText)
            .withLinkAccountCard()
            .getResponse();
        } else if (accountLinkingTaskStatus === 'USER_REQUIREMENTS_NOT_MET'){
          // このユーザーの音声によるアカウントリンクフローがトリガーできないか、完了できないため、リクエストがキャンセルされました
          // <HANDLE_USER_REQUIREMENTS_NOT_MET>
          //応答
        return handlerInput.responseBuilder
            .speak(speechText)
            .withLinkAccountCard()
            .getResponse();
        }

    }
};