予約の作成

このガイドでは、Orders API を使用して予約を行う Actions プロジェクトの開発プロセスについて説明します。

取引の流れ

Actions プロジェクトで予約を処理する場合、次のフローを使用します。

  1. 取引要件を検証する(省略可) - 会話の開始時に取引要件ヘルパーを使用して、ユーザーが取引を実行できることを確認します。
  2. 注文を作成する - 予約の詳細を作成する「カート アセンブリ」をユーザーに案内します。
  3. 注文を提案する - カートが完成したら、ユーザーに予約の「注文」を提案して、注文が正しいことを確認します。予約が確定すると、予約の詳細を含むレスポンスが返されます。
  4. 注文を確定して領収書を送信する - 注文が確定したら、予約システムを更新し、ユーザーに領収書を送信します。
  5. 注文の更新情報を送信する - 予約の有効期間中は、PATCH リクエストを Orders API に送信して、ユーザーに予約のステータスの更新情報を送信します。

制限と審査のガイドライン

トランザクションと Orders API を使用するアクションには追加のポリシーが適用されることに注意してください。トランザクションを含むアクションの審査には最大 6 週間かかるため、リリース スケジュールを計画する際は、それを考慮に入れてください。審査プロセスを容易にするため、審査のためにアクションを送信する前に、取引に関するポリシーとガイドラインに準拠していることを確認してください。

Orders API を使用するアクションは、次の国でのみデプロイできます。

オーストラリア
ブラジル
カナダ
インドネシア
日本
メキシコ
カタール
ロシア
シンガポール
スイス
タイ
トルコ
英国
米国

プロジェクトのビルド

トランザクションの会話のさまざまな例については、Node.js のトランザクションのサンプルをご覧ください。

設定

アクションを作成するときに、Actions Console でトランザクションを実行することを指定する必要があります。

プロジェクトとフルフィルメントを設定する手順は次のとおりです。

  1. 新しいプロジェクトを作成するか、既存のプロジェクトをインポートします。
  2. [Deploy](デプロイ)> [Directory information](ディレクトリ情報)の順に移動します。
  3. [その他の情報] > [トランザクション] で、[アクションで Transactions API を使用して物理的な商品のトランザクションを実行しますか?] チェックボックスをオンにします。

取引要件を確認する(省略可)

ユーザーが予約を設定する意思を示したら、すぐに予約をリクエストできることを確認する必要があります。たとえば、アクションが呼び出されたときに、「席を予約しますか?」と尋ねることがあります。ユーザーが「はい」と答えた場合は、続行できることを確認し、取引を続行できない設定を修正する機会を提供します。そのためには、取引要件のチェックを行うシーンに移行する必要があります。

取引要件の確認シーンを作成する

  1. [Scenes] タブで、TransactionRequirementsCheck という名前の新しいシーンを追加します。
  2. [スロットフィル] で [+] をクリックして新しいスロットを追加します。
  3. [タイプを選択] で、スロットタイプとして actions.type.TransactionRequirementsCheckResult を選択します。
  4. スロット名の欄で、スロットに「TransactionRequirementsCheck」という名前を付けます。
  5. [Customize slot value writeback] チェックボックスを有効にします(デフォルトで有効になっています)。
  6. [保存] をクリックします。

トランザクション要件のチェックでは、次のいずれかの結果が得られます。

  • 要件が満たされると、セッション パラメータに成功条件が設定され、ユーザーの注文の作成に進むことができます。
  • 1 つ以上の要件を満たすことができない場合、セッション パラメータに失敗条件が設定されます。この場合は、会話をトランザクション エクスペリエンスから離れるか、会話を終了する必要があります。
    • 障害状態の原因となったエラーがユーザーによって修正できる場合は、デバイスにプロンプトが表示され、問題を解決するようユーザーに促します。会話が音声のみのサーフェスで行われている場合、ユーザーのスマートフォンへのハンドオフが開始されます。

