非同期の注文更新

顧客が料理の注文を送信したら、注文更新メッセージを注文エンドツーエンド サービスに送信して、変更を Google に通知できます。

注文の更新情報を送信する一般的な理由は次のとおりです。

  • 注文の処理予定日時が利用できるようになる、または変更されること。
  • 注文の状態が変わる。
  • その注文は処理できなくなりました。
  • 注文に含まれるメニュー項目の価格が変更された。
  • 顧客は、カスタマー サポートやレストランの電話番号など、新しい方法で注文を管理できます。
  • 注文の領収書が利用可能になります。

以降のセクションでは、注文の更新を使用してこのようなさまざまなシナリオに対処する方法について説明します。

注文ステータスの移行

注文の状態は 6 つあります。次の図に、これらの状態と、考えられる遷移の概要を示します。

注文ステータスの遷移

お客様が初めて注文を送信すると、注文は CREATEDCONFIRMEDREJECTED のいずれかの状態で始まります。状態遷移が有効である限り、注文更新メッセージを送信して注文の状態を更新できます。CREATED 状態は、パートナーのプラットフォームが注文を直ちに確認または拒否できない場合に使用します。ユースケースの一例として、お客様が配送アグリゲータ経由で注文する場合が挙げられます。配送アグリゲータは Google から荷物を受け取り、アグリゲータはレストランに情報を送信します。レストランが注文を受け取って確定すると、ステータスは CONFIRMED になり、それ以外の場合は REJECTED になります。

CONFIRMED 状態にある注文は、次に IN_PREPARATION 状態に移行します。注文が受け取りか宅配かに応じて、次に READY_FOR_PICKUP または IN_TRANSIT ステータスを使用します。料理の配達や受け取りが完了すると、注文は FULFILLED ステータスに設定されます。

購入者が注文をキャンセルできるようにする場合は、CANCELLED ステータスを使用できます。注文をキャンセルできるのは、CREATEDCONFIRMEDIN_PREPARATIONREADY_FOR_PICKUPIN_TRANSIT 状態のときにです。 注文エンドツーエンド サービスは、キャンセル ポリシーとキャンセル時の支払い状態に応じて払い戻しを行います。

注文エンドツーエンド サービスが、利用可能なすべての状態と移行をサポートする必要はありません。ただし、注文の最終状態は FULFILLEDREJECTEDCANCELLED のいずれかである必要があります。

フルフィルメントの推定所要時間を指定する

注文品の受け取り(または配送)が可能になる時期の目安をユーザーに提供できます。FoodOrderUpdateExtensionestimatedFulfillmentTimeIso8601 フィールドを使用して、顧客の注文が集荷または配送可能になる時期の目安を指定します。

次のタイミングで estimatedFulfillmentTimeIso8601 を送信します。

  • 予定時刻が利用可能になったとき(理想的には CREATED または CONFIRMED 状態)。
  • 推定所要時間が変更された場合(注文が IN_TRANSIT の場合に推定所要時間がより正確になるよう更新されるなど)。

ユーザーの期待を効果的に管理するため、見積もりは慎重に行い、固定の日時ではなく日付と時刻の範囲を指定します。可能な限り、交通状況などの変動を考慮してください。たとえば、配送予定日が午後 1 時の注文の場合、午後 12 時 45 分(下限)から午後 1 時 15 分(上限)の目安を送信できます。

注文管理アクションを提供する

注文の更新を送信する際に、OrderManagementAction の形式で注文の管理に役立つリソースをお客様に提供できます。注文後、進捗状況の確認、変更、キャンセルのために、販売者または注文を担当するレストランへの連絡が必要になることがあります。

OrderManagementAction を使用すると、ユーザーはメール、通話、または URL へのリンクをデバイスから直接行うことができます。OrderManagementAction には、お客様に送信する注文確認メールと同じ情報を使用します。

注文管理操作には次のタイプがあります。

  • CUSTOMER_SERVICE: カスタマー サービスに問い合わせるためのアクションをユーザーに提供します。この管理操作の種類は、注文の更新に必須です。
  • EMAIL: 指定したメールアドレスにメールを送信するアクションを顧客に提供します。
  • CALL: 指定された電話番号に発信するアクションをユーザーに提供します。
  • VIEW_DETAIL: 注文の詳細を表示するアクションをお客様に提供します。

各注文の更新には、注文管理アクションを少なくとも 1 つ含める必要があります。ただし、提供される注文管理操作は、注文の状態によって異なる場合があります。たとえば、注文が CONFIRMED ステータスの場合、CUSTOMER_SERVICE アクションはカスタマー サービスの電話番号を指すことができます。その注文ステータスが IN_TRANSIT に更新されると、CUSTOMER_SERVICE アクションがフルフィルメント レストランの電話番号を指すことができます。

注文の更新情報を送信しています

AsyncOrderUpdateRequestMessage メッセージ タイプを使用して、注文の更新を Ordering End-to-End サービスに送信します。Google は AsyncOrderUpdateResponseMessage を返します。たとえば、注文が有効で承認されたことをお客様に伝える場合は、AsyncOrderUpdateRequestMessage を送信し、ラベル Accepted by restaurant を使用して、注文のステータスを CONFIRMED に変更します。

注文更新の図

注文更新メッセージを設定する

Google に AsyncOrderUpdateRequestMessage を送信する場合は、OrderUpdate フィールドを使用して注文の状態に関する情報を含める必要があります。

次の例は、各注文状態のサンプル AsyncOrderUpdateRequestMessage を示しています。

CONFIRMED

