予約を作成する(Dialogflow)

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

取引の流れ

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

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

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

取引と Orders API を使用するアクションには追加のポリシーが適用されることにご注意ください。取引を含むアクションのレビューには最大で 6 週間ほどかかります。リリース スケジュールを計画する際は、この点を考慮してください。レビュー プロセスがスムーズに進むよう、レビューにアクションを提出する前に、取引に関するポリシーとガイドラインに準拠していることを確認してください。

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

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

プロジェクトのビルド

取引に関連する会話の例については、Node.jsJava の取引のサンプルをご覧ください。

プロジェクトの設定

アクションを作成するときに、Actions Console でトランザクションを実行することを指定する必要があります。また、Node.JS クライアント ライブラリを使用している場合は、最新バージョンの Orders API を使用するようにフルフィルメントを設定します。

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

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

  4. Node.JS クライアント ライブラリを使用してアクションのフルフィルメントを作成する場合は、フルフィルメント コードを開き、アプリのデカレーションを更新して ordersv3 フラグを true に設定します。次のコード スニペットは、Orders バージョン 3 のアプリ宣言の例を示しています。

Node.js

const {dialogflow} = require('actions-on-google');
let app = dialogflow({
  clientId, // If using account linking
  debug: true,
  ordersv3: true,
});

Node.js

const {actionssdk} = require('actions-on-google');
let app = actionssdk({
  clientId, // If using account linking
  debug: true,
  ordersv3: true,
});

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

ユーザー エクスペリエンス

ユーザーが予約を設定する意思を示したらすぐに actions.intent.TRANSACTION_REQUIREMENTS_CHECK インテントをトリガーし、ユーザーが予約をリクエストできるようにすることをおすすめします。たとえば、アクションが呼び出されたときに、「座席を予約しますか?」と質問することがあります。ユーザーが「はい」と答えた場合は、すぐにこのインテントをリクエストする必要があります。これにより、先に進むだけでなく、取引の続行を妨げる設定を修正する機会を用意できます。

取引要件チェック インテントをリクエストすると、次のいずれかの結果になります。

  • 要件を満たしている場合、フルフィルメントは成功を示すインテントを受け取り、ユーザーの注文の作成に進むことができます。
  • 1 つ以上の要件が満たされていない場合、フルフィルメントは失敗状態を含むインテントを受け取ります。その場合は、会話を終了するか、予約フローから離れてください。

    ユーザーがエラーを修正できる場合は、デバイスで問題の解決を求めるメッセージが自動的に表示されます。スマート スピーカーなどの音声のみのサーフェスで行われている場合、会話はユーザーのスマートフォンに渡されます。

フルフィルメント

ユーザーが取引の要件を満たしていることを確認するには、TransactionRequirementsCheckSpec オブジェクトを使用して actions.intent.TRANSACTION_REQUIREMENTS_CHECK インテントのフルフィルメントをリクエストします。

要件を確認する

クライアント ライブラリを使用して、ユーザーが予約要件を満たしているかどうかを確認します。

Node.js
conv.ask(new TransactionRequirements());
Java
return getResponseBuilder(request)
    .add("Placeholder for transaction requirements text")
    .add(new TransactionRequirements())
    .build();
Dialogflow JSON

下記の JSON は Webhook レスポンスを示します。

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
        }
      }
    }
  }
}
Actions SDK JSON

下記の JSON は Webhook レスポンスを示します。

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
要件確認の結果を受け取る

アシスタントはインテントを遂行した後、チェックの結果を含む actions.intent.TRANSACTION_REQUIREMENTS_CHECK インテントでリクエストをフルフィルメントに送信します。

このリクエストを適切に処理するには、actions_intent_TRANSACTION_REQUIREMENTS_CHECK イベントによってトリガーされる Dialogflow インテントを宣言します。トリガーされると、このインテントをフルフィルメントで処理します。

Node.js
const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT');
if (arg && arg.resultType === 'CAN_TRANSACT') {
  // Normally take the user through cart building flow
  conv.ask(`Looks like you're good to go!`);
} else {
  conv.close('Transaction failed.');
}
Java
Argument transactionCheckResult = request
    .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT");