取引要件チェックの結果を処理する

  1. [Scenes] タブで、新しく作成した TransactionRequirementsCheck シーンを選択します。
  2. [Condition] で、[+] をクリックして新しい条件を追加します。
  3. テキスト フィールドに次の条件構文を入力して、成功条件を確認します。

    scene.slots.status == "FINAL" && session.params.TransactionRequirementsCheck.resultType == "CAN_TRANSACT"
    
  4. 追加した条件にカーソルを合わせ、上矢印をクリックして if scene.slots.status == "FINAL" の前に配置します。

  5. [プロンプトを送信] を有効にし、取引を行う準備ができたことをユーザーに伝える簡単なプロンプトを表示します。

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Looks like you're good to go!.
    
  6. [Transition] で、別のシーンを選択して、ユーザーが会話を続行してトランザクションを完了できるようにします。

  7. 条件 else if scene.slots.status == "FINAL" を選択します。

  8. [プロンプトを送信] を有効にし、取引ができないことをユーザーに伝える簡単なプロンプトを表示します。

    candidates:
      - first_simple:
          variants:
            - speech: Transaction requirements check failed.
    
  9. ユーザーが取引できない場合は、[移行] で [会話を終了] を選択して会話を終了します。

注文を作成する

必要なユーザー情報を取得したら、ユーザーが予約を作成できるようにする「カート アセンブリ」エクスペリエンスを構築します。カート アセンブリのフローは、アクションごとにサービスに合わせて若干異なります。

基本的なカート構成では、ユーザーはリストから予約に追加するオプションを選択しますが、ユーザー エクスペリエンスを簡素化するように会話を設計することもできます。たとえば、「はい」か「いいえ」で答えられる簡単な質問でユーザーが毎月の予約をスケジュールできるカート アセンブリ エクスペリエンスを構築します。ユーザーに「おすすめ」予約のカルーセルまたはリストカードを表示することもできます。

リッチ レスポンスを使用して、ユーザーの選択肢を視覚的に提示することをおすすめしますが、ユーザーが音声だけでもカートを作成できるように会話を設計してください。カート アセンブリのエクスペリエンスに関するベスト プラクティスと例については、設計ガイドラインをご覧ください。

注文を作成する

会話全体を通してユーザーの予約の詳細を収集し、Order オブジェクトを作成します。

Order には少なくとも次のものが含まれている必要があります。

  • buyerInfo - 商品を購入しようとしているユーザーに関する情報。
  • transactionMerchant - 注文を処理した販売者に関する情報。
  • contents - lineItems として列挙される注文の実際の内容。

カートを作成するには、Order レスポンス ドキュメントをご覧ください。予約に応じて異なるフィールドが必要になる場合があります。

次のサンプルコードは、省略可能なフィールドを含む完全な予約注文を示しています。

const order = {
   createTime: '2019-09-24T18:00:00.877Z',
   lastUpdateTime: '2019-09-24T18:00:00.877Z',
   merchantOrderId: orderId, // A unique ID String for the order
   userVisibleOrderId: orderId,
   transactionMerchant: {
     id: 'http://www.example.com',
     name: 'Example Merchant',
   },
   contents: {
     lineItems: [
       {
         id: 'LINE_ITEM_ID',
         name: 'Dinner reservation',
         description: 'A world of flavors all in one destination.',
         reservation: {
           status: 'PENDING',
           userVisibleStatusLabel: 'Reservation is pending.',
           type: 'RESTAURANT',
           reservationTime: {
             timeIso8601: '2020-01-16T01:30:15.01Z',
           },
           userAcceptableTimeRange: {
             timeIso8601: '2020-01-15/2020-01-17',
           },
           partySize: 6,
           staffFacilitators: [
             {
               name: 'John Smith',
             },
           ],
           location: {
             zipCode: '94086',
             city: 'Sunnyvale',
             postalAddress: {
               regionCode: 'US',
               postalCode: '94086',
               administrativeArea: 'CA',
               locality: 'Sunnyvale',
               addressLines: [
                 '222, Some other Street',
               ],
             },
           },
         },
       },
     ],
   },
   buyerInfo: {
     email: 'janedoe@gmail.com',
     firstName: 'Jane',
     lastName: 'Doe',
     displayName: 'Jane Doe',
   },
   followUpActions: [
     {
       type: 'VIEW_DETAILS',
       title: 'View details',
       openUrlAction: {
         url: 'http://example.com',
       },
     },
     {
       type: 'CALL',
       title: 'Call us',
       openUrlAction: {
         url: 'tel:+16501112222',
       },
     },
     {
       type: 'EMAIL',
       title: 'Email us',
       openUrlAction: {
         url: 'mailto:person@example.com',
       },
     },
   ],
   termsOfServiceUrl: 'http://www.example.com'
 };

