v1 提前下单功能

您可以在履单流程中添加支持,以便用户提前安排自提和外卖订单。在实现此服务支持之前,请创建一个服务商品目录 Feed,用于指定用户下单提前预订的服务时间,如商品目录 Feed 架构 (AdvanceServiceDeliveryHoursSpecification) 中所述。

提前预订空档

Google 会根据餐厅或服务的执行时间(如 AdvanceServiceDeliveryHoursSpecification 中所定义),以 15 分钟为单位,提前最多 7 天建议预订时间。

如需检索建议的提前预约时间,请在结账时使用 FoodCartExtension 对象的 fulfillmentPreference 字段中的以下值:

  • PickupInfo.pickupTimeIso8601
  • DeliveryInfo.deliveryTimeIso8601

在结账时实现预订单

下表列出了在用户尝试下单时,您在结账时实现履单响应的可能方式。

场景 执行方式
可以针对请求的空档时间执行预订。 通过创建具有相同槽位的 ProposedOrder,接受 P0M(“尽快”)或 FUTURE_SLOT 购物车。如需查看接受空位的结账响应示例,请参阅此代码段
无法针对所请求的时段履行预订。 您的执行方式应执行以下操作:
  1. 拒绝请求的 P0MFUTURE_SLOT 购物车,并在 FoodErrorExtension 对象中指明订单无法履单的原因。
    • 如果订单因容量问题而无法履行,请指定错误类型为 NO_CAPACITYFoodOrderError
    • 如果餐厅因打烊而无法履单,请指定错误类型为 CLOSEDFoodOrderError
    • 如果订单因其他原因而无法履行,请指定错误类型为 UNAVAILABLE_SLOTFoodOrderError
  2. 如果可能,请在 correctedProposedOrder 中提供替代 P0MFUTURE_SLOT 值。 这些值应为从当前时间开始的接下来 7 天的所有有效履单时间。在适用的情况下添加 P0M 槽。

如需查看提议备用空档的结账响应示例,请参阅此代码段

用于履单的备用空档

结账时,如果 Google 建议的提前订购时间不合适,您的履单服务可以使用 CheckoutResponseMessage 对象建议替代时间。

如需指定其他提前订购时间,请使用 FoodErrorExtension 响应结账请求,并设置以下值:

  1. foodOrderErrors 参数中,指定错误类型(例如 UNAVAILABLE_SLOTNO_CAPACITYCLOSED)。
  2. correctedProposedOrder 参数中,通过 availableFulfillmentOptions 提供替代 P0MFUTURE_SLOT 值。

备选空档应为从下单时间起的接下来 7 天,并且应包含可满足用户所请求购物车的所有空档。

例如,假设午餐特惠仅在周一至周五上午 11 点至下午 1 点提供。然后,用户尝试将午餐特惠添加到购物车,但所选的空档不可用。在这种情况下,您的履单服务应保留购物车中的午餐特惠,并仅返回接下来 7 天的 11:00 至 13:00 时段

您应在响应中省略 correctedProposedOrder.Cart.fulfillmentPreference 对象。

如果没有空闲的空档,或者餐厅或服务不支持提前订餐,则您无需提供 correctedProposedOrder

在餐厅或服务可以接收预订时,在提前订单的结账请求和响应流程中,您的履单服务与 Google 之间会交换 JSON 消息。请参阅以下示例。

示例:包含送货时间范围的 CheckoutRequest

以下代码段展示了一个包含提前订单送货时间的结账请求示例。