この例は、領収書と配送予定日で注文が確定されたことをユーザーに通知する注文更新リクエストの例を示しています。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CONFIRMED",
        "label": "Provider confirmed"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "2017-07-17T13:00:00Z/2017-07-17T13:30:00Z"
      }
    }
  }
}
    

棄却

この例では、注文が不承認になった理由を通知してユーザーに通知する注文更新リクエストの例を示します。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "REJECTED",
        "label": "Order rejected"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "rejectionInfo": {
        "type": "UNKNOWN",
        "reason": "Sorry, the restaurant cannot take your order right now."
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
      "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
      "foodOrderErrors": [
        {
        "error": "NO_CAPACITY",
        "description": "Sorry, the restaurant cannot take your order right now."
        }
      ]
      }
    }
  }
}
    

CANCELLED

以下の例は、注文がキャンセルされた理由とキャンセルの理由をユーザーに通知する注文更新リクエストを示しています。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CANCELLED",
        "label": "Order cancelled"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "cancellationInfo": {
        "reason": "Customer requested"
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

IN_PREPARATION

この例は、料理が準備中であることをユーザーに通知する注文更新リクエストの例を示しています。

{
  "isInSandbox":true,
  "customPushMessage":{
    "orderUpdate":{
      "actionOrderId":"sample_action_order_id",
      "orderState":{
        "state":"IN_PREPARATION",
        "label":"Order is being prepared"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime":"2018-04-15T11:30:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension":{
        "@type":"type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601":"PT20M"
      }
    }
  }
}
    

READY_FOR_PICKUP

次の例は、料理を受け取る準備ができたことをユーザーに通知する注文更新リクエストの例です。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "READY_FOR_PICKUP",
        "label": "Order is ready for pickup"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2018-04-15T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
    

IN_TRANSIT

この例では、注文品が配送中であることと配送予定日をユーザーに通知する注文更新リクエストの例を示します。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "IN_TRANSIT",
        "label": "Order is on the way"
      },
      "inTransitInfo": {
        "updatedTime": "2017-07-17T12:00:00Z"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
  

FULFILLED

次の例は、注文の集荷または配達をユーザーに通知する注文更新リクエストを示しています。

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
      "state": "FULFILLED",
      "label": "Order delivered"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "fulfillmentInfo": {
        "deliveryTime": "2017-05-10T02:30:00.000Z"
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

さまざまなユースケースでの注文更新リクエストのその他の例については、高度な注文更新を実装するをご覧ください。

認証トークンを生成してメッセージを送信する

注文の更新には認証トークンが必要です。これは、メッセージが Ordering End-to-End ウェブサービスからであることを検証できるようにすることです。

プロジェクトに注文の更新を実装する手順は次のとおりです。

  1. 次の手順で認証トークンを生成します。
    1. Google 認証ライブラリを使用して、サービス アカウント ファイルから認証情報を読み取ります。
    2. API スコープ https://www.googleapis.com/auth/actions.fulfillment.conversation を使用してトークンをリクエストします。
  2. このトークンを使用して、認証済みの HTTP POST リクエストを次のエンドポイントに送信します: https://actions.googleapis.com/v2/conversations:send
  3. リクエストの一部として、Content-Type ヘッダーを application/json に設定します。

次の例は、注文の更新を実装する方法を示しています。

Node.js

このコードでは Node.js 用の Google 認証ライブラリを使用しています。

const {auth} = require('google-auth-library')
const request = require('request');
// The service account client secret file downloaded from the Google Cloud Console
const serviceAccountJson = require('./service-account.json')
// order-update.json is a file that contains the payload
const jsonBody = require('./order-update.json')

/**
 * Get the authorization token using a service account.
 */
async function getAuthToken() {
  let client = auth.fromJSON(serviceAccountJson)
  client.scopes = ['https://www.googleapis.com/auth/actions.fulfillment.conversation']
  const tokens = await client.authorize()
  return tokens.access_token;
}

/**
 * Send an order update request
 */
async function sendOrderUpdate() {
  const token = await getAuthToken()
  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    url: 'https://actions.googleapis.com/v2/conversations:send',
    body: jsonBody,
    json: true
  },
  (err, res, body) => {
    if (err) { return console.log(err); }
    console.log(`Response: ${JSON.stringify(res)}`)
  })
}
    

Python

このコードは Python 用の Google 認証ライブラリを使用します。

from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession
import json

# service-account.json is the service account client secret file downloaded from the
# Google Cloud Console
credentials = service_account.Credentials.from_service_account_file(
    'service-account.json')

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/actions.fulfillment.conversation'])

authed_session = AuthorizedSession(scoped_credentials)

# order-update.json is a file that contains the payload
json_payload=json.load(open('order-update.json'))

response = authed_session.post(
    'https://actions.googleapis.com/v2/conversations:send',
    json=json_payload)
    

Java

このコードは Java 用の Google 認証ライブラリを使用しています。

/**
 * Get the authorization token using a service account.
 */
private static String getAuthToken() {
  InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json");
  ServiceAccountCredentials.Builder credentialsSimpleBuilder =
      ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder();
  credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/actions.fulfillment.conversation"));
  AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken();
  return accessToken.getTokenValue();
}

/**
 * Send an order update request
 */
public void sendOrderUpdate() {
  String authToken = getAuthToken();
  // Execute POST request
  executePostRequest("https://actions.googleapis.com/v2/conversations:send",
      authToken, "update_order_example.json",);
}
    

エラーなしで注文が正常に更新されると、Google は空のペイロードを含む HTTP 200 レスポンスを返します。更新の形式が正しくないなど、問題が発生した場合は、エラーが返されます。