注文とプレゼンテーションのオプションを作成する

const orderOptions = {
      'requestDeliveryAddress': false,
    };

const presentationOptions = {
      'actionDisplayName': 'RESERVE'
    };

セッション パラメータに注文データを保存

フルフィルメントから、注文データをセッション パラメータに保存します。order オブジェクトは、同じセッションのシーン全体で使用されます。

conv.session.params.order = {
    '@type': 'type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec',
    order: order,
    orderOptions: orderOptions,
    presentationOptions: presentationOptions
};

注文内容を表示する

予約注文を作成したら、それをユーザーに確認または拒否してもらう必要があります。そのためには、トランザクションの判断を行うシーンに移行する必要があります。

Transaction Decision シーンを作成する

  1. [Scenes] タブで、TransactionDecision という名前の新しいシーンを追加します。
  2. [スロットフィル] で [+] をクリックして新しいスロットを追加します。
  3. [タイプを選択] で、スロットタイプとして actions.type.TransactionDecisionValue を選択します。
  4. スロット名の欄で、スロットに「TransactionDecision」という名前を付けます。
  5. [Customize slot value writeback] チェックボックスを有効にします(デフォルトで有効になっています)。
  6. [スロットを構成] で、プルダウンから [セッション パラメータを使用] を選択します。
  7. [スロットの設定] で、注文の保存に使用するセッション パラメータの名前をテキスト フィールド(例: $session.params.order)に入力します。
  8. [保存] をクリックします。

TransactionDecisionValue スロットを埋めるために、アシスタントは組み込みエクスペリエンスを開始し、渡された Order をカート プレビュー カードに直接レンダリングします。ユーザーは「予約をスケジュール設定」と言う、取引を拒否する、または予約の詳細の変更をリクエストするなどの操作ができます。

ユーザーはこの時点で注文内容の変更をリクエストできます。この場合は、フルフィルメントでカート アセンブリのエクスペリエンスが完了した後に注文変更リクエストを処理できるようにする必要があります。

取引決定結果を処理する

TransactionDecisionValue スロットが埋まると、取引の決定に対するユーザーの回答がセッション パラメータに保存されます。この値には次のものが含まれます。

  • ORDER_ACCEPTED,
  • ORDER_REJECTED,
  • CART_CHANGE_REQUESTED
  • USER_CANNOT_TRANSACT.

トランザクションの判定結果を処理するには:

  1. [Scenes] タブで、新しく作成した TransactionDecision シーンを選択します。
  2. [Condition] で、[+] をクリックして新しい条件を追加します。
  3. テキスト フィールドに次の条件構文を入力して、成功条件を確認します。

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  4. 追加した条件にカーソルを合わせ、上矢印をクリックして if scene.slots.status == "FINAL" の前に配置します。

  5. [プロンプトを送信] を有効にし、予約が完了したことをユーザーに知らせる簡単なプロンプトを表示します。

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction completed! Your reservation
                $session.params.TransactionDecision.order.merchantOrderId is all
                set!
    
  6. [移行] で [会話を終了] を選択して会話を終了します。

  7. [Condition] で、[+] をクリックして新しい条件を追加します。

  8. テキスト フィールドに次の条件構文を入力して、失敗条件を確認します。

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_REJECTED"
    
  9. 追加した条件にカーソルを合わせ、上矢印をクリックして if scene.slots.status == "FINAL" の前に配置します。

  10. [プロンプトを送信] を有効にし、注文が拒否されたことをユーザーに知らせる簡単なプロンプトを表示します。

    candidates:
      - first_simple:
          variants:
            - speech: Looks like you don't want to set up a reservation. Goodbye.
    
  11. [移行] で [会話を終了] を選択して会話を終了します。

  12. 条件 else if scene.slots.status == "FINAL" を選択します。

  13. [プロンプトを送信] を有効にし、取引ができないことをユーザーに伝える簡単なプロンプトを表示します。

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction failed with status
                $session.params.TransactionDecision.transactionDecision
    
  14. ユーザーが取引できない場合は、[移行] で [会話を終了] を選択して会話を終了します。