{
  "inputs": [
    {
      "intent": "actions.foodordering.intent.CHECKOUT",
      "arguments": [
        {
          "extension": {
            "@type": "type.googleapis.com/google.actions.v2.orders.Cart",
            "merchant": {
              "id": "https://www.exampleprovider.com/merchant/id1",
              "name": "Cucina Venti"
            },
            "lineItems": [
              {
                "name": "Sizzling Prawns Dinner",
                "type": "REGULAR",
                "id": "sample_item_offer_id_1",
                "offerId": "https://www.exampleprovider.com/menu/item/offer/id1",
                "quantity": 1,
                "price": {
                  "type": "ESTIMATE",
                  "amount": {
                    "currencyCode": "USD",
                    "units": "16",
                    "nanos": 750000000
                  }
                },
              }
            ],
            "extension": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
              "fulfillmentPreference": {
                "fulfillmentInfo": {
                  "delivery": {
                    // Deliver at 6:30PM.
                    "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                  }
                }
              },
              "location": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

示例:接受相应空位的 CheckoutResponse

以下代码段显示了一个结账响应示例,其中您的履单服务接受了建议的提前订购时间。

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "checkoutResponse": {
              "proposedOrder": {
                "id": "sample_proposed_order_id_1",
                "cart": {
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Falafel Bite"
                  },
                  "lineItems": [
                    {
                      "name": "Sizzling Prawns Dinner",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_1",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id1",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "16",
                          "nanos": 750000000
                        }
                      },
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          // Same as the time in the request.
                          "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                        }
                      }
                    },
                    "location": {
                      ...
                     }
                   }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    // Represents $16.75
                    "currencyCode": "USD",
                    "units": "16",
                    "nanos": 750000000
                  }
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  // Send whole proposed order back.
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        // Same as the time in the request.
                        "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                      }
                    }
                  ]
                }
              },
              "paymentOptions": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

示例:包含备用槽的 CheckoutResponse

以下代码段显示了一个结账响应示例,其中您的履单服务提出了其他提前订购时间空档。请注意,您应在响应中省略 correctedProposedOrder.Cart.fulfillmentPreference 对象。

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "UNAVAILABLE_SLOT", // Cart level error
                  "description": "The restaurant is closed."
                }
              ],
              "correctedProposedOrder": {
                // Send whole original cart back,
                // without the fulfillmentPreference.
                "cart": {
                  ...
                },
                "otherItems": {
                  ...
                },
                "totalPrice": {
                  ...
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T19:00:00-07:00"
                      }
                    },
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T19:30:00-07:00"
                      }
                    },
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T20:00:00-07:00"
                      }
                    }
                  ]
                }
              },
              "paymentOptions": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

在提交订单时实现预订

提交订单时,如果预订时间出现问题,您的 SubmitOrderResponseMessage 应在 RejectionInfo 对象中包含原因(例如 UNAVAILABLE_SLOTUNKNOWN)。

当提供商接受订单时,在 OrderState 对象中将订单状态从 CREATED 更新为 CONFIRMED。在发送给用户的确认电子邮件中添加所选的时间段。

如果您的执行方式稍后将订单发送给餐厅,请使用异步订单更新操作向 Google 发送更新。

在履单方的提交订单响应或后续异步订单更新的 OrderUpdate 对象中,添加一个 estimatedFulfillmentTimeIso8601,并将其值设置如下:

  • 当订单状态为 CREATEDCONFIRMED 时,将此值设置为用户为其预订单安排的送货或自提时间。
  • 如果餐厅或服务提供更准确的预计送达时间,请将此值设置为预计送达时间或自提时间。

示例:包含送货时间范围的 SubmitOrderRequest

以下代码段展示了一个提交订单请求示例,其中指明了用户选择的提前预订时间。

{
  "inputs": [
    {
      "intent": "actions.intent.TRANSACTION_DECISION",
      "arguments": [
        {
          "transactionDecisionValue": {
            "order": {
              "finalOrder": {
                "cart": {
                  "notes": "Guest prefers their food to be hot when it is delivered.",
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Cucina Venti"
                  },
                  "lineItems": [
                    {
                      "name": "Sizzling Prawns Dinner",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_1",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id1",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "16",
                          "nanos": 750000000
                        }
                      }
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                        }
                      }
                    }
                    "contact": {
                      ...
                    }
                  }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    "currencyCode": "USD",
                    "units": "16",
                    "nanos": 750000000
                  }
                },
                "id": "sample_final_order_id",
                "extension": {
                  // Send whole proposed order back.
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                      }
                   ]
                }
              },
              "googleOrderId": "sample_google_order_id",
              "orderDate": "2017-07-17T12:00:00Z",
              "paymentInfo": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