boolean result = false;
if (transactionCheckResult != null) {
  Map<String, Object> map = transactionCheckResult.getExtension();
  if (map != null) {
    String resultType = (String) map.get("resultType");
    result = resultType != null && resultType.equals("CAN_TRANSACT");
  }
}
ResponseBuilder responseBuilder = getResponseBuilder(request);
if (result) {
  responseBuilder.add("Looks like you're good to go! Now say 'confirm transaction'");
} else {
  responseBuilder.add("Transaction failed");
}
return responseBuilder.build();
Dialogflow JSON

下記の JSON は Webhook リクエストを示します。

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_transaction_check_complete_df",
      "displayName": "reservation_transaction_check_complete_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": [
            {
              "extension": {
                "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
                "resultType": "CAN_TRANSACT"
              },
              "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
            }
          ]
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
Actions SDK JSON

下記の JSON は Webhook リクエストを示します。

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_transaction_check_complete_asdk",
      "arguments": [
        {
          "extension": {
            "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
            "resultType": "CAN_TRANSACT"
          },
          "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
        }
      ]
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

2. 注文を作成する

ユーザー エクスペリエンス

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

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

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

フルフィルメント

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

Order には、少なくとも次の内容を含める必要があります。

  • buyerInfo - 予約をスケジュールするユーザーに関する情報。
  • transactionMerchant - 予約を推進する販売者に関する情報。
  • contents - lineItems として表示される予約の実際の詳細。

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

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

Node.js
app.intent('build_reservation_df', (conv) => {
  const now = new Date().toISOString();
  const order = {
    createTime: now,
    lastUpdateTime: now,
    merchantOrderId: 'UNIQUE_ORDER_ID',
    userVisibleOrderId: 'USER_VISIBLE_ORDER_ID',
    transactionMerchant: {
      id: 'https://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: 'https://example.com',
        },
      },
      {
        type: 'CALL',
        title: 'Call us',
        openUrlAction: {
          url: 'tel:+16501112222',
        },
      },
      {
        type: 'EMAIL',
        title: 'Email us',
        openUrlAction: {
          url: 'mailto:person@example.com',
        },
      },
    ],
    termsOfServiceUrl: 'https://www.example.com',
  };

Java
private static OrderV3 createOrder() {
  // Transaction Merchant
  MerchantV3 transactionMerchant = new MerchantV3()
      .setId("http://www.example.com")
      .setName("Example Merchant");

  // Line Item

  // Reservation Item Extension
  ReservationItemExtension reservationItemExtension = new ReservationItemExtension()
      .setStatus("PENDING")
      .setUserVisibleStatusLabel("Reservation pending.")
      .setType("RESTAURANT")
      .setReservationTime(new TimeV3()
          .setTimeIso8601("2020-01-16T01:30:15.01Z"))
      .setUserAcceptableTimeRange(new TimeV3()
          .setTimeIso8601("2020-01-15/2020-01-17"))
      .setPartySize(6)
      .setStaffFacilitators(Collections.singletonList(new StaffFacilitator()
          .setName("John Smith")))
      .setLocation(new Location()
          .setZipCode("94086")
          .setCity("Sunnyvale")
          .setPostalAddress(new PostalAddress()
              .setRegionCode("US")
              .setPostalCode("94086")
              .setAdministrativeArea("CA")
              .setLocality("Sunnyvale")
              .setAddressLines(
                  Collections.singletonList("222, Some other Street"))));

  LineItemV3 lineItem = new LineItemV3()
      .setId("LINE_ITEM_ID")
      .setName("Dinner reservation")
      .setDescription("A world of flavors all in one destination.")
      .setReservation(reservationItemExtension);

  // Order Contents
  OrderContents contents = new OrderContents()
      .setLineItems(Collections.singletonList(lineItem));

  // User Info
  UserInfo buyerInfo = new UserInfo()
      .setEmail("janedoe@gmail.com")
      .setFirstName("Jane")
      .setLastName("Doe")
      .setDisplayName("Jane Doe");

  // Follow up actions
  Action viewDetails = new Action()
      .setType("VIEW_DETAILS")
      .setTitle("View details")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("https://example.com"));

  Action call = new Action()
      .setType("CALL")
      .setTitle("Call us")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("tel:+16501112222"));

  Action email = new Action()
      .setType("EMAIL")
      .setTitle("Email us")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("mailto:person@example.com"));

  // Terms of service and order note
  String termsOfServiceUrl = "https://example.com";

  String now = Instant.now().toString();

  OrderV3 order = new OrderV3()
      .setCreateTime(now)
      .setLastUpdateTime(now)
      .setMerchantOrderId("UNIQUE_ORDER_ID")
      .setUserVisibleOrderId("UNIQUE_USER_VISIBLE_ORDER_ID")
      .setTransactionMerchant(transactionMerchant)
      .setContents(contents)
      .setBuyerInfo(buyerInfo)
      .setFollowUpActions(Arrays.asList(
          viewDetails,
          call,
          email
      ))
      .setTermsOfServiceUrl(termsOfServiceUrl);

  return order;
}
JSON

