Mappage des flux de menus et des articles du panier de traitement
Lorsque les clients ajoutent des articles de votre flux de menu à leur panier et effectuent leur paiement, Google les envoie à votre point de traitement pour vérifier leur prix et leur disponibilité. Une fois les 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 avec les éléments du panier de traitement.
Les exemples de cette section sont des versions allégées du flux de menu et du schéma du panier. Seuls les champs pertinents pour illustrer le mappage entre le flux de menu et l'objet "Panier" sont affichés. Pour obtenir les schémas complets, consultez Menu
et Cart
.
Les articles du flux Menu
ajoutés à un panier sont envoyés dans l'objet Cart
pour le paiement et l'envoi de la commande.
- Un
MenuItem
simple est représenté par unLineItem
dans le tableaulineItems
,offerId
étant l'offer.id
de l'élément de menu sélectionné dans le flux de menu. - Un
MenuItem
avec unMenuItemOption
obligatoire est représenté par unLineItem
dans le tableaulineItems
,offerId
étant l'offer.id
de l'option d'élément de menu sélectionnée dans le flux de menu. - Un
AddOnMenuItem
d'unLineItem
est représenté par unFoodItemOption
dans le tableauoptions
deFoodItemExtension
. Chaque option est associée à unofferId
qui correspond à l'offer.id
de l'élément de menu du module complémentaire sélectionné dans le flux de menu. Notez qu'un AddOnMenuItem peut également avoir des AddOnMenuItem 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.
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" } ] } ] }
Éléments de menu mappé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" } ] }
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" } ] } ] }
Éléments de menu mappé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" } ] }
Cet exemple contient un élément de menu avec des options d'élément de menu, des AddOnMenuItems et des 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" } ] } ] } ] } ] } ] } } ] }
Éléments de menu mappé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 un CheckoutResponseMessage
contenant un FoodErrorExtension
plutôt qu'un CheckoutResponse. Vous pouvez utiliser cette réponse pour identifier une ou plusieurs erreurs survenues 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 changé de prix, vous pouvez répondre avec unFoodOrderError
de type d'erreurPRICE_CHANGED
, ainsi que lescorrectedProposedOrder
etpaymentOptions
. Google informe l'utilisateur du changement, mais lui permet de l'envoyer aveccorrectedProposedOrder
. L'utilisateur peut également revenir en arrière et modifier son panier si nécessaire. 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 un
FoodOrderError
de type d'erreurCLOSED
. Google informe l'utilisateur et gère l'interaction pour qu'il passe à un nouveau restaurant. Vous recevrez un nouveauCheckoutRequestMessage
pour un nouveau panier.
En règle générale, rendez les erreurs au niveau du panier irrécupérables et les erreurs au niveau des articles récupérables. Pour obtenir la liste complète des types d'erreurs et 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 demande de paiement d'un client, procédez comme suit:
- Répondez à
CheckoutRequestMessage
avec unCheckoutResponseMessage
contenant unFoodErrorExtension
, comme décrit dans la section Gestion des erreurs. - Dans la réponse d'erreur, utilisez
correctedProposedOrder.cart
pour mettre à jour le prix avec la valeur correcte. Google reçoit la commande corrigée et peut émettre une nouvelleCheckoutRequestMessage
.
Après le règlement, Google affiche une page de confirmation de commande à l'utilisateur final, que le ProposedOrder
ait été modifié ou non.
Si l'ordre proposé a été corrigé, Google peut afficher des avertissements supplémentaires pour informer l'utilisateur des modifications. Si l'utilisateur accepte de passer la commande, aucune autre demande de paiement ne sera envoyée. Le flux continue jusqu'à l'envoi de la commande, avec le ProposedOrder
corrigé.
Toutefois, l'utilisateur peut toujours changer d'avis et modifier son panier. Lorsque le panier est mis à jour de cette manière, Google envoie un nouveau CheckoutRequestMessage
.
Changement de prix 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 répondez pas par une erreur ni ne modifiez le prix dans votre réponse. Si les prix, les quantités ou d'autres informations de l'élément SubmitOrderRequestMessage
ne correspondent pas à vos données, répondez en définissant orderState
sur REJECTED
pour indiquer que la commande ne peut pas être passée comme demandé.
Ensuite, si les informations de commande et de paiement sont valides, définissez orderState
sur CREATED
ou CONFIRMED
. Incluez également un 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é le SubmitOrderRequestMessage
, vous pouvez envoyer un AsyncOrderUpdateRequestMessage
avec orderState
défini sur REJECTED
pour informer l'utilisateur que la commande ne sera pas traitée.
Changement de prix après l'envoi de la commande
Si vous constatez qu'un prix a changé par rapport à celui utilisé lorsqu'un client a passé sa commande, vous pouvez émettre un AsyncOrderUpdateRequestMessage
, comme décrit dans la section Implémenter des mises à jour asynchrones des commandes, avec le nouveau prix.
Pour mettre à jour les prix à l'aide de mises à jour des commandes asynchrones:
- Modifiez le prix dans
lineItemUpdates[x].price
. Cette valeur reflète le coût total de l'article, y compris les suppléments et multiplié par la quantité. (Pour en savoir plus, consultez la description du champprice
deLineItem
.) - Saisissez une explication dans
lineItemUpdates[x].reason
. - Définissez
lineItemUpdates[x].orderState
surCONFIRMED
.
Vous pouvez tenter de débiter le mode de paiement avant ou après l'envoi du message AsyncOrderUpdateRequestMessage, à votre discrétion. Si la transaction échoue (peut-être parce que la différence de prix est trop élevée), envoyez un 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é dans la section Étape 4: Implémenter le règlement, votre point de terminaison de traitement doit effectuer une validation sur chaque CheckoutRequestMessage
entrante et répondre avec un CheckoutResponseMessage
.
Voici un exemple de CheckoutResponseMessage
pour une validation réussie:
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:validation réussie | Renvoyez CheckoutResponse . Il doit disposer de ProposedOrder et de PaymentOptions .
ProposedOrder inclut les taxes, les frais et le prix total du panier. |
{ "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 n'est pas couverte par le 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 | Renvoyez FoodErrorExtension avec FoodOrderError de type d'erreur OUT_OF_SERVICE_AREA . |
{ "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 des commandes doit valider la valeur minimale de commande de chaque CheckoutRequestMessage
.
Si le montant minimal de commande n'est pas atteint, le CheckoutResponseMessage
renvoyé par votre traitement de commande doit contenir un 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 | Renvoyez FoodErrorExtension avec FoodOrderError de type d'erreur REQUIREMENTS_NOT_MET . |
{ "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 période de commande
Votre point de terminaison de traitement doit valider tous les facteurs pouvant 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 votre 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 pris en charge | Renvoyez FoodErrorExtension avec FoodOrderError de type d'erreur CLOSED . |
Cas d'utilisation 2:la validation a échoué, car le restaurant est occupé et n'accepte pas les commandes pour le moment | Renvoyez FoodErrorExtension avec FoodOrderError de type d'erreur NO_CAPACITY . |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "CLOSED", "description": "The restaurant is closed." } ] } } } ] } } }
{ "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 de menu et/ou leurs personnalisations ne sont pas valides ou sont en rupture de stock | Renvoyez FoodErrorExtension avec correctedProposedOrder , PaymentOptions et FoodOrderError du 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 de menu et/ou leurs personnalisations ne sont pas valides ou sont en rupture de stock. Le panier corrigé ne répond plus aux exigences de montant minimal de commande. | Renvoyez FoodErrorExtension avec FoodOrderError pour les types d'erreurs AVAILABILITY_CHANGED et REQUIREMENTS_NOT_MET . |
Cas d'utilisation 3:la validation a échoué, car certains éléments de menu et/ou les prix de personnalisation ont changé | Renvoyez FoodErrorExtension avec correctedProposedOrder , PaymentOptions et FoodOrderError de type d'erreur PRICE_CHANGED . Les prix obsolètes doivent être mis à jour dans CorrectedProposedOrder . |
Cas d'utilisation 4:la validation a échoué, car certains éléments de menu et/ou les prix de personnalisation ont changé. Le panier corrigé ne répond plus au montant minimal de commande requis | Renvoyez FoodErrorExtension avec FoodOrderError pour les types d'erreurs PRICE_CHANGED et REQUIREMENTS_NOT_MET . |
{ "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 } } } } } ] } } }
{ "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." } ] } } } ] } } }
{ "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 } } } } } ] } } }
{ "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é dans l'étape 7: Implémenter l'envoi de la commande, votre point de terminaison de traitement doit effectuer une validation sur chaque SubmitOrderRequestMessage
entrante et répondre avec un SubmitOrderResponseMessage
.
Voici un exemple de SubmitOrderResponseMessage
pour une validation réussie:
Cas d'utilisation | Mise en œuvre |
---|---|
Cas d'utilisation 1:la commande est créée | SubmitOrderResponseMessage avec l'état de 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 | SubmitOrderResponseMessage avec l'état de commande REJECTED . Il doit contenir actionOrderId , userVisibleId , orderManagementActions et rejectionInfo de type PAYMENT_DECLINED . |
Cas d'utilisation 3:la commande est refusée, car l'utilisateur est signalé comme banni | SubmitOrderResponseMessage avec l'état de commande REJECTED . Il doit contenir actionOrderId , userVisibleId , orderManagementActions et rejectionInfo de type INELIGIBLE . |
Cas d'utilisation 4:la commande est refusée, car les informations sur l'utilisateur sont incomplètes ou non valides | SubmitOrderResponseMessage avec l'état de commande REJECTED . Il doit contenir actionOrderId , userVisibleId , orderManagementActions et rejectionInfo de type INELIGIBLE . |
Cas d'utilisation 5:la commande est refusée pour une raison inconnue | SubmitOrderResponseMessage avec l'état de commande REJECTED . Il doit contenir actionOrderId , userVisibleId , orderManagementActions et rejectionInfo de type UNKNOWN . |
{ "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" } } } ] } } } ] } } }
{ "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" } } } ] } } } ] } } }
{ "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" } } } ] } } } ] } } }
{ "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" } } } ] } } } ] } } }
{ "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" } } } ] } } } ] } } }