异步订单更新

客户提交食品订单后,您可以向订购端到端服务发送订单更新消息,通知我们发生的更改。

以下是发送订单更新的一些常见原因:

  • 订单的预计履单时间变为可用状态或发生变化。
  • 订单的状态会发生变化。
  • 该订单无法再履行。
  • 订单中所包含的菜单项的价格发生了变化。
  • 客户可以通过新的方式管理订单,例如客户服务或餐厅电话号码。
  • 订单收据可供使用。

以下部分将详细介绍如何使用订单更新来解决这些不同场景。

转换订单状态

订单有六种可能的状态。下图概述了这些状态及其可能的转换:

订单状态转换

当客户首次提交订单时,订单的状态为 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 以将订单状态更改为 CONFIRMED,并添加标签 Accepted by restaurant

订单更新示意图

设置订单更新消息

向 Google 发送 AsyncOrderUpdateRequestMessage 时,您必须使用 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."
        }
      ]
      }
    }
  }
}
    

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

已完成

以下示例展示了一个订单更新请求示例,该请求会通知用户已自提或配送订单:

{
  "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 Auth 库从您的服务帐号文件中读取凭据。
    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 会返回错误。