非同步訂單更新

客戶提交餐點訂單後,您可以傳送訂單更新訊息到「訂購端對端」服務,讓我們通知變更內容。

以下是傳送訂單最新資訊的常見原因:

  • 訂單的預計到達時間確定後有商品可用,或有變動。
  • 訂單狀態會改變,
  • 無法再履行訂單。
  • 訂單中的菜單品項價格已變更。
  • 客戶有新的訂單管理方式,例如客戶服務或餐廳電話號碼。
  • 訂單收據已備妥。

以下各節詳細說明如何使用訂單更新因應這些不同的情況。

轉換訂單狀態

訂單有六種可能的狀態。下圖概略說明這些狀態及可能的轉場效果:

訂單狀態轉換作業

客戶首次提交訂單時,訂單的狀態為 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:00,您可以將訂單的預計晚上 12:45 (下限) 傳送至下午 1:15 (上限)。

提供訂單管理動作

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

OrderManagementAction 可讓客戶直接從自己的裝置傳送電子郵件、撥打電話或連結至網址。請使用 OrderManagementAction 中要傳送給使用者的電子郵件訂單確認信中提供的資訊。

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

  • CUSTOMER_SERVICE:提供客戶聯絡客戶服務的動作。您必須選擇這個管理動作類型才能更新訂單。
  • EMAIL:提供客戶動作,以便將電子郵件傳送至提供的電子郵件地址。
  • CALL:提供客戶行動號召,方便他們撥打提供的電話號碼。
  • VIEW_DETAIL:提供客戶動作,讓他們查看訂單詳細資料。

每筆訂單更新都必須包含至少一個訂單管理動作。不過,提供的訂單管理操作可能因訂單狀態而有所不同。舉例來說,當訂單處於 CONFIRMED 狀態時,CUSTOMER_SERVICE 動作可以指向客戶服務電話號碼。當訂單狀態更新為 IN_TRANSIT 時,CUSTOMER_SERVICE 動作可以指向出貨餐廳的電話號碼。

正在傳送訂單更新

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

訂單更新圖表

設定訂單更新訊息

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

已完成

以下範例顯示訂單更新要求範例,透過通知使用者已收到訂購或訂購的商品:

{
  "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 會傳回錯誤。