Mappatura dei feed del menu e degli articoli del carrello di evasione degli ordini
Quando i clienti aggiungono al carrello articoli presenti nel tuo feed Menu e al momento del pagamento, Google li invia all'endpoint di evasione degli ordini per verificare il prezzo e la disponibilità. Una volta convalidati prezzi e disponibilità, il cliente può effettuare l'ordine. Questa sezione illustra come mappare gli elementi del feed del menu agli articoli del carrello di evasione degli ordini.
Gli esempi in questa sezione sono versioni ridotte del feed Menu e dello schema Carrello. Vengono mostrati solo i campi pertinenti per illustrare la mappatura tra il feed Menu e
l'oggetto Carrello. Per gli schemi completi, consulta Menu
e Cart
.
Gli articoli del feed Menu
aggiunti a un carrello vengono inviati nell'oggetto Cart
sia per il pagamento sia per l'invio dell'ordine.
- Un
MenuItem
semplice è rappresentato come unLineItem
nell'arraylineItems
, doveofferId
è l'offer.id
della voce di menu selezionata nel feed del menu. - Una
MenuItem
con unMenuItemOption
obbligatorio è rappresentata comeLineItem
nell'arraylineItems
, doveofferId
è l'opzione della voce di menu selezionata eoffer.id
nel feed del menu. - Una
AddOnMenuItem
diLineItem
è rappresentata comeFoodItemOption
nell'arrayoptions
diFoodItemExtension
. Ogni opzione ha unofferId
che corrisponde aloffer.id
dell'elemento di menu del componente aggiuntivo selezionato dal feed del menu. Tieni presente che un AddOnMenuItem può anche avere AddOnMenuItem nidificati che sono rappresentati comesubOptions
all'interno di ogni opzione.
I seguenti esempi mappano le voci di menu tra il feed del menu e il carrello di evasione.
JSON
Questo esempio contiene un elenco di semplici voci di menu.
Voci di menu in un feed Menu:
{ "@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" } ] } ] }
Voci di menu mappate a un carrello di evasione degli ordini:
{ "@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
Questo esempio contiene una voce di menu con una o più voci di menu aggiuntive.
Voci di menu in un feed Menu:
{ "@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" } ] } ] }
Voci di menu mappate a un carrello di evasione degli ordini:
{ "@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
Questo esempio contiene una voce di menu con opzioni di voce di menu, AddOnMenuItems e AddOnMenuItems nidificati
Voci di menu in un feed Menu:
{ "@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" } ] } ] } ] } ] } ] } } ] }
Voci di menu mappate a un carrello di evasione degli ordini:
{ "@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" } ] } ] } } ] }
Gestione degli errori
Se riscontri problemi durante l'elaborazione di una CheckoutRequestMessage
, puoi rispondere con una CheckoutResponseMessage
che contiene una FoodErrorExtension
anziché una CheckoutResponse. Puoi utilizzare questa risposta per identificare uno o più errori che si sono verificati durante l'elaborazione.
Esistono due modi per gestire gli errori:
- Errori recuperabili: l'utente non è tenuto a modificare il carrello per inviare l'ordine. Ad esempio, se stabilisci che un elemento nella
Cart
ha una variazione di prezzo, puoi rispondere con unFoodOrderError
di tipo di errorePRICE_CHANGED
, insieme acorrectedProposedOrder
epaymentOptions
. Google informa l'utente della modifica, ma consente all'utente di inviare un modulo concorrectedProposedOrder
. L'utente può anche tornare indietro e modificare il carrello, se lo desidera. Riceverai un nuovoCheckoutRequestMessage
o unSubmitOrderRequestMessage
. - Errori irreversibili: l'utente è tenuto a modificare il carrello prima
di inviare l'ordine. Ad esempio, se stabilisci che il ristorante è chiuso, puoi rispondere con un
FoodOrderError
di tipo di erroreCLOSED
. Google informa l'utente e gestisce l'interazione per effettuare l'aggiornamento a un nuovo ristorante. Riceverai un nuovoCheckoutRequestMessage
per un nuovo carrello.
In generale, rendi non recuperabili gli errori a livello di carrello e gli errori a livello di articolo. Per un elenco completo dei tipi di errore e del loro significato, consulta
FoodOrderError
.
Gestione delle modifiche ai prezzi
Variazioni di prezzo durante il pagamento
Se riscontri un problema di prezzo durante l'elaborazione della richiesta di pagamento di un cliente:
- Rispondi alla
CheckoutRequestMessage
con unCheckoutResponseMessage
che contiene unFoodErrorExtension
, come descritto in Gestione degli errori. - Nella risposta di errore, utilizza
correctedProposedOrder.cart
per aggiornare il prezzo in base al valore corretto. Google riceve l'ordine corretto e potrebbe emettere un nuovoCheckoutRequestMessage
.
Dopo il pagamento, Google mostra una pagina di conferma dell'ordine all'utente finale, indipendentemente dal fatto che la ProposedOrder
sia stata modificata o meno.
Se l'ordine proposto è stato corretto, Google potrebbe mostrare avvisi aggiuntivi per
informare l'utente delle modifiche. Se l'utente accetta di effettuare l'ordine, non ci saranno più richieste di pagamento. Il flusso prosegue con l'invio dell'ordine, con
il ProposedOrder
corretto.
Tuttavia, l'utente può sempre cambiare idea e modificare nuovamente il carrello. Quando
il carrello si aggiorna in questo modo, Google invia un nuovo CheckoutRequestMessage
.
Variazioni di prezzo durante l'invio dell'ordine
Se riscontri un problema di prezzo durante l'invio dell'ordine (è stato attivato l'intent actions.intent.TRANSACTION_DECISION
), non rispondere con un errore e non aggiornare il prezzo nella risposta. Se i prezzi, le quantità o altri dettagli nella sezione SubmitOrderRequestMessage
non corrispondono ai tuoi dati, rispondi con orderState
impostato su REJECTED
per indicare che l'ordine non può essere effettuato come richiesto.
Quindi, se i dettagli dell'ordine e del pagamento sono validi, imposta orderState
su CREATED
o CONFIRMED
. Inoltre, includi un elemento actionOrderId
per rappresentare l'ID dell'ordine nel
sistema. Questo ID deve essere utilizzato per l'invio di aggiornamenti successivi.
Se non riesci a elaborare il pagamento e hai già inviato il
SubmitOrderRequestMessage
, puoi inviare un
AsyncOrderUpdateRequestMessage
con orderState
impostato su REJECTED
per comunicare
all'utente che l'ordine non verrà evaso.
Il prezzo cambia dopo l'invio dell'ordine
Se stabilisci che un prezzo è cambiato rispetto a quello utilizzato quando un cliente ha inviato l'ordine, puoi emettere un AsyncOrderUpdateRequestMessage
, come descritto in Implementazione degli aggiornamenti degli ordini asincroni, con il nuovo prezzo.
Per aggiornare i prezzi utilizzando gli aggiornamenti asincroni degli ordini:
- Modifica il prezzo in
lineItemUpdates[x].price
. Questo valore riflette il costo totale dell'articolo, compresi i componenti aggiuntivi, e moltiplicato per la quantità. Per ulteriori informazioni, consulta la descrizione del campoprice
diLineItem
. - Inserisci una spiegazione in
lineItemUpdates[x].reason
. - Imposta
lineItemUpdates[x].orderState
suCONFIRMED
.
Puoi tentare di effettuare un addebito sullo strumento di pagamento prima o dopo aver inviato
AsyncOrderUpdateRequestMessage, a tua discrezione. Se la transazione non va a buon fine (probabilmente perché il delta di prezzo è troppo elevato), invia un messaggio AsyncOrderUpdateRequestMessage con le seguenti impostazioni nel OrderUpdate
per informare Google dell'errore:
- Imposta
orderState
suREJECTED
. - Descrivi l'errore nel campo
label
.
Convalida del pagamento
Come spiegato nel Passaggio 4: implementa la procedura di pagamento, l'endpoint di evasione degli ordini deve eseguire la convalida su ogni CheckoutRequestMessage
in entrata e rispondere con un CheckoutResponseMessage
.
Ecco un esempio di CheckoutResponseMessage
per una convalida
corretta:
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1: la convalida è riuscita | Restituisci CheckoutResponse . Deve avere
ProposedOrder e PaymentOptions .
ProposedOrder include tasse, commissioni e il prezzo totale del
carrello. |
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 } } } } } ] } } }
Convalida dell'indirizzo di consegna
L'endpoint di evasione degli ordini deve convalidare l'indirizzo di consegna contenuto in ogni CheckoutRequestMessage
.
Se si è verificato un problema con l'indirizzo di consegna, ad esempio perché non rientra nell'intervallo del servizio di consegna, l'elemento CheckoutResponseMessage
restituito dal fulfillment deve contenere un valore FoodOrderError
del tipo appropriato.
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1: convalida non riuscita perché l'indirizzo di consegna è fuori intervallo o si è verificato un problema con l'indirizzo di consegna | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore 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." } ] } } } ] } } }
Convalida del valore minimo dell'ordine
L'endpoint di evasione degli ordini deve convalidare il valore minimo dell'ordine di ogni CheckoutRequestMessage
.
Se il valore minimo dell'ordine non viene soddisfatto, l'elemento CheckoutResponseMessage
restituito dall'evasione degli ordini deve contenere un valore FoodOrderError
di tipo di errore
REQUIREMENTS_NOT_MET
.
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1: convalida non riuscita perché non è stato soddisfatto il valore minimo dell'ordine | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore 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." } ] } } } ] } } }
Convalida del periodo di ordinazione
L'endpoint di evasione degli ordini deve convalidare tutti i fattori che potrebbero influire sulla finestra di ordinazione di ogni CheckoutRequestMessage
.
Ad esempio, se il ristorante è chiuso o non accetta più ordini al momento, la metrica CheckoutResponseMessage
restituita dall'evasione dell'ordine deve contenere rispettivamente un FoodOrderError
di tipo di errore CLOSED
o NO_CAPACITY
.
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1: convalida non riuscita perché il ristorante è chiuso o non è più supportato | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore CLOSED . |
Caso d'uso 2. Convalida non riuscita perché il ristorante è occupato e non accetta ordini al momento | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore 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." } ] } } } ] } } }
Convalida articoli nel carrello
L'endpoint di evasione degli ordini deve convalidare i prezzi e la disponibilità di ogni
articolo del carrello contenuto in un elemento CheckoutRequestMessage
.
Se la disponibilità o i prezzi sono cambiati, l'elemento CheckoutResponseMessage
restituito dal fulfillment deve contenere, rispettivamente, FoodOrderError
di tipo di errore
AVAILABILITY_CHANGED
o PRICE_CHANGED
.
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1: convalida non riuscita perché alcune voci di menu e/o le relative personalizzazioni non sono valide o non sono disponibili | Restituisce FoodErrorExtension con correctedProposedOrder ,
PaymentOptions e FoodOrderError di tipo di errore
AVAILABILITY_CHANGED . Gli elementi non validi devono essere rimossi da
CorrectedProposedOrder . |
Caso d'uso 2: convalida non riuscita perché alcune voci di menu e/o le relative personalizzazioni non sono valide o non sono disponibili. Il carrello corretto non soddisfa più il requisito del valore minimo dell'ordine. | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore AVAILABILITY_CHANGED e
REQUIREMENTS_NOT_MET . |
Caso d'uso 3: convalida non riuscita perché alcune voci di menu e/o prezzi di personalizzazione sono cambiati | Restituisce FoodErrorExtension con correctedProposedOrder ,
PaymentOptions e FoodOrderError di tipo di errore
PRICE_CHANGED . I prezzi obsoleti devono essere aggiornati in
CorrectedProposedOrder . |
Caso d'uso 4. Convalida non riuscita perché alcune voci di menu e/o prezzi di personalizzazione sono cambiati. Il carrello corretto non soddisfa più il requisito del valore minimo dell'ordine | Restituisce FoodErrorExtension con FoodOrderError
di tipo di errore 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 } } ] } } } ] } } }
Invia convalida ordine
Come spiegato nel Passaggio 7: implementa l'opzione Invia ordine, l'endpoint di evasione degli ordini deve eseguire la convalida su ogni SubmitOrderRequestMessage
in entrata e rispondere con un SubmitOrderResponseMessage
.
Ecco un esempio di SubmitOrderResponseMessage
per una convalida
corretta:
Caso d'uso | Come implementare |
---|---|
Caso d'uso 1:l'ordine è stato creato correttamente | Una proprietà SubmitOrderResponseMessage con stato dell'ordine
CREATED . Deve avere actionOrderId ,
userVisibleId , orderManagementActions e
estimatedFulfillmentTime . |
Caso d'uso 2. L'ordine è stato rifiutato a causa di problemi con il pagamento | Una proprietà SubmitOrderResponseMessage con stato dell'ordine
REJECTED . Deve avere actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo di tipo PAYMENT_DECLINED . |
Caso d'uso 3. L'ordine è stato rifiutato perché l'utente è contrassegnato come escluso | Una SubmitOrderResponseMessage con stato dell'ordine
REJECTED . Deve avere actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo di tipo INELIGIBLE . |
Caso d'uso 4: l'ordine viene rifiutato perché le informazioni dell'utente sono incomplete o non valide | Una proprietà SubmitOrderResponseMessage con stato dell'ordine
REJECTED . Deve avere actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo di tipo INELIGIBLE . |
Caso d'uso 5: l'ordine è stato rifiutato per un motivo sconosciuto | Una proprietà SubmitOrderResponseMessage con stato dell'ordine
REJECTED . Deve avere actionOrderId ,
userVisibleId , orderManagementActions e
rejectionInfo di 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" } } } ] } } } ] } } }