示例:接受订单的 SubmitOrderResponse

以下代码段显示了一个提交订单响应的示例,其中您的履单方确认已接受用户的预订单。

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "CREATED",
                "label": "Order placed"
              },
              "receipt": {
                "userVisibleOrderId": "userVisibleId1234"
              },
              "updateTime": "2017-07-17T12:00:00Z",
              "orderManagementActions": [
                ...
              ],
              "infoExtension": {
                 "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
                 // Same as the user selected time.
                 "estimatedFulfillmentTimeIso8601": "2017-12-14T18:30:00-07:00"
              }
            }
          }
        }
      ]
    }
  }
}

示例:由于没有空闲的槽,SubmitOrderResponse 会拒绝订单

以下代码段显示了一个提交订单响应的示例,其中您的履单服务因空档不可用而拒绝了用户的预订。

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Unavailable slot"
              },
              "rejectionInfo": {
                // Note that this UNAVAILABLE_SLOT is different from the enum
                // with the same name proposed for FoodOrderError.
                "state": "UNAVAILABLE_SLOT",
                "label": "Unavailable slot"
              },
              "updateTime": "2017-07-17T12:00:00Z",
              "orderManagementActions": [
                ...
              ]
            }
          }
        }
      ]
    }
  }
}

提前下单示例

AdvanceServiceDeliveryHoursSpecification 类型可用于指定送货或自提时间,以便用户提前安排订单。

注意 :您必须为服务执行指定两个单独的时间范围:订购时间范围,用于指定用户可以下单的时间;执行时间范围,用于指定订单的执行时间。OpeningHoursSpecification 对象定义了用户何时可以下单。其子执行时间(ServiceDeliveryHoursSpecificationAdvanceServiceDeliveryHoursSpecification)定义了订单何时可以执行。

以下示例定义了服务接受预订的时间,服务间隔为 15 分钟。

{
  "hoursAvailable": [
    {
      "@type": "OpeningHoursSpecification",
      "opens": "T00:00:00", // Ordering available 24 hours
      "closes": "T23:59:59",
      "deliveryHours": [
        {
          "@type": "ServiceDeliveryHoursSpecification",
          "opens": "T09:00:00", // ASAP orders b/w 9am and 8:59:59pm
          "closes": "T21:00:00",
          "deliveryLeadTime": {
            "value": "60",
            "unitCode": "MIN"
          }
        },
        {
          "@type": "AdvanceServiceDeliveryHoursSpecification",
          "opens": "T10:00:00",  // Delivery between 10AM and 7:59:59PM
          "closes": "T20:00:00",
          "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart (ISO8601)
          "advanceBookingRequirement": {
            "minValue": 60,   // The slot should be at least 60 mins away
            "maxValue": 8640, // but not more than 6 days away
            "unitCode": "MIN"
          }
        }
      ]
    }
  ]
}

以下示例展示了如何指定服务在圣诞节当天接受当天送达的订单,但不接受当天安排提前送达的订单。此示例支持以下场景:

  • 用户可以在 12 月 25 日下单,以便当天送达。
  • 用户可以在 12 月 25 日提前下单,安排在 12 月 27 日送达。
  • 用户无法在 12 月 22 日提前下单,并安排在 12 月 25 日送达。
{
  "specialOpeningHoursSpecification": {
    "@type": "AdvanceServiceDeliveryHoursSpecification",
    "validFrom": "2018-12-25T00:00:00-07:00",
    "validThrough": "2018-12-26T00:00:00-07:00",
    "opens": "T00:00:00", // No advance ordering
    "closes": "T00:00:00"
  }
}

