Asignación de feeds de menú y artículos del carrito de entregas
Cuando los clientes agregan artículos del feed del menú al carrito y finalizan la compra, Google envía esos artículos a tu extremo de entrega para verificar el precio. disponibilidad. Una vez validados los precios y la disponibilidad, el cliente puede realizar el pedido. Esta sección ilustra cómo asignar elementos del feed de menú a artículos del carrito de entrega.
Las muestras de esta sección son versiones simplificadas del feed del menú y del Carrito
. Solo los campos relevantes para ilustrar la asignación entre el feed de menú y
se muestran el objeto Cart. Para ver los esquemas completos, consulta Menu
y Cart
.
Los artículos del feed Menu
que se agregan al carrito se envían en Cart
.
objeto para la confirmación de la compra y el envío de pedidos.
- Un
MenuItem
simple se representa como unLineItem
enlineItems
. array en el queofferId
es eloffer.id
del elemento de menú seleccionado en el menú feed. - Un
MenuItem
con unMenuItemOption
obligatorio se representa como unLineItem
en el arraylineItems
conofferId
el que se seleccionó de la opción de elemento de menúoffer.id
del feed del menú. - Un
AddOnMenuItem
de unLineItem
se representa como unFoodItemOption
en el arrayoptions
deFoodItemExtension
. Cada opción tiene unofferId
que corresponde al menú de complementos seleccionado.offer.id
del elemento en el feed de menú. Ten en cuenta que un AddOnMenuItem también puede tener AddOnMenuItem(s) anidados que se representan comosubOptions
dentro de cada uno de 12 a 1 con la nueva opción de compresión.
En los siguientes ejemplos, se asignan elementos de menú entre el feed de menú y la entrega carrito.
JSON
Este ejemplo contiene una lista de elementos simples del menú.
Elementos del menú en un feed de menú:
{ "@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" } ] } ] }
Elementos de menú asignados a un carrito de entregas:
{ "@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
Este ejemplo contiene un elemento de menú con uno o más AddOnMenuItems.
Elementos del menú en un feed de menú:
{ "@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" } ] } ] }
Elementos de menú asignados a un carrito de entregas:
{ "@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
Este ejemplo contiene un elemento de menú con opciones de elemento de menú, AddOnMenuItems, y objetos AddOnMenuItems anidados
Elementos del menú en un feed de menú:
{ "@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" } ] } ] } ] } ] } ] } } ] }
Elementos de menú asignados a un carrito de entregas:
{ "@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" } ] } ] } } ] }
Maneja los errores
Si tienes problemas durante el procesamiento de un CheckoutRequestMessage
, puedes
puede responder con un CheckoutResponseMessage
que contenga un
FoodErrorExtension
en lugar de una CheckoutResponse. Puedes usar esta
para identificar uno o más errores que ocurrieron durante el procesamiento.
Hay 2 maneras de solucionar los errores:
- Errores recuperables: El usuario no tiene que editar su carrito para enviar el
en el orden personalizado. Por ejemplo, si determinas que un elemento en
Cart
tiene un cambio de precio, puedes responder con unFoodOrderError
de tipo de errorPRICE_CHANGED
, junto concorrectedProposedOrder
ypaymentOptions
. Google informa al usuario sobre el cambio, pero le permite enviar la solicitud con elcorrectedProposedOrder
El usuario también puede volver y editar su carrito si deseado. Recibirás un nuevoCheckoutRequestMessage
o unSubmitOrderRequestMessage
- Errores irrecuperables: el usuario debe editar su carrito antes
antes de enviar el pedido. Por ejemplo, si determinas que el restaurante está
cerrado, puedes responder con una
FoodOrderError
de tipo de errorCLOSED
. Google informa al usuario y administra la interacción para que se actualice a una nueva restaurante. Recibirás un nuevoCheckoutRequestMessage
por una nueva carrito.
En general, haz que los errores a nivel del carrito sean irrecuperables y a nivel del artículo
y se pueden recuperar de forma indefinida. Para obtener una lista completa de los tipos de errores y sus significados, consulta
FoodOrderError
Cómo controlar los cambios de precio
Los precios cambian durante la confirmación de la compra
Si encuentras un problema con el precio mientras procesas la confirmación de compra de un cliente de la página de destino, haz lo siguiente:
- Responde a la
CheckoutRequestMessage
con unCheckoutResponseMessage
que contiene unFoodErrorExtension
, como se describe en Cómo manejar errores. - En la respuesta de error, usa
correctedProposedOrder.cart
para actualizar el precio. al valor correcto. Google recibe el pedido corregido y puede emitir un nuevoCheckoutRequestMessage
Después de la confirmación de la compra, Google muestra una página de confirmación del pedido al usuario final.
independientemente de si se modificó o no el objeto ProposedOrder
.
Si se corrigió el Propuesta de pedido, Google puede mostrar advertencias adicionales a
para informarle al usuario sobre los cambios. Si el usuario acepta realizar el pedido, no
ya no habrá solicitudes de confirmación de la compra. El flujo continúa con el envío de pedidos, con
el ProposedOrder
corregido.
Sin embargo, el usuario siempre puede cambiar de opinión y volver a editar su carrito. Cuándo
sus actualizaciones del carrito de esta manera, Google envía un nuevo CheckoutRequestMessage
.
Cambios de precio durante el envío del pedido
Si tienes un problema con el precio mientras procesas el envío de un pedido
(se activó el intent actions.intent.TRANSACTION_DECISION
), no respondas
por un error o actualiza el precio en tu respuesta. Si los precios, las cantidades
o algún otro detalle de SubmitOrderRequestMessage
no se corresponde con
tus datos, responde con orderState
establecido en REJECTED
para indicar que el
el pedido no se puede realizar según lo solicitado.
Luego, si los detalles del pedido y del pago son válidos, establece orderState
en CREATED
.
o CONFIRMED
. Además, incluye un actionOrderId
para representar el ID del pedido en
en tu sistema. Se debe usar este ID cuando se envíen actualizaciones posteriores.
Si no puedes procesar el pago y ya enviaste
SubmitOrderRequestMessage
, puedes enviar un
AsyncOrderUpdateRequestMessage
con orderState
configurado como REJECTED
para permitir
el usuario sepa que el pedido no se procesará.
Cambios de precio después de enviar el pedido
Si determinas que un precio cambió del que se usó cuando un cliente
envió su pedido, puedes emitir una AsyncOrderUpdateRequestMessage
, como
que se describe en Cómo implementar actualizaciones asíncronas de pedidos, con el nuevo precio.
Sigue estos pasos para actualizar los precios con actualizaciones asíncronas de pedidos:
- Cambia el precio en
lineItemUpdates[x].price
. Esta refleja el costo total del elemento, incluidos los complementos y multiplicado por la cantidad. (Para obtener más información, consulta la descripción delprice
deLineItem
). - Ingrese una explicación en
lineItemUpdates[x].reason
. - Establecer
lineItemUpdates[x].orderState
aCONFIRMED
.
Puede intentar cobrarle al instrumento de pago antes o después de enviar la
AsyncOrderUpdateRequestMessage, a tu discreción. Si la transacción falla
(posiblemente porque el precio delta es demasiado alto), envía un
AsyncOrderUpdateRequestMessage con la siguiente configuración en la
OrderUpdate
para informar a Google sobre el error:
- Establece
orderState
enREJECTED
. - Describe la falla en el campo
label
.
Validación de la compra
Como se explica en el Paso 4: Implementa la confirmación de la compra, tu
extremo de entrega debería realizar la validación en cada entrada
CheckoutRequestMessage
y responde con CheckoutResponseMessage
.
Este es un ejemplo de un CheckoutResponseMessage
de una ruta correcta
validación:
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: La validación se realiza correctamente | Muestra CheckoutResponse . Debe tener
ProposedOrder y PaymentOptions .
ProposedOrder incluye los impuestos, las tarifas y el precio total de
carrito. |
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 } } } } } ] } } }
Validación de la dirección de entrega
Tu extremo de entrega debe validar la dirección de entrega contenida en cada
CheckoutRequestMessage
Si hay un problema con la dirección de entrega, como que está fuera del rango de
el servicio de entrega, el CheckoutResponseMessage
devuelto
de entrega debe contener un FoodOrderError
del tipo adecuado.
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: No se pudo realizar la validación porque la dirección de entrega está fuera del intervalo o hay un problema con la dirección de entrega | Devuelve FoodErrorExtension con FoodOrderError
del tipo de error 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." } ] } } } ] } } }
Validación del valor mínimo de pedido
Tu extremo de entrega debe validar el valor mínimo de pedido de cada
CheckoutRequestMessage
Si no se alcanza el valor mínimo del pedido, la CheckoutResponseMessage
que muestre tu entrega deben contener un FoodOrderError
de tipo de error
REQUIREMENTS_NOT_MET
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: La validación falló porque el valor mínimo del pedido no se cumple | Devuelve FoodErrorExtension con FoodOrderError
del tipo de error 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." } ] } } } ] } } }
Validación del período de pedidos
Tu extremo de entrega debe validar cualquier factor que pueda afectar el
la ventana de pedido de cada CheckoutRequestMessage
.
Por ejemplo, si el restaurante está cerrado o ya no recibe pedidos en el
por el momento, el CheckoutResponseMessage
que muestra tu entrega debería
contienen una FoodOrderError
de tipo de error CLOSED
o NO_CAPACITY
,
respectivamente.
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: No se pudo realizar la validación porque el restaurante está cerrado ya no es compatible | Devuelve FoodErrorExtension con FoodOrderError
del tipo de error CLOSED . |
Caso de uso 2: No se pudo realizar la validación porque el restaurante está concurrido. no recibo pedidos en este momento | Devuelve FoodErrorExtension con FoodOrderError
del tipo de error 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." } ] } } } ] } } }
Validación de artículos del carrito
Tu extremo de entrega debe validar el precio y la disponibilidad de cada
artículo del carrito incluido en un elemento CheckoutRequestMessage
.
Si la disponibilidad o los precios cambiaron, el ícono CheckoutResponseMessage
que muestre tu entrega deben contener un FoodOrderError
de tipo de error
AVAILABILITY_CHANGED
o PRICE_CHANGED
, respectivamente.
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: La validación falló porque algunos elementos de menú o sus personalizaciones no son válidas o están agotados | Muestra FoodErrorExtension con correctedProposedOrder ,
PaymentOptions y FoodOrderError de tipo de error
AVAILABILITY_CHANGED Los elementos no válidos deben quitarse de
CorrectedProposedOrder |
Caso de uso 2: La validación falló porque algunos elementos de menú o sus personalizaciones no son válidas o están agotados. El carrito corregido ya no cumple con el requisito de valor mínimo del pedido. | Devuelve FoodErrorExtension con FoodOrderError
de los tipos de error AVAILABILITY_CHANGED y
REQUIREMENTS_NOT_MET |
Caso de uso 3: La validación falló porque algún elemento de menú o cambiaron los precios de personalización | Muestra FoodErrorExtension con correctedProposedOrder ,
PaymentOptions y FoodOrderError de tipo de error
PRICE_CHANGED Los precios desactualizados se deben actualizar en
CorrectedProposedOrder |
Caso de uso 4: La validación falló porque algún elemento de menú o y los precios de personalización. El carrito corregido ya no cumple con requisito de valor mínimo de pedido | Devuelve FoodErrorExtension con FoodOrderError
de los tipos de error PRICE_CHANGED y
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 } } ] } } } ] } } }
Enviar validación de pedido
Como se explicó en el Paso 7: Implementa el envío de pedidos, las
extremo de entrega debería realizar la validación en cada entrada
SubmitOrderRequestMessage
y responde con una
SubmitOrderResponseMessage
.
Este es un ejemplo de un SubmitOrderResponseMessage
de una ruta correcta
validación:
Caso de uso | Cómo implementarlo |
---|---|
Caso de uso 1: El pedido se creó correctamente | Un SubmitOrderResponseMessage con un pedido de CREATED
para cada estado. Debe tener actionOrderId ,
userVisibleId , orderManagementActions y
estimatedFulfillmentTime |
Caso de uso 2: El pedido se rechaza debido a problemas de pago | Un SubmitOrderResponseMessage con un pedido de REJECTED
para cada estado. Debe tener actionOrderId ,
userVisibleId , orderManagementActions y
rejectionInfo de tipo PAYMENT_DECLINED . |
Caso de uso 3: Se rechazó el pedido porque el usuario se marcó como bloqueado | Un SubmitOrderResponseMessage con un pedido de REJECTED
estado. Debe tener actionOrderId ,
userVisibleId , orderManagementActions y
rejectionInfo de tipo INELIGIBLE . |
Caso de uso 4: El pedido se rechaza porque la información del usuario es incompleto o no válido | Un SubmitOrderResponseMessage con un pedido de REJECTED
para cada estado. Debe tener actionOrderId ,
userVisibleId , orderManagementActions y
rejectionInfo de tipo INELIGIBLE . |
Caso de uso 5: El pedido se rechaza por motivos desconocidos | Un SubmitOrderResponseMessage con un pedido de REJECTED
para cada estado. Debe tener actionOrderId ,
userVisibleId , orderManagementActions y
rejectionInfo de tipo 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" } } } ] } } } ] } } }