사용자가 음식 주문 수령 및 배달을 미리 예약할 수 있도록 처리에 지원을 추가할 수 있습니다. 처리에서 이 지원을 구현하기 전에 인벤토리 피드 스키마(AdvanceServiceDeliveryHoursSpecification
)에 설명된 대로 사용자가 고급 주문을 할 수 있는 시간을 지정하는 서비스 인벤토리 피드를 만듭니다.
사전 주문 슬롯
Google은 식당 또는 서비스의 처리 시간을 기준으로 (AdvanceServiceDeliveryHoursSpecification
에 정의됨) 최대 7일 전에 15분 단위로 사전 주문 슬롯을 제안합니다.
제안된 사전 주문 슬롯을 가져오려면 결제 시 FoodCartExtension
객체의 fulfillmentPreference
필드에서 다음 값을 사용합니다.
PickupInfo.pickupTimeIso8601
DeliveryInfo.deliveryTimeIso8601
결제 시 고급 주문 구현
아래 표에는 사용자가 주문하려고 할 때 결제 시 처리 응답을 구현할 수 있는 방법이 나열되어 있습니다.
시나리오 | 처리 동작 |
---|---|
요청된 시간대에 대해 사전 주문이 처리될 수 있습니다. | 동일한 슬롯으로 ProposedOrder 를 만들어 P0M ('최대한 빨리') 또는 FUTURE_SLOT 장바구니를 수락합니다. 슬롯을 허용하는 결제 응답의 예는 이 코드 스니펫을 참조하세요. |
요청된 시간대에 대해 사전 주문을 처리할 수 없습니다. | 처리는 다음을 실행해야 합니다.
대체 슬롯을 제안하는 결제 응답의 예는 이 코드 스니펫을 참조하세요. |
주문 처리를 위한 대체 슬롯
결제 시 Google에서 제안한 미리 주문 시간대가 적합하지 않은 경우 처리에서 CheckoutResponseMessage
객체를 사용하여 대안을 제안할 수 있습니다.
대체 사전 주문 슬롯을 지정하려면 결제 요청에
FoodErrorExtension
로 응답하고 다음 값을 설정합니다.
foodOrderErrors
매개변수에서 오류 유형 (예:UNAVAILABLE_SLOT
,NO_CAPACITY
,CLOSED
)을 지정합니다.correctedProposedOrder
매개변수에서availableFulfillmentOptions
를 통해 대체P0M
또는FUTURE_SLOT
값을 제공합니다.
대체 슬롯은 주문일로부터 7일 이내여야 하며 사용자가 요청한 장바구니를 처리할 수 있는 모든 슬롯을 포함해야 합니다.
예를 들어 점심 특판이 월요일부터 금요일, 오전 11시부터 오후 1시까지만 제공된다고 가정해 보겠습니다. 그런 다음 사용자는 점심 스페셜을 장바구니에 추가하려고 하지만 선택한 시간대를 사용할 수 없습니다. 이 경우 처리는 장바구니에 점심 특별 메뉴를 유지하고 다음 7일 동안 오전 11시~오후 1시 시간대만 반환해야 합니다.
응답에서 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_SLOT
또는 UNKNOWN
)를 포함해야 합니다.
제공업체에서 주문을 수락하면 OrderState
객체에서
주문 상태를 CREATED
에서 CONFIRMED
로 업데이트합니다. 사용자에게 보내는 확인 이메일에 선택한 시간대를 포함합니다.
처리에서 나중에 음식점에 주문을 전송하면 비동기 주문 업데이트 작업을 사용하여 Google에 업데이트를 전송합니다.
처리 제출 주문 응답 또는 후속 비동기 주문 업데이트의 OrderUpdate
객체에 다음과 같이 설정된 값이 있는 estimatedFulfillmentTimeIso8601
를 포함합니다.
- 주문 상태가
CREATED
또는CONFIRMED
이면 사용자가 고급 주문에 대해 예약한 배달 또는 수령 시간으로 값을 설정합니다. - 식당이나 서비스의 예상 배달 시간이 더 정확한 경우 값을 배달 또는 수령 예상 시간으로 설정합니다.
예: 전송 슬롯이 있는 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
또는 ServiceDeliveryHoursSpecification
)은 주문을 처리할 수 있는 시기를 정의합니다.AdvanceServiceDeliveryHoursSpecification
다음 예에서는 서비스의 사전 주문을 수락하는 시간을 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월 27일에 배송되도록 12월 25일에 사전 주문을 할 수 있습니다.
- 사용자는 12월 22일에 배송되도록 12월 22일에 사전 주문을 할 수 없습니다.
{ "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월 27일에 배송되도록 12월 25일에 사전 주문을 할 수 있습니다.
- 사용자는 12월 22일에 배송되도록 12월 22일에 사전 주문을 할 수 없습니다.
{ "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" } ] }
다음 샘플 서비스는 연중무휴 24시간 주문을 받고 평일 오전 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초의 주문을 받습니다. 고객은 1시간 이내에 배송하도록 선택하거나 시간대 중 하나를 선택할 수 있습니다.
... { "@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분에 문을 여는 경우를 보여줍니다. 연중무휴 24시간 주문은 불가합니다.
... { // 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" } } ] } ...