Mettre en correspondance les flux de menu et les articles du panier de traitement
Lorsque des clients ajoutent des articles de votre flux Menu à leur panier et procèdent au paiement, Google envoie ces articles à votre point de terminaison de traitement pour vérifier leur prix et leur disponibilité. Une fois le prix et la disponibilité validés, le client peut passer commande. Cette section explique comment mettre en correspondance les éléments du flux de menu et les articles du panier de traitement.
Les exemples de cette section sont des versions simplifiées du flux de menu et du schéma du panier. Seuls les champs pertinents pour illustrer le mappage entre le flux Menu et l'objet Panier sont affichés. Pour obtenir des schémas complets, consultez Menu
et Cart
.
Les articles du flux Menu
ajoutés à un panier sont envoyés dans l'objet Cart
lors du règlement et de l'envoi de la commande.
- Un
MenuItem
simple est représenté par unLineItem
dans le tableaulineItems
,offerId
étant laoffer.id
de l'élément de menu sélectionné dans le flux de menu. - Un
MenuItem
avec unMenuItemOption
obligatoire est représenté parLineItem
dans le tableaulineItems
, leofferId
correspondant à l'élémentoffer.id
de l'option de menu sélectionnée dans le flux de menu. - Un
AddOnMenuItem
d'unLineItem
est représenté par unFoodItemOption
dans le tableauoptions
duFoodItemExtension
. Chaque option est associée à unofferId
correspondant auoffer.id
de l'élément de menu complémentaire sélectionné dans le flux de menu. Notez qu'un AddOnMenuItem peut également avoir un ou plusieurs AddOnMenuItems imbriqués représentés parsubOptions
dans chaque option.
Les exemples suivants mappent les éléments de menu entre le flux de menu et le panier de traitement.
JSON
Cet exemple contient une liste d'éléments de menu simples.
Éléments de menu dans un flux de menus:
{ "@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" } ] } ] }
Articles de menu associés à un panier de traitement:
{ "@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
Cet exemple contient un élément de menu avec un ou plusieurs AddOnMenuItems.
Éléments de menu dans un flux de menus:
{ "@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" } ] } ] }
Articles de menu associés à un panier de traitement:
{ "@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
Cet exemple contient un élément de menu avec des options d'élément de menu, AddOnMenuItems et des éléments AddOnMenuItems imbriqués
Éléments de menu dans un flux de menus:
{ "@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" } ] } ] } ] } ] } ] } } ] }
Articles de menu associés à un panier de traitement:
{ "@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" } ] } ] } } ] }
Traiter les erreurs
Si vous rencontrez des problèmes lors du traitement d'une CheckoutRequestMessage
, vous pouvez répondre avec une CheckoutResponseMessage
contenant un FoodErrorExtension
plutôt qu'une CheckoutResponse. Vous pouvez utiliser cette réponse pour identifier une ou plusieurs erreurs qui se sont produites lors du traitement.
Il existe deux façons de gérer les erreurs:
- Erreurs récupérables: l'utilisateur n'est pas obligé de modifier son panier pour envoyer la commande. Par exemple, si vous déterminez qu'un article de la
Cart
a un changement de prix, vous pouvez répondre avec uneFoodOrderError
de type d'erreurPRICE_CHANGED
, aveccorrectedProposedOrder
etpaymentOptions
. Google informe l'utilisateur de la modification, mais lui permet d'envoyer la modification aveccorrectedProposedOrder
. L'utilisateur peut également revenir en arrière et modifier son panier si besoin. Vous recevrez un nouveauCheckoutRequestMessage
ou unSubmitOrderRequestMessage
. - Erreurs irrécupérables: l'utilisateur doit modifier son panier avant d'envoyer la commande. Par exemple, si vous déterminez que le restaurant est fermé, vous pouvez répondre avec une
FoodOrderError
de type d'erreurCLOSED
. Google informe l'utilisateur et gère l'interaction pour effectuer la mise à jour vers un nouveau restaurant. Vous recevrez une nouvelleCheckoutRequestMessage
pour un nouveau panier.
En règle générale, vous pouvez faire en sorte que les erreurs au niveau du panier soient irrécupérables et que les erreurs au niveau des articles puissent être récupérées. Pour obtenir la liste complète des types d'erreurs et de leur signification, consultez FoodOrderError
.
Gérer les changements de prix
Changements de prix lors du règlement
Si vous rencontrez un problème de prix lors du traitement de la requête de paiement d'un client, procédez comme suit:
- Répondez à
CheckoutRequestMessage
avec unCheckoutResponseMessage
contenant unFoodErrorExtension
, comme décrit dans la section Gérer les erreurs. - Dans la réponse d'erreur, utilisez
correctedProposedOrder.cart
pour définir le prix sur la valeur correcte. Google reçoit la commande corrigée et peut émettre un nouveauCheckoutRequestMessage
.
Après le règlement, Google présente une page de confirmation de commande à l'utilisateur final, que la valeur ProposedOrder
ait été modifiée ou non.
Si la commande proposée a été corrigée, Google peut afficher des avertissements supplémentaires pour informer l'utilisateur des modifications. Si l'utilisateur accepte de passer la commande, il n'y aura plus de demandes de paiement. Le flux se poursuit pour l'envoi de la commande, avec la version corrigée de ProposedOrder
.
Cependant, l'utilisateur peut toujours changer d'avis et modifier à nouveau son panier. Lorsque son panier est mis à jour de cette manière, Google envoie un nouveau CheckoutRequestMessage
.
Le prix change lors de l'envoi de la commande
Si vous rencontrez un problème de prix lors du traitement de l'envoi de la commande (l'intent actions.intent.TRANSACTION_DECISION
a été déclenché), ne renvoyez pas d'erreur et ne mettez pas à jour le prix dans votre réponse. Si les prix, les quantités ou d'autres informations indiquées dans SubmitOrderRequestMessage
ne correspondent pas à vos données, répondez en définissant la valeur de orderState
sur REJECTED
pour indiquer que la commande ne peut pas être passée comme demandé.
Ensuite, si la commande et les détails du paiement sont valides, définissez orderState
sur CREATED
ou CONFIRMED
. Incluez également un élément actionOrderId
pour représenter l'ID de la commande dans votre système. Cet ID doit être utilisé lors de l'envoi de mises à jour ultérieures.
Si vous ne parvenez pas à traiter le paiement et que vous avez déjà envoyé la demande SubmitOrderRequestMessage
, vous pouvez envoyer un AsyncOrderUpdateRequestMessage
avec orderState
défini sur REJECTED
pour informer l'utilisateur que la commande ne sera pas effectuée.
Changement de prix après l'envoi de la commande
Si vous constatez qu'un prix a changé par rapport au prix utilisé lorsqu'un client a envoyé sa commande, vous pouvez envoyer une AsyncOrderUpdateRequestMessage
, comme décrit dans la section Implémenter des mises à jour de commandes asynchrones avec le nouveau prix.
Pour mettre à jour des prix à l'aide de mises à jour asynchrones des commandes:
- Modifiez le prix en
lineItemUpdates[x].price
. Cette valeur reflète le coût total de l'article, y compris les modules complémentaires, et multiplié par la quantité. (Pour en savoir plus, consultez la description du champprice
deLineItem
.) - Saisissez une explication dans le fichier
lineItemUpdates[x].reason
. - Définissez
lineItemUpdates[x].orderState
surCONFIRMED
.
Vous pouvez, à votre discrétion, tenter de débiter le mode de paiement avant ou après l'envoi du message AsyncOrderUpdateRequestMessage. Si la transaction échoue (probablement parce que le delta de prix est trop élevé), envoyez un message AsyncOrderUpdateRequestMessage avec les paramètres suivants dans OrderUpdate
pour informer Google de l'échec:
- Définissez
orderState
surREJECTED
. - Décrivez l'échec dans le champ
label
.
Validation du règlement
Comme indiqué à l'Étape 4: Implémenter le règlement, votre point de terminaison de traitement doit effectuer une validation à chaque CheckoutRequestMessage
entrant et répondre avec une CheckoutResponseMessage
.
Voici un exemple de CheckoutResponseMessage
pour une validation réussie:
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la validation a réussi | Renvoyez CheckoutResponse . Il doit contenir ProposedOrder et PaymentOptions .
ProposedOrder inclut les taxes, les frais et le prix total du panier. |
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 } } } } } ] } } }
Validation de l'adresse de livraison
Votre point de terminaison de traitement doit valider l'adresse de livraison contenue dans chaque CheckoutRequestMessage
.
En cas de problème avec l'adresse de livraison, par exemple si elle est hors de portée du service de livraison, le CheckoutResponseMessage
renvoyé par votre traitement doit contenir un FoodOrderError
du type approprié.
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la validation a échoué, car l'adresse de livraison est hors plage ou présente un problème. | Renvoie FoodErrorExtension avec un FoodOrderError de type d'erreur 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." } ] } } } ] } } }
Validation du montant minimal de commande
Votre point de terminaison de traitement doit valider le montant minimal de commande de chaque CheckoutRequestMessage
.
Si le montant minimal de commande n'est pas atteint, la CheckoutResponseMessage
renvoyée par votre traitement doit contenir une FoodOrderError
de type d'erreur REQUIREMENTS_NOT_MET
.
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la validation a échoué, car le montant minimal de commande n'est pas atteint | Renvoie FoodErrorExtension avec un FoodOrderError de type d'erreur 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." } ] } } } ] } } }
Validation de la fenêtre de commande
Votre point de terminaison de traitement doit valider tous les facteurs susceptibles d'affecter la période de commande de chaque CheckoutRequestMessage
.
Par exemple, si le restaurant est fermé ou ne prend plus de commandes pour le moment, le CheckoutResponseMessage
renvoyé par le traitement doit contenir un FoodOrderError
de type d'erreur CLOSED
ou NO_CAPACITY
, respectivement.
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la validation a échoué, car le restaurant est fermé ou n'est plus accepté | Renvoie FoodErrorExtension avec un FoodOrderError de type d'erreur CLOSED . |
Cas d'utilisation 2:la validation a échoué, car le restaurant est occupé et ne prend pas de commandes en ce moment. | Renvoie FoodErrorExtension avec un FoodOrderError de type d'erreur 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." } ] } } } ] } } }
Validation des articles du panier
Votre point de terminaison de traitement doit valider le prix et la disponibilité de chaque article du panier contenu dans un CheckoutRequestMessage
.
Si la disponibilité ou le prix ont changé, le CheckoutResponseMessage
renvoyé par votre traitement doit contenir un FoodOrderError
de type d'erreur AVAILABILITY_CHANGED
ou PRICE_CHANGED
, respectivement.
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la validation a échoué, car certains éléments du menu et/ou leurs personnalisations ne sont pas valides ou ne sont pas disponibles | Renvoie FoodErrorExtension avec correctedProposedOrder , PaymentOptions et FoodOrderError de type d'erreur AVAILABILITY_CHANGED . Les éléments non valides doivent être supprimés de CorrectedProposedOrder . |
Cas d'utilisation 2:la validation a échoué, car certains éléments du menu et/ou leurs personnalisations ne sont pas valides ou ne sont pas disponibles. Le panier corrigé ne répond plus aux exigences concernant le montant minimal de commande. | Renvoyez FoodErrorExtension avec FoodOrderError des types d'erreur AVAILABILITY_CHANGED et REQUIREMENTS_NOT_MET . |
Cas d'utilisation 3:la validation a échoué, car certains éléments du menu et/ou prix de personnalisation ont changé | Renvoie FoodErrorExtension avec correctedProposedOrder , PaymentOptions et FoodOrderError de type d'erreur PRICE_CHANGED . Les prix obsolètes doivent être mis à jour dans le pays suivant :
CorrectedProposedOrder . |
Cas d'utilisation 4:la validation a échoué, car certains éléments du menu et/ou prix de personnalisation ont changé. Le panier corrigé ne répond plus aux exigences concernant le montant minimal de commande | Renvoyez FoodErrorExtension avec FoodOrderError des types d'erreur PRICE_CHANGED et 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 } } ] } } } ] } } }
Envoyer la validation de la commande
Comme indiqué à l'étape 7: Implémenter l'envoi de la commande, votre point de terminaison de traitement doit effectuer une validation à chaque SubmitOrderRequestMessage
entrant et répondre avec une SubmitOrderResponseMessage
.
Voici un exemple de SubmitOrderResponseMessage
pour une validation réussie:
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la commande a bien été créée | Une commande SubmitOrderResponseMessage avec l'état de la commande CREATED . Il doit contenir actionOrderId , userVisibleId , orderManagementActions et estimatedFulfillmentTime . |
Cas d'utilisation 2:la commande est refusée en raison de problèmes de paiement. | Une commande SubmitOrderResponseMessage avec l'état de la commande REJECTED . Les champs actionOrderId , userVisibleId , orderManagementActions et rejectionInfo doivent être de type PAYMENT_DECLINED . |
Cas d'utilisation 3:la commande est refusée, car l'utilisateur est signalé comme exclu. | Une SubmitOrderResponseMessage avec l'état de la commande REJECTED . Les champs actionOrderId , userVisibleId , orderManagementActions et rejectionInfo doivent être de type INELIGIBLE . |
Cas d'utilisation 4:la commande est refusée, car les informations sur l'utilisateur sont incomplètes ou non valides | Une commande SubmitOrderResponseMessage avec l'état de la commande REJECTED . Les champs actionOrderId , userVisibleId , orderManagementActions et rejectionInfo doivent être de type INELIGIBLE . |
Cas d'utilisation 5:la commande est refusée pour une raison inconnue | Une commande SubmitOrderResponseMessage avec l'état de la commande REJECTED . Les champs actionOrderId , userVisibleId , orderManagementActions et rejectionInfo doivent être de type 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" } } } ] } } } ] } } }