Mapeamento de feeds do menu e itens do carrinho de atendimento do pedido
Quando os clientes adicionam itens do seu feed de cardápio ao carrinho e finalizam a compra, o Google os envia ao endpoint de atendimento do pedido para verificar o preço e a disponibilidade. Depois que o preço e a disponibilidade forem validados, o cliente poderá fazer o pedido. Esta seção ilustra como mapear itens do feed de menu para itens do carrinho de atendimento do pedido.
Os exemplos desta seção são versões simplificadas do feed de cardápio e do esquema
do carrinho. Somente os campos relevantes para ilustrar o mapeamento entre o feed de cardápio e
o objeto "Carrinho" são exibidos. Para esquemas completos, consulte Menu
e Cart
.
Os itens no feed Menu
adicionados a um carrinho são enviados no objeto Cart
para finalização da compra e envio do pedido.
- Um
MenuItem
simples é representado como umLineItem
na matrizlineItems
, com oofferId
sendo aoffer.id
do item de menu selecionado no feed de menu. - Um
MenuItem
com umMenuItemOption
obrigatório é representado como umLineItem
na matrizlineItems
, comofferId
sendo a opção de item selecionadaoffer.id
no feed de menu. - Um
AddOnMenuItem
de umLineItem
é representado como umFoodItemOption
na matrizoptions
doFoodItemExtension
. Cada opção tem umofferId
que corresponde aooffer.id
do item de menu do complemento selecionado no feed de menu. Um AddOnMenuItem também pode ter AddOnMenuItem(s) aninhados que são representados comosubOptions
dentro de cada opção.
Os exemplos a seguir mapeiam itens de menu entre o feed de cardápio e o carrinho de fulfillment.
JSON
Este exemplo contém uma lista de itens de menu simples.
Itens do cardápio em um feed:
{ "@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" } ] } ] }
Itens de menu mapeados para um carrinho de atendimento do pedido:
{ "@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 exemplo contém um item de menu com um ou mais AddOnMenuItems.
Itens do cardápio em um feed:
{ "@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" } ] } ] }
Itens de menu mapeados para um carrinho de atendimento do pedido:
{ "@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 exemplo contém um item de menu com opções de item de menu, AddOnMenuItems e AddOnMenuItems aninhados
Itens do cardápio em um feed:
{ "@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" } ] } ] } ] } ] } ] } } ] }
Itens de menu mapeados para um carrinho de atendimento do pedido:
{ "@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" } ] } ] } } ] }
Como processar os erros
Se você tiver problemas ao processar uma CheckoutRequestMessage
, responda com uma CheckoutResponseMessage
que contenha uma
FoodErrorExtension
em vez de uma CheckoutResponse. Use essa resposta para identificar um ou mais erros que ocorreram durante o processamento.
Há duas maneiras de lidar com os erros:
- Erros recuperáveis: o usuário não precisa editar o carrinho para enviar o
pedido. Por exemplo, se você determinar que um item na
Cart
tem uma mudança de preço, poderá responder com umaFoodOrderError
do tipo de erroPRICE_CHANGED
, além decorrectedProposedOrder
epaymentOptions
. O Google informa o usuário sobre a mudança, mas permite que ele o envie com ocorrectedProposedOrder
. O usuário também pode voltar e editar o carrinho. Você vai receber um novoCheckoutRequestMessage
ou umSubmitOrderRequestMessage
. - Erros irrecuperáveis: o usuário precisa editar o carrinho antes de enviar o pedido. Por exemplo, se você determinar que o restaurante está
fechado, responda com uma
FoodOrderError
do tipo de erroCLOSED
. O Google informa o usuário e gerencia a interação para atualizar para um novo restaurante. Você receberá um novoCheckoutRequestMessage
para um novo carrinho.
Em geral, torne os erros no carrinho irrecuperáveis e os erros no item
que possam ser recuperados. Para ver uma lista completa dos tipos de erro e os significados deles, consulte
FoodOrderError
.
Como processar mudanças de preço
Mudanças no preço durante a finalização da compra
Se você encontrar um problema de preço ao processar a solicitação de checkout de um cliente, faça o seguinte:
- Responda à
CheckoutRequestMessage
com umaCheckoutResponseMessage
que contenha umFoodErrorExtension
, conforme descrito em Como processar erros. - Na resposta de erro, use
correctedProposedOrder.cart
para atualizar o preço com o valor correto. O Google recebe o pedido corrigido e pode emitir um novoCheckoutRequestMessage
.
Após a finalização da compra, o Google exibe uma página de confirmação do pedido para o usuário final,
independentemente de o ProposedOrder
ter sido alterado ou não.
Se o ProposedOrder for corrigido, o Google poderá mostrar mais avisos para
informar o usuário sobre as mudanças. Se o usuário concordar em fazer o pedido, não haverá mais solicitações de finalização de compra. O fluxo continua pedindo o envio, com
o ProposedOrder
corrigido.
No entanto, o usuário pode mudar de ideia e editar o carrinho a qualquer momento. Quando
o carrinho é atualizado dessa forma, o Google envia uma nova CheckoutRequestMessage
.
Alterações de preço durante o envio do pedido
Se você encontrar um problema de preço ao processar o envio de pedido
(a intent actions.intent.TRANSACTION_DECISION
foi acionada), não responda
com um erro nem atualize o preço na resposta. Se os preços, as quantidades
ou outros detalhes no SubmitOrderRequestMessage
não corresponderem aos
seus dados, responda com o orderState
definido como REJECTED
para indicar que o
pedido não pode ser feito conforme solicitado.
Em seguida, se os detalhes do pedido e da forma de pagamento forem válidos, defina orderState
como CREATED
ou CONFIRMED
. Além disso, inclua um actionOrderId
para representar o ID do pedido no
seu sistema. Esse ID precisa ser usado ao enviar atualizações subsequentes.
Se não for possível processar o pagamento e já tiver enviado o
SubmitOrderRequestMessage
, você poderá enviar um
AsyncOrderUpdateRequestMessage
com orderState
definido como REJECTED
para informar
ao usuário que o pedido não será processado.
Alterações de preço após o envio do pedido
Se você determinar que um preço mudou em relação ao usado quando um cliente enviou o pedido, emita um AsyncOrderUpdateRequestMessage
, conforme descrito em Como implementar atualizações assíncronas de pedidos, com o novo preço.
Para atualizar preços usando atualizações de pedidos assíncronas:
- Mudar o preço em
lineItemUpdates[x].price
. Esse valor reflete o custo total do item, incluindo complementos, e multiplicado pela quantidade. Para saber mais, consulte a descrição do campoprice
deLineItem
. - Insira uma explicação em
lineItemUpdates[x].reason
. - Defina
lineItemUpdates[x].orderState
comoCONFIRMED
.
É possível tentar cobrar o instrumento de pagamento antes ou depois de enviar a AsyncOrderUpdateRequestMessage, a seu critério. Se a transação falhar, possivelmente porque o delta de preço está muito alto, envie uma AsyncOrderUpdateRequestMessage com as seguintes configurações em OrderUpdate
para informar o Google sobre a falha:
- Defina
orderState
comoREJECTED
. - Descreva a falha no campo
label
.
Validação da finalização de compra
Conforme discutido na Etapa 4: implementar a finalização da compra, seu
endpoint de fulfillment precisa realizar a validação em cada
CheckoutRequestMessage
recebida e responder com uma CheckoutResponseMessage
.
Confira um exemplo de CheckoutResponseMessage
para uma validação
bem-sucedida:
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1: validação bem-sucedida | Retorne o CheckoutResponse . Ela precisa ter
ProposedOrder e PaymentOptions .
ProposedOrder inclui tributos, taxas e o preço total do
carrinho. |
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 } } } } } ] } } }
Validação do endereço de entrega
Seu endpoint de fulfillment precisa validar o endereço de entrega contido em cada
CheckoutRequestMessage
.
Se houver um problema com o endereço de entrega, como estar fora do alcance do
serviço de entrega, o CheckoutResponseMessage
retornado pelo
fulfillment precisa conter um FoodOrderError
do tipo adequado.
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1:a validação falhou porque o endereço de entrega está fora do intervalo ou há um problema com o endereço de entrega | Retorne FoodErrorExtension com FoodOrderError do tipo de erro 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." } ] } } } ] } } }
Validação do valor mínimo do pedido
Seu endpoint de fulfillment precisa validar o valor mínimo do pedido de cada
CheckoutRequestMessage
.
Se o valor mínimo do pedido não for atingido, os CheckoutResponseMessage
retornados pelo fulfillment precisarão conter um FoodOrderError
do tipo de erro
REQUIREMENTS_NOT_MET
.
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1:ocorreu uma falha na validação porque o valor mínimo do pedido não foi atendido | Retorne FoodErrorExtension com FoodOrderError do tipo de erro 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." } ] } } } ] } } }
Validação da janela de pedidos
Seu endpoint de fulfillment precisa validar todos os fatores que podem afetar a
janela de ordenação de cada CheckoutRequestMessage
.
Por exemplo, se o restaurante estiver fechado ou não receber mais pedidos no
momento, o CheckoutResponseMessage
retornado pelo atendimento do pedido precisará
conter um FoodOrderError
do tipo de erro CLOSED
ou NO_CAPACITY
,
respectivamente.
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1:a validação falhou porque o restaurante está fechado ou não é mais compatível | Retorne FoodErrorExtension com FoodOrderError do tipo de erro CLOSED . |
Caso de uso 2:a validação falhou porque o restaurante está ocupado e não aceita pedidos no momento | Retorne FoodErrorExtension com FoodOrderError do tipo de erro 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." } ] } } } ] } } }
Validação de itens do carrinho
Seu endpoint de fulfillment precisa validar o preço e a disponibilidade de cada
item de carrinho contido em um CheckoutRequestMessage
.
Se a disponibilidade ou os preços tiverem mudado, o CheckoutResponseMessage
retornado pelo fulfillment deve conter um FoodOrderError
do tipo de erro
AVAILABILITY_CHANGED
ou PRICE_CHANGED
, respectivamente.
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1:a validação falhou porque alguns itens de menu e/ou as personalizações deles não são válidos ou estão esgotados | Retorne FoodErrorExtension com correctedProposedOrder ,
PaymentOptions e FoodOrderError do tipo de erro
AVAILABILITY_CHANGED . Os itens inválidos precisam ser removidos do CorrectedProposedOrder . |
Caso de uso 2:a validação falhou porque alguns itens de menu e/ou as personalizações deles não são válidos ou estão esgotados. O carrinho corrigido não atende mais ao requisito de valor mínimo do pedido. | Retorne FoodErrorExtension com FoodOrderError
dos tipos de erro AVAILABILITY_CHANGED e
REQUIREMENTS_NOT_MET . |
Caso de uso 3:a validação falhou porque alguns itens de menu e/ou preços de personalização mudaram | Retorne FoodErrorExtension com correctedProposedOrder ,
PaymentOptions e FoodOrderError do tipo de erro
PRICE_CHANGED . Os preços desatualizados precisam ser atualizados em
CorrectedProposedOrder . |
Caso de uso 4:a validação falhou porque alguns itens de menu e/ou preços de personalização mudaram. O carrinho corrigido não atende mais ao requisito de valor mínimo do pedido | Retorne FoodErrorExtension com FoodOrderError
dos tipos de erro PRICE_CHANGED e
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 validação do pedido
Conforme discutido na Etapa 7: implementar o envio de pedido, seu
endpoint de fulfillment precisa realizar a validação em cada
SubmitOrderRequestMessage
recebida e responder com uma
SubmitOrderResponseMessage
.
Confira um exemplo de SubmitOrderResponseMessage
para uma validação
bem-sucedida:
Caso de uso | Como fazer a implementação |
---|---|
Caso de uso 1:o pedido foi criado | Um SubmitOrderResponseMessage com estado de pedido CREATED . Ela precisa ter actionOrderId ,
userVisibleId , orderManagementActions e
estimatedFulfillmentTime . |
Caso de uso 2:o pedido foi recusado devido a problemas no pagamento | Um SubmitOrderResponseMessage com estado de pedido REJECTED . Ele precisa ter actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo do tipo PAYMENT_DECLINED . |
Caso de uso 3:o pedido é rejeitado porque o usuário é sinalizado como banido | Um SubmitOrderResponseMessage com status do pedido
REJECTED . Ele precisa ter actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo do tipo INELIGIBLE . |
Caso de uso 4:o pedido é rejeitado porque as informações do usuário estão incompletas ou inválidas | Um SubmitOrderResponseMessage com estado de pedido REJECTED . Ele precisa ter actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo do tipo INELIGIBLE . |
Caso de uso 5: o pedido foi recusado por motivo desconhecido | Um SubmitOrderResponseMessage com estado de pedido REJECTED . Ele precisa ter actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo do 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" } } } ] } } } ] } } }