Сопоставление каналов меню и элементов корзины выполнения
Когда клиенты добавляют товары из вашего фида меню в корзину и оформляют заказ, Google отправляет эти товары в вашу конечную точку выполнения, чтобы проверить их цену и наличие. После подтверждения цены и наличия товара клиент может разместить заказ. В этом разделе показано, как сопоставить элементы ленты меню с элементами корзины выполнения.
Примеры в этом разделе представляют собой урезанные версии ленты меню и схемы корзины. Показаны только поля, необходимые для иллюстрации сопоставления между лентой меню и объектом «Корзина». Полные схемы см. в разделе Menu
и Cart
.
Элементы в ленте Menu
, добавленные в корзину, отправляются в объект Cart
как для оформления заказа, так и для отправки заказа.
- Простой
MenuItem
представлен какLineItem
в массивеlineItems
, гдеofferId
— этоoffer.id
выбранного пункта меню в ленте меню. -
MenuItem
с обязательнымMenuItemOption
представлен какLineItem
в массивеlineItems
, гдеofferId
— этоoffer.id
выбранного пункта меню из ленты меню. -
AddOnMenuItem
LineItem
представлен какFoodItemOption
в массивеoptions
FoodItemExtension
. У каждого параметра естьofferId
, который соответствуетoffer.id
выбранного пункта меню дополнения из фида меню. Обратите внимание, что AddOnMenuItem также может иметь вложенные AddOnMenuItem(ы), которые представлены какsubOptions
внутри каждого параметра.
Следующие примеры сопоставляют пункты меню между лентой меню и корзиной выполнения.
JSON
Этот пример содержит список простых пунктов меню.
Пункты меню в ленте меню:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
Пункты меню, сопоставленные с корзиной выполнения:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*p_1)", "nanos": "cent(q_1*p_1)" } }, "quantity": "q_1" }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
JSON
Этот пример содержит пункт меню с одним или несколькими AddOnMenuItems.
Пункты меню в ленте меню:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "MenuAddOnSection", "@id": "menuaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ] } ] } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
Пункты меню, сопоставленные с корзиной выполнения:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*addon_p_2)", "nanos": "cent(addon_q_2*addon_p_2)" }, "quantity": "addon_q_2" } ] } }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
JSON
Этот пример содержит элемент меню с параметрами элемента меню AddOnMenuItems и вложенными AddOnMenuItems.
Пункты меню в ленте меню:
{ "@type": "MenuItem", "@id": "menuitem_id_1", "hasMenuItemOptions": [ { "@type": "MenuItemOption", "value": { "@type": "PropertyValue", "name": "OPTION", "value": "Large", "offers": [ { "@type": "Offer", "@id": "menuitem_option_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_addon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_subaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_subaddon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_subaddon_offer_id_1", "price": "subaddon_p_1", "priceCurrency": "USD" } ] } ] } ] } ] } ] } } ] }
Пункты меню, сопоставленные с корзиной выполнения:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_option_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_option_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_option_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))", "nanos": "cent(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))" }, "quantity": "addon_q_2", "subOptions": [ { "offerId": "menuitem_option_subaddon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(subaddon_q_1*subaddon_p_1)", "nanos": "cent(subaddon_q_1*subaddon_p_1)" }, "quantity": "subaddon_q_1" } ] } ] } } ] }
Обработка ошибок
Если у вас возникли проблемы при обработке CheckoutRequestMessage
, вы можете ответить сообщением CheckoutResponseMessage
, которое содержит FoodErrorExtension
, а не CheckoutResponse . Этот ответ можно использовать для выявления одной или нескольких ошибок, произошедших во время обработки.
Есть 2 способа обработки ошибок:
- Устранимые ошибки: пользователю не требуется редактировать корзину, чтобы отправить заказ. Например, если вы определите, что цена товара в
Cart
изменилась, вы можете ответитьFoodOrderError
типа ошибкиPRICE_CHANGED
вместе сcorrectedProposedOrder
иpaymentOptions
. Google информирует пользователя об изменении, но позволяет пользователю отправить его сcorrectedProposedOrder
. При желании пользователь также может вернуться и отредактировать свою корзину. Вы получите либо новоеCheckoutRequestMessage
, либоSubmitOrderRequestMessage
. - Неустранимые ошибки: пользователю необходимо отредактировать свою корзину перед отправкой заказа. Например, если вы определите, что ресторан закрыт, вы можете ответить
FoodOrderError
типа ошибкиCLOSED
. Google информирует пользователя и управляет взаимодействием с обновлением до нового ресторана. Вы получите новоеCheckoutRequestMessage
для новой корзины.
В общем, сделайте ошибки на уровне корзины неустранимыми, а ошибки на уровне товара — исправимыми. Полный список типов ошибок и их значений см. в FoodOrderError
.
Обработка изменений цен
Изменение цен во время оформления заказа
Если при обработке запроса клиента на оформление заказа вы столкнулись с проблемой цены, сделайте следующее:
- Ответьте на
CheckoutRequestMessage
сообщениемCheckoutResponseMessage
, содержащимFoodErrorExtension
, как описано в разделе Обработка ошибок . - В ответе об ошибке используйте
correctedProposedOrder.cart
, чтобы обновить цену до правильного значения. Google получает исправленный заказ и может выдать новоеCheckoutRequestMessage
.
После оформления заказа Google отображает страницу подтверждения заказа конечному пользователю независимо от того, был ли изменен ProposedOrder
или нет.
Если ProposeOrder был исправлен, Google может показать дополнительные предупреждения, чтобы проинформировать пользователя об изменениях. Если пользователь соглашается разместить заказ, запросов на оформление заказа больше не будет. Поток продолжает упорядочивать отправку с исправленным ProposedOrder
.
Однако пользователь всегда может передумать и снова отредактировать свою корзину. Когда их корзина обновляется таким образом, Google отправляет новое CheckoutRequestMessage
.
Изменения цен во время отправки заказа
Если при обработке отправки заказа вы столкнулись с проблемой цены (сработало намерение actions.intent.TRANSACTION_DECISION
), не сообщайте об ошибке и не обновляйте цену в своем ответе. Если цены, количества или другие сведения в SubmitOrderRequestMessage
не соответствуют вашим данным, ответьте, установив для параметра orderState
значение REJECTED
чтобы указать, что заказ не может быть размещен в соответствии с запросом.
Затем, если сведения о заказе и платеже действительны, установите orderState
значение CREATED
или CONFIRMED
. Кроме того, включите actionOrderId
для представления идентификатора заказа в вашей системе. Этот идентификатор необходимо использовать при отправке последующих обновлений.
Если вы не можете обработать платеж и уже отправили SubmitOrderRequestMessage
, вы можете отправить AsyncOrderUpdateRequestMessage
с orderState
, установленным в REJECTED
, чтобы сообщить пользователю, что заказ не будет выполнен.
Изменение цены после отправки заказа
Если вы определите, что цена изменилась по сравнению с ценой, использованной при отправке заказа клиентом, вы можете отправить сообщение AsyncOrderUpdateRequestMessage
, как описано в разделе «Реализация асинхронных обновлений заказов» , с новой ценой.
Чтобы обновить цены с помощью асинхронных обновлений заказов:
- Измените цену в
lineItemUpdates[ x ].price
. Это значение отражает общую стоимость товара, включая надстройки и умноженную на количество. (Для получения дополнительной информации см. описание поляprice
LineItem
.) - Введите объяснение в
lineItemUpdates[ x ].reason
. - Установите
lineItemUpdates[ x ].orderState
значениеCONFIRMED
.
Вы можете попытаться списать средства с платежного средства до или после отправки сообщения AsyncOrderUpdateRequestMessage, по вашему усмотрению. Если транзакция не удалась (возможно, из-за слишком большой разницы цен), отправьте сообщение AsyncOrderUpdateRequestMessage со следующими настройками в OrderUpdate
, чтобы сообщить Google об ошибке:
- Установите для
orderState
значениеREJECTED
. - Опишите сбой в поле
label
.
Проверка оформления заказа
Как обсуждалось в разделе «Шаг 4: реализация Checkout» , ваша конечная точка выполнения должна выполнять проверку каждого входящего CheckoutRequestMessage
и отвечать сообщением CheckoutResponseMessage
.
Вот пример сообщения CheckoutResponseMessage
для успешной проверки:
Вариант использования | Как реализовать |
---|---|
Вариант использования 1. Проверка прошла успешно. | Вернуть ответ CheckoutResponse . Он должен иметь ProposedOrder и PaymentOptions . ProposedOrder включает налоги, сборы и общую стоимость корзины. |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "checkoutResponse": { "proposedOrder": { "id": "sample_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "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": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { // Represents $36.73 "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
Проверка адреса доставки
Ваша конечная точка выполнения должна проверить адрес доставки, содержащийся в каждом CheckoutRequestMessage
.
Если с адресом доставки возникла проблема, например, он находится за пределами зоны действия службы доставки, CheckoutResponseMessage
, возвращаемое вашим выполнением, должно содержать FoodOrderError
соответствующего типа.
Вариант использования | Как реализовать |
---|---|
Вариант использования 1: проверка не удалась, поскольку адрес доставки находится вне диапазона или возникла проблема с адресом доставки. | Возвращает FoodErrorExtension с FoodOrderError типа ошибки OUT_OF_SERVICE_AREA . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "OUT_OF_SERVICE_AREA", "description": "Sorry, the restaurant cannot deliver to your address." } ] } } } ] } } }
Проверка минимальной стоимости заказа
Ваша конечная точка выполнения должна проверять минимальную стоимость заказа каждого CheckoutRequestMessage
.
Если минимальная стоимость заказа не достигнута, CheckoutResponseMessage
, возвращаемое вашим выполнением, должно содержать FoodOrderError
типа ошибки REQUIREMENTS_NOT_MET
.
Вариант использования | Как реализовать |
---|---|
Вариант использования 1: проверка не удалась, поскольку минимальная стоимость заказа не достигнута. | Возвращает FoodErrorExtension с FoodOrderError типа ошибки REQUIREMENTS_NOT_MET . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." } ] } } } ] } } }
Проверка окна заказа
Ваша конечная точка выполнения должна проверять любые факторы, которые могут повлиять на окно заказа каждого CheckoutRequestMessage
.
Например, если ресторан закрыт или больше не принимает заказы в данный момент, CheckoutResponseMessage
, возвращаемое вашим выполнением, должно содержать FoodOrderError
типа ошибки CLOSED
или NO_CAPACITY
соответственно.
Вариант использования | Как реализовать |
---|---|
Вариант использования 1: проверка не удалась, поскольку ресторан закрыт или больше не поддерживается. | Возвращает FoodErrorExtension с FoodOrderError типа ошибки CLOSED . |
Вариант использования 2: проверка не удалась, поскольку ресторан в данный момент занят и не принимает заказы. | Возвращает FoodErrorExtension с FoodOrderError типа ошибки NO_CAPACITY . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "CLOSED", "description": "The restaurant is closed." } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "NO_CAPACITY", "description": "Sorry, the restaurant is busy at the moment." } ] } } } ] } } }
Проверка товаров в корзине
Ваша конечная точка выполнения должна проверять цену и доступность каждого элемента корзины, содержащегося в CheckoutRequestMessage
.
Если доступность или цена изменились, CheckoutResponseMessage
, возвращаемое вашим выполнением, должно содержать FoodOrderError
типа ошибки AVAILABILITY_CHANGED
или PRICE_CHANGED
соответственно.
Вариант использования | Как реализовать |
---|---|
Вариант использования 1: проверка не удалась, поскольку некоторые пункты меню и/или их настройки недействительны или отсутствуют на складе. | Возвращает FoodErrorExtension с correctedProposedOrder , PaymentOptions и FoodOrderError типа ошибки AVAILABILITY_CHANGED . Недопустимые элементы необходимо удалить из CorrectedProposedOrder . |
Вариант использования 2: проверка не удалась, поскольку некоторые пункты меню и/или их настройки недействительны или отсутствуют на складе. Исправленная корзина больше не соответствует требованию минимальной суммы заказа. | Возвращает FoodErrorExtension с FoodOrderError типов ошибок AVAILABILITY_CHANGED и REQUIREMENTS_NOT_MET . |
Вариант использования 3: проверка не удалась, поскольку изменились цены на некоторые пункты меню и/или настройки. | Возвращает FoodErrorExtension с correctedProposedOrder , PaymentOptions и FoodOrderError типа ошибки PRICE_CHANGED . Устаревшие цены необходимо обновить в CorrectedProposedOrder . |
Вариант использования 4: проверка не удалась, поскольку цены на некоторые пункты меню и/или настройки изменились. Исправленная корзина больше не соответствует требованию минимальной суммы заказа. | Возвращает FoodErrorExtension с FoodOrderError типов ошибок PRICE_CHANGED и REQUIREMENTS_NOT_MET . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_1", "description": "The item is no longer available." }, { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_2", "description": "The item is no longer available." } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "AVAILABILITY_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id is no longer available." } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_1", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_2", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "8" } } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "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": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "PRICE_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id price has been updated." "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } } ] } } } ] } } }
Отправить подтверждение заказа
Как обсуждалось в разделе «Шаг 7: реализация заказа на отправку» , ваша конечная точка выполнения должна выполнять проверку каждого входящего SubmitOrderRequestMessage
и отвечать сообщением SubmitOrderResponseMessage
.
Вот пример сообщения SubmitOrderResponseMessage
для успешной проверки:
Вариант использования | Как реализовать |
---|---|
Вариант использования 1: заказ успешно создан. | SubmitOrderResponseMessage с состоянием заказа CREATED . Он должен иметь actionOrderId , userVisibleId , orderManagementActions и estimatedFulfillmentTime . |
Вариант использования 2: заказ отклонен из-за проблем с оплатой. | SubmitOrderResponseMessage со статусом заказа REJECTED . Он должен иметь actionOrderId , userVisibleId , orderManagementActions и rejectionInfo типа PAYMENT_DECLINED . |
Вариант использования 3: заказ отклонен, поскольку пользователь помечен как заблокированный | SubmitOrderResponseMessage со статусом заказа REJECTED . Он должен иметь actionOrderId , userVisibleId , orderManagementActions и rejectionInfo типа INELIGIBLE . |
Вариант использования 4: заказ отклонен, поскольку информация о пользователе неполная или недействительная. | SubmitOrderResponseMessage со статусом заказа REJECTED . Он должен иметь actionOrderId , userVisibleId , orderManagementActions и rejectionInfo типа INELIGIBLE . |
Вариант использования 5: заказ отклонен по неизвестной причине. | SubmitOrderResponseMessage со статусом заказа REJECTED . Он должен иметь actionOrderId , userVisibleId , orderManagementActions и rejectionInfo типа UNKNOWN . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "CREATED", "label": "Order received" }, "updateTime": "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", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "PAYMENT_DECLINED", "reason": "Insufficient funds" }, "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", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, we are not able to take orders from this user" }, "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", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, the phone number must not be blank" }, "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", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "UNKNOWN", "reason": "Sorry, there is something wrong with this order." }, "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", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }