
VoiceflowでAmazon Payの第2回です。前回はAmazon PayとAlexaスキルの接続を行い、デモスキルが動作するところまでをやりました。今回はスキルの実装について解説したいと思います。
第1回はこちら。
目次
解説
ではデモプロジェクトの解説です。今回はポイントを絞って解説します。
全体像
全体のフローはこんな感じです(コメントや画像等を外してシンプルにしてみました)

大きく分けてフローは2つです。上がスキル起動からSetup APIによるAmazon Payのセットアップ、下がSetup APIとCharge APIのConnections.Responseを受けてそれぞれの処理を行うフローになっています。まず上から見ていきましょう。
Step1
スキルが起動するとSTEP1のところが処理されます。ここでは、
- Amazon Payとの連携に必要な識別子(注文ID・オーソリID・トークン)の生成
- 出品者ID・テスト用メールアドレスの設定
- Billing Agreement IDの初期化
- Amazon Payへのアクセス権のチェック
などの「初期化」的な処理を行った上でスキルを起動、ユーザとの対話の中で書籍の購入を確認しています。
ここでポイントとなるのは、Step1の上から2つ目のUser Info Stepです。

Alexaの機能の中には、ユーザによるアクセス権限許可をもらわないと使えないものがいくつかあります。例えば、前回やった「リマインダー」とかがそれにあたります。User Info Stepはこれらの機能のアクセス権が付与されているか?を確認し、場合によってはその情報を取得するためのステップです。
スキルでアクセス権限を取り扱う場合には、Alexa開発者コンソールでそれらを有効にする必要があります。開発者コンソールの左のメニューから、ツール→アクセス権限を開きます。

アクセス権が必要な情報の一覧が表示されますので、ここでそれぞれを有効にするわけですね。

VoiceflowのUser Info Blockで必要なアクセス権を指定すると、実はアップロード時にこれらの設定を有効化してくれます。そしてAmazon Payを使う場合にもこの設定が必要なのです。

が、実はUser Info StepはAmazon Payに(まだ)対応していません。VoiceflowのUser Info Stepで選択できるのは以下です。

ではこれはどうやって有効化するのでしょうか?
実はここ少しハック的なやり方になっています。デモスキルの.vfファイルを見てみましょう。

ここがUser Info Stepでの権限チェックの設定になります。ここにあるpayments:autopay_consentがAmazon Payの利用権限の設定です。
User Info Stepでもし何も権限を設定しなかった場合、.vfファイルはこうなります。

つまりこの"type":nullを手動で設定してあげればいいということですね。やり方としてはこうなります。
- 一旦何も指定せずにUser Info Stepを配置する。
- .プロジェクトを.vfファイルにエクスポートする
- .vfファイルをテキストエディタ等で開いて編集、該当のUser Info StepにAmazon Payの利用権限を記載して保存
- 編集後の.vfファイルをインポート
ここは普通にやってるとなかなか思いつかないところですし、あまり好ましいやり方ではないという気もします。現時点でAmazon Pay対応スキルを作る場合にはこういうやり方が必要になる、ということだけ、頭の片隅においておけばいいかと思います。(User Info StepでAmazon Payに対応してくれればいいんですけどね)
もう一つ補足しておくと、User Info Stepは権限をチェックしたあと、本来はその結果で分岐させて権限の許可を求める処理を行ったりさせます。

今回の例のように複数のステップを一つにまとめている場合(Combined Stepといいます)、チェックの結果に関わらず下のブロックに進みます。

つまり、ここは権限チェックとして機能はしません。単にAlexa開発者コンソールアクセス権を有効にするためにおいているわけです。これだとユーザがAmazon Payの権限を許可していないに対応できないのではないか、と思いますが、Amazon Payの場合は後述するSetup APIを送信したときにこのあたりのチェックもやってくれます。
スキル内でAmazon Payを使用するには、2つの権限が必要になります。(①Amazon Payのアクセス権限(スキル毎)、②音声ショッピングの設定(全体で1つの設定))
音声ショッピングの設定はAlexaアプリで有効にする必要がありますが、Amazon Payのアクセス権限は、声で権限を有効にすることが出来ます。 Amazon Pay Setup APIを呼び出すと、Alexaが双方の権限チェックを代わりに実施し、適切な発話をユーザに返>します。 音声ショッピングの設定が有効になっていない場合は、Alexaアプリにカードが送信され、Amazon Payのアクセス権限が有効になっていない場合は、声で権限を有効にするようAlexaが促します。
Step2
ここでは、Amazon PayのSetup APIを実行するために、Directive StepでConnection.SendRequestを送信します。

Directive Stepで送信するConnections.SendRequestディレクティブは以下となっています。
{ "type": "Connections.SendRequest", "name": "Setup", "payload": { "@type": "SetupAmazonPayRequest", "@version": "2", "sellerId": "{sellerId}", "countryOfEstablishment": "JP", "ledgerCurrency": "JPY", "checkoutLanguage": "ja-JP", "needAmazonShippingAddress": true, "sandboxMode": true, "sandboxCustomerEmailId":"{sandboxCustomer}" }, "token": "{tokenId}" }
ここで、Setup APIにリクエストを投げて、Amazon Payの準備をします。最初に説明したとおり、Setup APIでは、出品者IDや国・通貨などを指定します。また、モノの発送などで住所が必要な場合(needAmazonShippingAddress)や、テストモードで実行する場合の情報など(sandboxMode、sandboxCustomerEmailId)もここで指定します。
詳細はAmazon Payのドキュメントを参照してください。
そして、ここは、前回の音声パーミッションでも出てきた「Skill Connections」と似た感じの仕組みになっています。つまり、結果はConnections.Responseとして、Event Stepで受けることになります。つまり、こういう流れになります。

STEP3
ということでStep3では、Step2で送信されたSetup APIの結果をEvent Stepで受け取ります。

Setup APIからのレスポンスはこんな感じです。
{ "type": "Connections.Response", "requestId": "amzn1.echo-api.request.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "timestamp": "2021-06-25T14:15:55Z", "locale": "ja-JP", "status": { "code": "200", "message": "OK" }, "name": "Setup", "payload": { "billingAgreementDetails": { "releaseEnvironment": "SANDBOX", "billingAgreementStatus": "OPEN", "creationTimestamp": "2021-06-24T17:50:15.607Z", "destination": { "stateOrRegion": "兵庫県", "phone": "<202a>07812345678", "countryCode": "JP", "postalCode": "1234567", "name": "山田太郎", "addressLine1": "神戸市中央区○○町1丁目1番1号", "addressLine2": "△△マンション101号室" }, "billingAgreementId": "C03-XXXXXXX-XXXXXXX", "checkoutLanguage": "ja_JP" } }, "token": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }
Setup APIの結果なので、イベント名(name)はSetupになっています。Charge APIの場合はChargeになります。これでどのConnections.SendRequestからのレスポンスなのかを見分けることができます。
Setup APIが正しく処理されたかの結果はstatus.codeで取得できます(200なら成功)。処理が正しく行われて、支払いができることが確認できればbillingAgreementIdが返ってきます。billingAgreementIdは実際の支払いを行うCharge APIで必要になりますので、これが正しく取得できているかが重要です。
あと、Setup API送信時にneedAmazonShippingAddressを有効にしていたので、配送先の住所が返ってきていますね。
これらをEvent Stepで受け取って変数に入れています。

次に、Condition Stepを使って、Setup APIからのレスポンスなのか、Charge APIからのレスポンスなのか、で分岐させます。それ以外の場合(Else)はセッションを継続させるハックですね、詳細は前回の音声パーミッションの記事をご覧ください。

Setup APIからの分岐で、さらにCondition Stepを使って、今度は結果をチェックします。

Condition Stepの条件は上から順に判定され、条件にマッチしたものがあればそれ以降のチェックは行われずに分岐先に飛びます。上で述べたとおり、重要なのはbillingAgreementIdが返ってきているか?です。status.codeは単にSetup APIが処理できたかどうか、だけであり、200が返ってきた場合でもbillingAgreementIdが返ってこない場合があります(例えば、権限がない、何からの理由で支払いが停止されている場合など)。なのでbillingAgreementIdが取れていない場合を一番最初にチェックしています。
次にstatus.codeが200の場合、つまり、成功している場合です。上の条件でbillingAgreementIdが0ではなく、status.codeが200ならば、支払いの準備ができているとしてStep4に進みます。
billingAgreementIdが取れていない場合や、status.codeが200以外の場合は、エラーとしてスキルを終了させます。
Step 4
Step4では、Setup APIの結果から住所情報を取り出して、変数に入れているだけです。

addressLine1 = data.payload.billingAgreementDetails.destination.addressLine1; addressLine2 = data.payload.billingAgreementDetails.destination.addressLine2; city = data.payload.billingAgreementDetails.destination.city; countryCode = data.payload.billingAgreementDetails.destination.countryCode; name = data.payload.billingAgreementDetails.destination.name; phone = data.payload.billingAgreementDetails.destination.phone; postalCode = data.payload.billingAgreementDetails.destination.postalCode; stateOrRegion = data.payload.billingAgreementDetails.destination.stateOrRegion;
Step 5
Step 4で取り出した住所情報をもとにユーザに送付先確認を行います。

送付先の住所情報は、ユーザ側で複数設定することが可能です。ただし、Setup APIで返ってくる住所はデフォルトのものになります。そこで、ここではデフォルトの配送先でよいか?を確認しています。
また、今回のデモには含めていませんが、地域によって異なる送料が発生する場合などは送付先をもとに送料の計算をこのあたりで行うことになると思います。送料含めたトータルの請求額はこのあとのCharge APIで指定することになりますので。
まとめ
Setup APIからCharge APIの直前までの流れを説明しました。次回はCharge APIで実際の請求を行うところを説明します。