非同步訂單更新

客戶提交餐點訂單後,您可以傳送訂單更新訊息給 Ordering End-to-End 服務,通知我們有變更。

以下是傳送訂單更新的常見原因:

  • 訂單的預估交貨時間會顯示或變更。
  • 訂單狀態會變更。
  • 系統無法再處理訂單。
  • 訂單中包含的菜單項目價格有所異動。
  • 客戶可以透過新方式管理訂單,例如客戶服務或餐廳電話號碼。
  • 訂單的收據會隨即顯示。

接下來的章節將詳細說明如何使用訂單更新功能處理這些不同情況。

轉換訂單狀態

訂單可能會有六種狀態。下圖概略說明這些狀態及其可能的轉換:

訂單狀態轉換

顧客初次提交訂單時,訂單的狀態會從 CREATEDCONFIRMEDREJECTED 開始。只要狀態轉換有效,您就可以傳送訂單更新訊息來更新訂單狀態。當合作夥伴的平台無法立即確認或拒絕訂單時,就會使用 CREATED 狀態。舉例來說,如果消費者透過外送集結平台下單,運送整合服務接收 Google 傳送的運送資訊,並將資訊傳送給餐廳。餐廳收到並確認訂單可供供應後,狀態可改為 CONFIRMED,否則為 REJECTED

處於 CONFIRMED 狀態的訂單會接著移至 IN_PREPARATION 狀態。視訂單是自取或外送而定,接著使用 READY_FOR_PICKUPIN_TRANSIT 狀態。餐點送達或自取後,訂單會設為 FULFILLED 狀態。

如果允許客戶取消訂單,您可以使用 CANCELLED 狀態。訂單處於 CREATEDCONFIRMEDIN_PREPARATIONREADY_FOR_PICKUPIN_TRANSIT 狀態時,即可取消訂單。你的訂單端到端服務應根據你的取消政策和取消時的付款狀態核發退款。

端對端訂購服務不必支援所有可用的狀態和轉換。不過,訂單的最終狀態必須FULFILLEDREJECTEDCANCELLED

提供預估交貨時間

您可以向使用者提供訂單可供自取 (或送達) 的預估時間範圍。請使用 FoodOrderUpdateExtensionestimatedFulfillmentTimeIso8601 欄位,提供客戶訂單可供取貨或送達的預估時間範圍。

在下列時間傳送 estimatedFulfillmentTimeIso8601

  • 預估時間可用時,最好是訂單處於 CREATEDCONFIRMED 狀態。
  • 預估時間變更時,例如在訂單為 IN_TRANSIT 時更新預估時間,以便更準確。

為了有效管理使用者的期望,請務必謹慎估算,並提供日期和時間範圍,而非固定日期和時間。您應盡可能將路況等變化因素納入考量。舉例來說,如果訂單的預估送達時間為下午 1 點,您可以傳送 12 點 45 分 (下限) 到下午 1 點 15 分 (上限) 的預估時間。

提供訂單管理動作

傳送訂單更新時,您可以以 OrderManagementAction 的形式向客戶提供資源,協助他們管理訂單。消費者下單後,可能需要與你或負責處理訂單的餐廳聯絡,以便追蹤進度、變更或取消訂單。

OrderManagementAction 可讓客戶直接透過裝置傳送電子郵件、撥打電話或連結至網址。OrderManagementAction 中的資訊必須與您傳送給使用者的電子郵件訂單確認信中的資訊相同。

訂單管理動作包括下列類型:

  • CUSTOMER_SERVICE:提供客戶與客戶服務團隊聯絡的動作。如要更新訂單,必須使用這類管理動作。
  • EMAIL:提供客戶動作,讓他們可以傳送電子郵件至提供的電子郵件地址。
  • CALL:提供客戶撥打所提供電話號碼的動作。
  • VIEW_DETAIL:提供可讓消費者查看訂單詳細資料的動作。

每項訂單更新都必須包含至少一項訂單管理動作。不過,提供的訂單管理動作可能會因訂單狀態而異。舉例來說,當訂單處於 CONFIRMED 狀態時,CUSTOMER_SERVICE 動作可以指向客戶服務電話號碼。當該訂單狀態更新為 IN_TRANSIT 時,CUSTOMER_SERVICE 動作可以指向訂單執行餐廳的電話號碼。

傳送訂單更新

您可以使用 AsyncOrderUpdateRequestMessage 訊息類型,將訂單更新內容傳送至 Ordering 端對端服務。Google 會回傳 AsyncOrderUpdateResponseMessage。舉例來說,如果您想通知客戶其訂單有效且已接受,可以傳送 AsyncOrderUpdateRequestMessage,將訂單狀態變更為 CONFIRMED,並加上標籤 Accepted by restaurant

訂單更新圖表

設定訂單更新訊息

AsyncOrderUpdateRequestMessage 傳送給 Google 時,您必須使用 OrderUpdate 欄位提供訂單狀態相關資訊。

以下範例顯示每個訂單狀態的範例 AsyncOrderUpdateRequestMessage

已確認

以下範例顯示訂單更新要求的範例,通知使用者訂單已確認,並提供收據和預估送達時間。

{
  "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."
        }
      ]
      }
    }
  }
}
    

已取消

本範例顯示訂單更新要求的範例,可通知使用者訂單已取消,並提供取消原因。

{
  "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"
            }
          }
        }
      ]
    }
  }
}
    

如需其他不同用途的訂單更新要求範例,請參閱「實作進階訂單更新」。

產生授權權杖並傳送訊息

訂單更新需要授權權杖,以便端對端訂購服務驗證訊息是否來自端對端訂購網路服務。

如要為專案實作訂單更新,請按照下列步驟操作:

  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 回應,並附上空白酬載。如果發生問題 (例如更新格式不正確),Google 會傳回錯誤。