予約を確定して領収書を送る

TransactionDecisionValue スロットが ORDER_ACCEPTED の結果を返した場合は、予約のスケジュール設定に必要な処理(独自のデータベースで保持するなど)を直ちに実行する必要があります。

シンプルなレスポンスを送信して会話を続けます。ユーザーには、レスポンスと一緒に「折りたたまれた領収書カード」が届きます。

初回注文の更新情報を送信するには:

  1. [Scenes] タブで TransactionDecision シーンを選択します。
  2. [条件] で、成功結果 ORDER_ACCEPTED をチェックする条件を選択します。

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  3. この条件で、[Webhook の呼び出し] を有効にして、インテント ハンドラ名(update_order など)を指定します。

  4. Webhook コードで、初回注文更新を送信するインテント ハンドラを追加します。

    app.handle('update_order', conv => {
      const currentTime = new Date().toISOString();
      let order = conv.session.params.TransactionDecision.order;
      conv.add(new OrderUpdate({
        'updateMask': {
          'paths': [
            'reservation.status',
            'reservation.user_visible_status_label',
            'reservation.confirmation_code'
          ]
        },
        'order': {
          'merchantOrderId': order.merchantOrderId,
          'lastUpdateTime': currentTime,
          'reservation': {
            'status': 'CONFIRMED',
            'userVisibleStatusLabel': 'Reservation confirmed',
            'confirmationCode': '123ABCDEFGXYZ',
          },
        },
        'reason': 'Reason string'
      }));
    });
    

注文の更新情報を送信する

予約のステータスは、存続期間が終わるまでの間に変化します。HTTP PATCH リクエストを使用して、注文のステータスと詳細を含むユーザー予約注文の更新を Orders API に送信します。

Orders API に非同期リクエストを設定する

Orders API に対する注文更新リクエストはアクセス トークンで承認されます。Orders API に注文の更新を PATCH するには、Actions Console プロジェクトに関連付けられた JSON サービス アカウント キーをダウンロードして、そのサービス アカウント キーを署名なしトークンと交換します。署名なしトークンは、HTTP リクエストの Authorization ヘッダーに渡すことができます。

サービス アカウント キーを取得するには、次の手順に従います。

  1. Google Cloud コンソールで、メニューの 単に お知らせください 所有の可能です デフォルトの [API とサービス] > 認証情報 > 認証情報の作成 > サービス アカウント キー
  2. [サービス アカウント] で [新しいサービス アカウント] を選択します。
  3. サービス アカウントを service-account に設定します。
  4. [役割] を [プロジェクト] > [オーナー] に設定します。
  5. キータイプを [JSON] に設定します。
  6. [作成] を選択します。
  7. 非公開の JSON サービス アカウント キーがローカルマシンにダウンロードされます。

注文を更新するコードで、Google API クライアント ライブラリと "https://www.googleapis.com/auth/actions.order.developer" スコープを使用して、サービスキーを署名なしトークンと交換します。インストール手順と例については、GitHub ページの API クライアント ライブラリをご覧ください。

鍵交換の例については、Node.js サンプルorder-update.js をご覧ください。

注文の更新情報を送信する

サービス アカウント キーを OAuth 署名なしトークンと交換したら、注文の更新を承認済みの PATCH リクエストとして Orders API に送信します。

Orders API の URL: PATCH https://actions.googleapis.com/v3/orders/${orderId}

リクエストに次のヘッダーを指定します。

  • "Authorization: Bearer token" は、サービス アカウント キーを交換した OAuth 署名なしトークンに置き換えます。
  • "Content-Type: application/json".

PATCH リクエストは、次の形式の JSON 本文を使用します。

{ "orderUpdate": OrderUpdate }

OrderUpdate オブジェクトは、次の最上位フィールドで構成されます。

  • updateMask - 更新する注文のフィールド。予約ステータスを更新するには、値を reservation.status, reservation.userVisibleStatusLabel に設定します。
  • order - 更新の内容。予約の内容を更新する場合は、更新された Order オブジェクトに値を設定します。予約のステータスを更新するだけの場合(たとえば、"PENDING" から "FULFILLED" に)、オブジェクトには次のフィールドがあります。

    • merchantOrderId - Order オブジェクトで設定したのと同じ ID。
    • lastUpdateTime - この更新のタイムスタンプ。
    • purchase - 次のものを含むオブジェクト。
      • status - ReservationStatus での注文のステータス(「CONFIRMED」や「CANCELLED」など)。
      • userVisibleStatusLabel - 注文ステータスの詳細を示すユーザー向けのラベル。「予約が確定しました」など。
  • userNotification オブジェクト。なお、このオブジェクトを含めても、ユーザーのデバイスに通知が表示されるとは限りません。