下記の JSON は Webhook レスポンスを示します。

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "order": {
            "createTime": "2019-07-17T18:25:30.182Z",
            "lastUpdateTime": "2019-07-17T18:25:30.182Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://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": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          },
          "orderOptions": {
            "requestDeliveryAddress": false,
            "userInfoOptions": {
              "userInfoProperties": [
                "EMAIL"
              ]
            }
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          }
        }
      }
    }
  }
}

3. 注文内容を表示する

予約注文をユーザーに提示して、ユーザーが確認または拒否できるようにします。actions.intent.TRANSACTION_DECISION インテントをリクエストし、作成した Order を指定します。

ユーザー エクスペリエンス

actions.intent.TRANSACTION_DECISION インテントをリクエストすると、アシスタントは組み込みエクスペリエンスを開始し、Order がカートのプレビュー カードに直接レンダリングされます。ユーザーは「予約のスケジュールを設定」と言う、取引を承認しない、予約の詳細の変更をリクエストするなどの操作ができます。

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

フルフィルメント

actions.intent.TRANSACTION_DECISION インテントをリクエストしたら、OrderorderOptions を含む TransactionDecision を作成します。

次のコードは、注文の TransactionsDecision の例を示しています。

Node.js
conv.ask(new TransactionDecision({
  orderOptions: {
    requestDeliveryAddress: 'false',
  },
  presentationOptions: {
    actionDisplayName: 'RESERVE',
  },
  order: order,
}));
Java
// Create order options
OrderOptionsV3 orderOptions = new OrderOptionsV3()
    .setRequestDeliveryAddress(false)
    .setUserInfoOptions(new UserInfoOptions()
        .setUserInfoProperties(Collections.singletonList("EMAIL")));

// Create presentation options
PresentationOptionsV3 presentationOptions = new PresentationOptionsV3()
    .setActionDisplayName("RESERVE");

// Ask for transaction decision
return getResponseBuilder(request)
    .add("Placeholder for transaction decision text")
    .add(new TransactionDecision()
        .setOrder(order)
        .setOrderOptions(orderOptions)
        .setPresentationOptions(presentationOptions)
    )
    .build();
Dialogflow JSON

下記の JSON は Webhook レスポンスを示します。

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "orderOptions": {
            "requestDeliveryAddress": "false"
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          },
          "order": {
            "createTime": "2019-07-17T18:25:30.184Z",
            "lastUpdateTime": "2019-07-17T18:25:30.184Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://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": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          }
        }
      }
    }
  }
}
Actions SDK JSON

下記の JSON は Webhook レスポンスを示します。

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_DECISION",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
            "orderOptions": {
              "requestDeliveryAddress": "false"
            },
            "presentationOptions": {
              "actionDisplayName": "RESERVE"
            },
            "order": {
              "createTime": "2019-07-17T18:25:30.057Z",
              "lastUpdateTime": "2019-07-17T18:25:30.057Z",
              "merchantOrderId": "UNIQUE_ORDER_ID",
              "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
              "transactionMerchant": {
                "id": "https://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": "https://example.com"
                  }
                },
                {
                  "type": "CALL",
                  "title": "Call us",
                  "openUrlAction": {
                    "url": "tel:+16501112222"
                  }
                },
                {
                  "type": "EMAIL",
                  "title": "Email us",
                  "openUrlAction": {
                    "url": "mailto:person@example.com"
                  }
                }
              ],
              "termsOfServiceUrl": "https://www.example.com"
            }
          }
        }
      ]
    }
  ]
}
ユーザーの決定に対応する