以下示例展示了如何指定服务不为当天订单或安排在圣诞节当天送达的预订订单提供,但为安排在之后送达的预订订单提供。此示例支持以下场景:

  • 用户无法在 12 月 25 日当天下单选择当日送达。
  • 用户可以在 12 月 25 日提前下单,安排在 12 月 27 日送达。
  • 用户无法在 12 月 22 日提前下单,安排在 12 月 25 日送达。
{
  "specialOpeningHoursSpecification": [
    {
      "@type": "ServiceDeliveryHoursSpecification",
      "validFrom": "2018-12-25T00:00:00-07:00",
      "validThrough": "2018-12-26T00:00:00-07:00",
      "opens": "T00:00:00", // No ASAP ordering on Christmas
      "closes": "T00:00:00"
    },
    {
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "validFrom": "2018-12-25T00:00:00-07:00",
      "validThrough": "2018-12-26T00:00:00-07:00",
      "opens": "T00:00:00", // Orders cannot be scheduled for Christmas
      "closes": "T00:00:00"
    }
  ]
}

以下示例服务全天候接受订单,并在工作日上午 10 点至下午 2:59:59 送货:

...
{
  "@type": "OpeningHoursSpecification",
  "opens": "T00:00:00",
  "closes": "T23:59:59",
  "deliveryHours": {
    "@type": "AdvanceServiceDeliveryHoursSpecification",
    "opens": "T10:00:00", // Delivery starts at 10:00AM
    "closes": "T15:00:00", // Delivery ends at 3:00PM. Delivery from 10AM-2:59:59PM.
    "dayOfWeek": [
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday"
    ],
    "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart
    "advanceBookingRequirement": {
      "minValue": 60,   // The slot should be at least 60 mins away
      "maxValue": 8640, // but not more than 6 days away
      "unitCode": "MIN"
    }
  }
}
...

以下示例服务每天上午 8 点至下午 4:59:59 接受订单,客户可以选择一小时内送达,也可以选择以下任一时间段:

...
{
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",  // Ordering opens at 8:00AM
  "closes": "T17:00:00",  // Ordering closes at 5:00PM, last order at 4:59:59PM
  "deliveryHours": [
    {
      "@type": "ServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "deliveryLeadTime": {
        "@type": "QuantitativeValue",
        "value": "60", // If no exact deliveryLeadTime, put a maximum time
        "unitCode": "MIN"
      }
    },
    {
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart
      "advanceBookingRequirement": {
        "minValue": 90,   // The slot should be at least 90 mins away
        "maxValue": 8640, // but not more than 6 days away
        "unitCode": "MIN"
      }
    }
  ]
}
...

以下示例展示了商店的工作日营业时间为上午 8 点至下午 4 点 59 分 59 秒,周末营业时间为上午 8 点至下午 6 点 59 分。我们不全天候接受订单。

...
{
  // On weekdays, ordering open from 8AM-4:59:59PM.
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",
  "closes": "T17:00:00",
  "dayOfWeek": [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday"
  ],
  "deliveryHours": [
    {
      // Fulfillment between 8AM-4:59:59PM on weekdays.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "dayOfWeek": [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    },
    {
      // Fulfillment between 8AM-6:59:59PM on weekends (even for orders placed on a
      // weekday).
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T19:00:00",
      "dayOfWeek": [
        "Saturday",
        "Sunday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    }
  ]
},
{
  // On weekends, one can place orders upto 6:59:59PM.
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",
  "closes": "T19:00:00",
  "dayOfWeek": [
    "Saturday",
    "Sunday"
  ],
  "deliveryHours": [
    {
      // But fulfillment on weekdays is only till 4:59:59PM.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "dayOfWeek": [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    },
    {
      // Fulfillment on weekends is till 6:59:59PM.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T19:00:00",
      "dayOfWeek": [
        "Saturday",
        "Sunday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    }
  ]
}
...