次のサンプルコードは、予約注文のステータスを FULFILLED に更新する OrderUpdate の例を示しています。

// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request-promise' module for sending an HTTP POST request.
const request = require('request-promise');
// Import the OrderUpdate class from the client library.
const {OrderUpdate} = require('@assistant/conversation');

// Import the service account key used to authorize the request.
// Replacing the string path with a path to your service account key.
// i.e. const serviceAccountKey = require('./service-account.json')

// Create a new JWT client for the Actions API using credentials
// from the service account key.
let jwtClient = new google.auth.JWT(
   serviceAccountKey.client_email,
   null,
   serviceAccountKey.private_key,
   ['https://www.googleapis.com/auth/actions.order.developer'],
   null,
);

// Authorize the client
let tokens = await jwtClient.authorize();

// Declare the ID of the order to update.
const orderId = '<UNIQUE_MERCHANT_ORDER_ID>';

// Declare order update
const orderUpdate = new OrderUpdate({
   updateMask: {
     paths: [
       'contents.lineItems.reservation.status',
       'contents.lineItems.reservation.userVisibleStatusLabel'
     ]
   },
   order: {
     merchantOrderId: orderId, // Specify the ID of the order to update
     lastUpdateTime: new Date().toISOString(),
     contents: {
       lineItems: [
         {
           reservation: {
             status: 'FULFILLED',
             userVisibleStatusLabel: 'Reservation fulfilled',
           },
         }
       ]
     },
   },
   reason: 'Reservation status was updated to fulfilled.',
});

// Set up the PATCH request header and body,
// including the authorized token and order update.
let options = {
 method: 'PATCH',
 uri: `https://actions.googleapis.com/v3/orders/${orderId}`,
 auth: {
   bearer: tokens.access_token,
 },
 body: {
   header: {
     isInSandbox: true,
   },
   orderUpdate,
 },
 json: true,
};

// Send the PATCH request to the Orders API.
try {
 await request(options);
} catch (e) {
 console.log(`Error: ${e}`);
}

予約ステータスを設定する

注文更新の ReservationStatus には、注文の現在の状態を記述する必要があります。アップデートの order.ReservationStatus フィールドで、次のいずれかの値を使用します。

  • PENDING - 予約はアクションによって作成されましたが、バックエンドで追加の処理が必要です。
  • CONFIRMED - 予約がスケジューリング バックエンドで確認されました。
  • CANCELLED - ユーザーが予約をキャンセルしました。
  • FULFILLED - ユーザーの予約がサービスによって履行されました。
  • CHANGE_REQUESTED - ユーザーが予約の変更をリクエストし、変更の処理中です。
  • REJECTED - 予約を処理できなかったか、確認できなかった場合。

予約に関連する各ステータスの注文の更新情報を送信します。たとえば、予約がリクエストされた後に確認するための手動処理が必要な場合は、その追加処理が完了するまで PENDING 注文の更新を送信します。すべての予約でステータス値が必要になるとは限りません。

プロジェクトをテストする

プロジェクトをテストするときは、Actions Console でサンドボックス モードを有効にして、お支払い方法を請求せずにアクションをテストできます。サンドボックス モードを有効にする手順は次のとおりです。

  1. Actions Console で、ナビゲーションの [Test] をクリックします。
  2. [設定] をクリックします。
  3. [開発サンドボックス] オプションを有効にします。

現物取引の場合は、サンプルで isInSandbox フィールドを true に設定することもできます。この操作は、Actions Console でサンドボックス モードの設定を有効にすることと同じです。isInSandbox を使用するコード スニペットを確認するには、注文の更新を送信するをご覧ください。

トラブルシューティング

テスト中に問題が発生した場合は、取引に関するトラブルシューティングの手順をご覧ください。