提案された注文にユーザーが応答すると、フルフィルメントは、TransactionDecisionValue を含む引数を含む actions_intent_TRANSACTION_DECISION インテントを受け取ります。この値には次のものが含まれます。

  • transactionDecision - 注文の提案に関するユーザーの決定。指定できる値は ORDER_ACCEPTEDORDER_REJECTEDCART_CHANGE_REQUESTEDUSER_CANNOT_TRANSACT です。

このリクエストを処理するには、actions_intent_TRANSACTION_DECISION イベントによってトリガーされる Dialogflow インテントを宣言します。このインテントをフルフィルメントで処理します。

Node.js
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE');
if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') {
  console.log('order accepted');
  const order = arg.order;
}
Java
Argument transactionDecisionValue = request
    .getArgument("TRANSACTION_DECISION_VALUE");
Map<String, Object> extension = null;
if (transactionDecisionValue != null) {
  extension = transactionDecisionValue.getExtension();
}

String transactionDecision = null;
if (extension != null) {
  transactionDecision = (String) extension.get("transactionDecision");
}
if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) {
  OrderV3 order = ((OrderV3) extension.get("order"));
}
Dialogflow JSON

下記の JSON は Webhook リクエストを示します。

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_get_transaction_decision_df",
      "displayName": "reservation_get_transaction_decision_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": []
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
Actions SDK JSON

下記の JSON は Webhook リクエストを示します。

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_get_transaction_decision_asdk",
      "arguments": []
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

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

actions.intent.TRANSACTION_DECISION インテントの transactionDecisionORDER_ACCEPTED の場合は、予約をスケジュールするために必要な処理(独自のデータベースで保持する場合など)を実行します。

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

フルフィルメント

Node.js
// Set lastUpdateTime and update status of reservation
order.lastUpdateTime = new Date().toISOString();
order.reservation.status = 'CONFIRMED';
order.reservation.userVisibleStatusLabel = 'Reservation confirmed';
order.reservation.confirmationCode = '123ABCDEFGXYZ';

// Send synchronous order update
conv.ask(`Transaction completed! You're all set!`);
conv.ask(new OrderUpdate({
  type: 'SNAPSHOT',
  reason: 'Reason string',
  order: order,
}));
Java
ResponseBuilder responseBuilder = getResponseBuilder(request);
order.setLastUpdateTime(Instant.now().toString());

// Set reservation status to confirmed and provide confirmation code
LineItemV3 lineItem = order.getContents().getLineItems().get(0);
ReservationItemExtension reservationItemExtension = lineItem.getReservation();
reservationItemExtension.setStatus("CONFIRMED");
reservationItemExtension.setUserVisibleStatusLabel("Reservation confirmed.");
reservationItemExtension.setConfirmationCode("123ABCDEFGXYZ");
lineItem.setReservation(reservationItemExtension);
order.getContents().getLineItems().set(0, lineItem);

// Order update
OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setType("SNAPSHOT")
    .setReason("Reason string")
    .setOrder(order);

responseBuilder
    .add("Transaction completed! You're all set! Would you like to do anything else?")
    .add(new StructuredResponse().setOrderUpdateV3(orderUpdate));
return responseBuilder.build();
Dialogflow JSON

下記の JSON は Webhook レスポンスを示します。

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Transaction completed! You're all set!"
            }
          },
          {
            "structuredResponse": {
              "orderUpdateV3": {
                "type": "SNAPSHOT",
                "reason": "Reason string",
                "order": {
                  "merchantOrderId": "UNIQUE_ORDER_ID",
                  "reservation": {
                    "status": "CONFIRMED",
                    "userVisibleStatusLabel": "Reservation confirmed",
                    "confirmationCode": "123ABCDEFGXYZ"
                  },
                  "lastUpdateTime": "2019-07-17T18:25:30.187Z"
                }
              }
            }
          }
        ]
      }
    }
  }
}
Actions SDK JSON

下記の JSON は Webhook レスポンスを示します。

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Transaction completed! You're all set!"
              }
            },
            {
              "structuredResponse": {
                "orderUpdateV3": {
                  "type": "SNAPSHOT",
                  "reason": "Reason string",
                  "order": {
                    "merchantOrderId": "UNIQUE_ORDER_ID",
                    "reservation": {
                      "status": "CONFIRMED",
                      "userVisibleStatusLabel": "Reservation confirmed",
                      "confirmationCode": "123ABCDEFGXYZ"
                    },
                    "lastUpdateTime": "2019-07-17T18:25:30.059Z"
                  }
                }
              }
            }
          ]
        }
      }
    }
  ]
}

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

予約ステータスは存続期間に応じて変化します。注文のステータスと詳細を含む 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" スコープを使用して、サービスキーを署名なしトークンと交換します。インストール手順と例については、API クライアント ライブラリの GitHub ページをご覧ください。

鍵交換の例については、Node.jsJava のサンプルで 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 の例を示しています。

Node.js
// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request' module for sending an HTTP POST request.
const request = require('request');
// Import the OrderUpdate class from the Actions on Google client library.
const {OrderUpdate} = require('actions-on-google');
// Import the service account key used to authorize the request. Replace the string path with a path to your service account key.
const key = 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(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/actions.order.developer'],
    null
);
// Authorize the client asynchronously, passing in a callback to run upon authorization.
jwtClient.authorize((err, tokens) => {
    if (err) {
        console.log(err);
        return;
    }
    // Declare the ID of the order to update.
    const orderId = '<UNIQUE_MERCHANT_ORDER_ID>';

    const orderUpdateJson = new OrderUpdate({
        updateMask: [
          'lastUpdateTime',
          'contents.lineItems.reservation.status',
          'contents.lineItems.reservation.userVisibleStatusLabel',
      ].join(','),
        order: {
          merchantOrderId: orderId,
          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.
    const bearer = 'Bearer ' + tokens.access_token;
    const options = {
        method: 'PATCH',
        url: `https://actions.googleapis.com/v3/orders/${orderId}`,
        headers: {
          'Authorization': bearer,
        },
        body: {
          header: {
            'isInSandbox': true,
          },
          orderUpdate: orderUpdateJson,
        },
        json: true,
      };
    // Send the PATCH request to the Orders API.
    request.patch(options, (err, httpResponse, body) => {
        if (err) {
            console.log('There was an error...');
            console.log(err);
            return;
        }
    });
});
Java
// Create order update
FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList(
    "lastUpdateTime",
    "contents.lineItems.reservation.status",
    "contents.lineItems.reservation.userVisibleStatusLabel"))
    .build();

OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setOrder(new OrderV3()
        .setMerchantOrderId(orderId)
        .setLastUpdateTime(Instant.now().toString())
        .setContents(new OrderContents()
        .setLineItems(Collections.singletonList(new LineItemV3()
            .setReservation(new ReservationItemExtension()
                .setStatus("FULFILLED")
                .setUserVisibleStatusLabel("Reservation fulfilled."))))))
    .setUpdateMask(FieldMaskUtil.toString(fieldMask))
    .setReason("Reservation status was updated to fulfilled.");

// Setup JSON body containing order update
JsonParser parser = new JsonParser();
JsonObject orderUpdateJson =
    parser.parse(new Gson().toJson(orderUpdate)).getAsJsonObject();
JsonObject body = new JsonObject();
body.add("orderUpdate", orderUpdateJson);
JsonObject header = new JsonObject();
header.addProperty("isInSandbox", true);
body.add("header", header);
StringEntity entity = new StringEntity(body.toString());
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
request.setEntity(entity);

// Make request
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse response = httpClient.execute(request);
予約ステータスを設定する

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

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

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

トラブルシューティング

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