Fonction de commande à l'avance v1

Vous pouvez ajouter une fonctionnalité dans votre traitement permettant aux utilisateurs de planifier à l'avance la livraison et le retrait de leurs commandes de repas. Avant d'implémenter cette fonctionnalité dans l'exécution, créez un flux d'inventaire de services qui spécifie les heures auxquelles les utilisateurs peuvent passer des commandes à l'avance, comme décrit dans le schéma du flux d'inventaire (AdvanceServiceDeliveryHoursSpecification).

Créneaux horaires de commande anticipée

Google propose des créneaux de commande à l'avance par tranche de 15 minutes, jusqu'à sept jours à l'avance, en fonction des délais de traitement d'un restaurant ou d'un service (tels que définis dans AdvanceServiceDeliveryHoursSpecification).

Pour récupérer les créneaux proposés pour la commande à l'avance, utilisez les valeurs suivantes du champ fulfillmentPreference de l'objet FoodCartExtension au moment du règlement:

  • PickupInfo.pickupTimeIso8601
  • DeliveryInfo.deliveryTimeIso8601

Implémenter les commandes anticipées lors du paiement

Le tableau ci-dessous présente les différentes façons d'implémenter la réponse de votre traitement au moment du règlement lorsque les utilisateurs tentent de passer commande.

Scénario Comportement de traitement
La commande anticipée peut être traitée pour l'emplacement demandé. Acceptez le panier P0M ("dès que possible") ou FUTURE_SLOT en créant un ProposedOrder avec le même emplacement. Pour obtenir un exemple de réponse de règlement acceptant un créneau horaire, consultez cet extrait de code.
La commande anticipée ne peut pas être traitée pour le créneau horaire demandé. Votre prestataire de traitement doit effectuer les opérations suivantes :
  1. Refusez le panier P0M ou FUTURE_SLOT demandé, puis indiquez la raison pour laquelle la commande ne peut pas être traitée dans l'objet FoodErrorExtension.
    • Si la commande ne peut pas être exécutée en raison de la capacité, spécifiez un FoodOrderError de type d'erreur NO_CAPACITY.
    • Si la commande ne peut pas être traitée parce que le restaurant est fermé, spécifiez un FoodOrderError de type d'erreur CLOSED.
    • Si la commande ne peut pas être traitée pour une autre raison, spécifiez un FoodOrderError de type d'erreur UNAVAILABLE_SLOT.
  2. Si possible, fournissez d'autres valeurs P0M ou FUTURE_SLOT dans correctedProposedOrder. Ces valeurs doivent correspondre à tous les créneaux de traitement valides pour les sept prochains jours à partir de l'heure actuelle. Incluez l'emplacement P0M, le cas échéant.

Pour voir un exemple de réponse de règlement proposant d'autres créneaux, consultez cet extrait de code.

Autres créneaux horaires pour le traitement des commandes

Lors du règlement, si les créneaux proposés par Google pour la commande à l'avance ne conviennent pas, votre service de traitement peut suggérer d'autres créneaux à l'aide de l'objet CheckoutResponseMessage.

Pour spécifier d'autres créneaux de commande anticipée, répondez à la requête de paiement avec un FoodErrorExtension et définissez les valeurs suivantes:

  1. Dans le paramètre foodOrderErrors, spécifiez le type d'erreur (par exemple, UNAVAILABLE_SLOT, NO_CAPACITY ou CLOSED).
  2. Dans le paramètre correctedProposedOrder, indiquez d'autres valeurs P0M ou FUTURE_SLOT via availableFulfillmentOptions.

Les créneaux alternatifs doivent concerner les sept prochains jours à compter de la date de passation de la commande et inclure tous les créneaux au cours desquels le panier demandé par l'utilisateur peut être expédié.

Par exemple, imaginons que les plats du jour ne sont disponibles que du lundi au vendredi de 11h à 13h. L'utilisateur tente ensuite d'ajouter des plats du jour à son panier, mais l'heure sélectionnée n'est pas disponible. Dans ce cas, votre traitement doit conserver les plats du jour dans le panier et ne renvoyer que les créneaux de 11h à 13h pour les sept prochains jours.

Vous devez omettre l'objet correctedProposedOrder.Cart.fulfillmentPreference dans votre réponse.

Si aucun créneau n'est disponible ou si le restaurant ou le service n'accepte pas les commandes à l'avance, vous n'avez pas besoin de fournir de correctedProposedOrder.

Consultez les exemples ci-dessous pour les messages JSON échangés entre votre prestataire de traitement et Google lors du flux de requête et de réponse de paiement pour une commande anticipée, lorsque le restaurant ou le service est disponible pour les précommandes.

Exemple: CheckoutRequest avec créneau de livraison

L'extrait de code ci-dessous montre un exemple de requête de paiement avec un créneau de livraison de commande anticipée.

{
  "inputs": [
    {
      "intent": "actions.foodordering.intent.CHECKOUT",
      "arguments": [
        {
          "extension": {
            "@type": "type.googleapis.com/google.actions.v2.orders.Cart",
            "merchant": {
              "id": "https://www.exampleprovider.com/merchant/id1",
              "name": "Cucina Venti"
            },
            "lineItems": [
              {
                "name": "Sizzling Prawns Dinner",
                "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": "16",
                    "nanos": 750000000
                  }
                },
              }
            ],
            "extension": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
              "fulfillmentPreference": {
                "fulfillmentInfo": {
                  "delivery": {
                    // Deliver at 6:30PM.
                    "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                  }
                }
              },
              "location": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

Exemple: CheckoutResponse acceptant l'emplacement

L'extrait de code ci-dessous montre un exemple de réponse de règlement dans lequel votre chaîne d'exécution accepte les créneaux de commande anticipée proposés.

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "checkoutResponse": {
              "proposedOrder": {
                "id": "sample_proposed_order_id_1",
                "cart": {
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Falafel Bite"
                  },
                  "lineItems": [
                    {
                      "name": "Sizzling Prawns Dinner",
                      "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": "16",
                          "nanos": 750000000
                        }
                      },
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          // Same as the time in the request.
                          "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                        }
                      }
                    },
                    "location": {
                      ...
                     }
                   }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    // Represents $16.75
                    "currencyCode": "USD",
                    "units": "16",
                    "nanos": 750000000
                  }
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  // Send whole proposed order back.
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        // Same as the time in the request.
                        "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                      }
                    }
                  ]
                }
              },
              "paymentOptions": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

Exemple: CheckoutResponse avec des emplacements de remplacement

L'extrait de code ci-dessous montre un exemple de réponse de règlement dans lequel votre chaîne d'exécution propose d'autres créneaux de commande anticipée. Notez que l'objet correctedProposedOrder.Cart.fulfillmentPreference doit être omis dans votre réponse.

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "UNAVAILABLE_SLOT", // Cart level error
                  "description": "The restaurant is closed."
                }
              ],
              "correctedProposedOrder": {
                // Send whole original cart back,
                // without the fulfillmentPreference.
                "cart": {
                  ...
                },
                "otherItems": {
                  ...
                },
                "totalPrice": {
                  ...
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T19:00:00-07:00"
                      }
                    },
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T19:30:00-07:00"
                      }
                    },
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T20:00:00-07:00"
                      }
                    }
                  ]
                }
              },
              "paymentOptions": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

Implémenter les commandes anticipées au moment de l'envoi de la commande

Lors de l'envoi de la commande, si un problème survient avec les créneaux de commande anticipée, votre SubmitOrderResponseMessage doit inclure le motif (par exemple, UNAVAILABLE_SLOT ou UNKNOWN) dans l'objet RejectionInfo.

Passez l'état de la commande de CREATED à CONFIRMED dans l'objet OrderState lorsque la commande est acceptée par le fournisseur. Incluez la plage horaire sélectionnée dans l'e-mail de confirmation envoyé à l'utilisateur.

Si votre système de traitement envoie la commande au restaurant plus tard, envoyez une mise à jour à Google à l'aide de l'action de mise à jour de commande asynchrone.

Dans l'objet OrderUpdate de la réponse d'envoi de commande de votre chaîne d'exécution ou des mises à jour de commande asynchrones ultérieures, incluez un estimatedFulfillmentTimeIso8601 dont la valeur est définie comme suit:

  • Lorsque l'état de la commande est CREATED ou CONFIRMED, définissez la valeur sur l'heure de livraison ou de retrait que l'utilisateur a planifiée pour sa commande anticipée.
  • Lorsque le restaurant ou le service indique un délai de livraison estimé plus précis, définissez la valeur sur le délai de livraison ou de retrait estimé.

Exemple: SubmitOrderRequest avec créneau de livraison

L'extrait de code ci-dessous montre un exemple de requête d'envoi de commande indiquant l'heure de commande anticipée sélectionnée par l'utilisateur.

{
  "inputs": [
    {
      "intent": "actions.intent.TRANSACTION_DECISION",
      "arguments": [
        {
          "transactionDecisionValue": {
            "order": {
              "finalOrder": {
                "cart": {
                  "notes": "Guest prefers their food to be hot when it is delivered.",
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Cucina Venti"
                  },
                  "lineItems": [
                    {
                      "name": "Sizzling Prawns Dinner",
                      "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": "16",
                          "nanos": 750000000
                        }
                      }
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                        }
                      }
                    }
                    "contact": {
                      ...
                    }
                  }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    "currencyCode": "USD",
                    "units": "16",
                    "nanos": 750000000
                  }
                },
                "id": "sample_final_order_id",
                "extension": {
                  // Send whole proposed order back.
                  "availableFulfillmentOptions": [
                    "fulfillmentInfo": {
                      "delivery": {
                        "deliveryTimeIso8601": "2017-12-14T18:30:00-07:00"
                      }
                   ]
                }
              },
              "googleOrderId": "sample_google_order_id",
              "orderDate": "2017-07-17T12:00:00Z",
              "paymentInfo": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

Exemple: SubmitOrderResponse acceptant la commande

L'extrait de code ci-dessous montre un exemple de réponse d'envoi de commande dans laquelle votre traitement confirme avoir accepté la commande anticipée de l'utilisateur.

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "CREATED",
                "label": "Order placed"
              },
              "receipt": {
                "userVisibleOrderId": "userVisibleId1234"
              },
              "updateTime": "2017-07-17T12:00:00Z",
              "orderManagementActions": [
                ...
              ],
              "infoExtension": {
                 "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
                 // Same as the user selected time.
                 "estimatedFulfillmentTimeIso8601": "2017-12-14T18:30:00-07:00"
              }
            }
          }
        }
      ]
    }
  }
}

Exemple: SubmitOrderResponse refusant la commande en raison de l'indisponibilité d'un créneau

L'extrait de code ci-dessous montre un exemple de réponse d'envoi de commande dans laquelle votre traitement refuse la commande anticipée d'un utilisateur en raison d'un créneau horaire indisponible.

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Unavailable slot"
              },
              "rejectionInfo": {
                // Note that this UNAVAILABLE_SLOT is different from the enum
                // with the same name proposed for FoodOrderError.
                "state": "UNAVAILABLE_SLOT",
                "label": "Unavailable slot"
              },
              "updateTime": "2017-07-17T12:00:00Z",
              "orderManagementActions": [
                ...
              ]
            }
          }
        }
      ]
    }
  }
}

Exemples de commandes avancées

Le type AdvanceServiceDeliveryHoursSpecification permet de spécifier les heures de livraison ou de retrait pour que les utilisateurs puissent planifier leur commande à l'avance.

Remarque : Vous devez spécifier deux périodes distinctes pour le traitement du service: la période de commande, qui indique quand les utilisateurs peuvent passer une commande, et la période de traitement, qui indique quand la commande doit être traitée. L'objet OpeningHoursSpecification définit le moment où l'utilisateur peut passer la commande. Les délais de traitement de ses enfants (ServiceDeliveryHoursSpecification ou AdvanceServiceDeliveryHoursSpecification) définissent quand la commande peut être traitée.

L'exemple suivant définit les heures d'acceptation des commandes à l'avance d'un service, avec des intervalles de service de 15 minutes.

{
  "hoursAvailable": [
    {
      "@type": "OpeningHoursSpecification",
      "opens": "T00:00:00", // Ordering available 24 hours
      "closes": "T23:59:59",
      "deliveryHours": [
        {
          "@type": "ServiceDeliveryHoursSpecification",
          "opens": "T09:00:00", // ASAP orders b/w 9am and 8:59:59pm
          "closes": "T21:00:00",
          "deliveryLeadTime": {
            "value": "60",
            "unitCode": "MIN"
          }
        },
        {
          "@type": "AdvanceServiceDeliveryHoursSpecification",
          "opens": "T10:00:00",  // Delivery between 10AM and 7:59:59PM
          "closes": "T20:00:00",
          "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart (ISO8601)
          "advanceBookingRequirement": {
            "minValue": 60,   // The slot should be at least 60 mins away
            "maxValue": 8640, // but not more than 6 days away
            "unitCode": "MIN"
          }
        }
      ]
    }
  ]
}

L'exemple suivant montre comment spécifier que le service est ouvert pour les commandes du même jour le jour de Noël, mais fermé pour les commandes avancées planifiées pour ce jour. Cet exemple est compatible avec les scénarios suivants:

  • Les utilisateurs peuvent passer commande le 25 décembre pour une livraison le même jour.
  • Les utilisateurs peuvent passer une commande anticipée le 25 décembre pour une livraison prévue le 27 décembre.
  • Les utilisateurs ne peuvent pas passer de commande anticipée le 22 décembre pour une livraison prévue le 25 décembre.
{
  "specialOpeningHoursSpecification": {
    "@type": "AdvanceServiceDeliveryHoursSpecification",
    "validFrom": "2018-12-25T00:00:00-07:00",
    "validThrough": "2018-12-26T00:00:00-07:00",
    "opens": "T00:00:00", // No advance ordering
    "closes": "T00:00:00"
  }
}

L'exemple suivant montre comment spécifier que le service est fermé pour les commandes le même jour ou les commandes à l'avance planifiées pour le jour de Noël, mais ouvert pour les commandes à l'avance planifiées pour un autre jour. Cet exemple est compatible avec les scénarios suivants:

  • Les utilisateurs ne peuvent pas passer de commande le 25 décembre pour une livraison le même jour.
  • Les utilisateurs peuvent passer une commande anticipée le 25 décembre pour une livraison prévue le 27 décembre.
  • Les utilisateurs ne peuvent pas passer de commande anticipée le 22 décembre pour une livraison prévue le 25 décembre.
{
  "specialOpeningHoursSpecification": [
    {
      "@type": "ServiceDeliveryHoursSpecification",
      "validFrom": "2018-12-25T00:00:00-07:00",
      "validThrough": "2018-12-26T00:00:00-07:00",
      "opens": "T00:00:00", // No ASAP ordering on Christmas
      "closes": "T00:00:00"
    },
    {
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "validFrom": "2018-12-25T00:00:00-07:00",
      "validThrough": "2018-12-26T00:00:00-07:00",
      "opens": "T00:00:00", // Orders cannot be scheduled for Christmas
      "closes": "T00:00:00"
    }
  ]
}

L'exemple de service suivant accepte les commandes 24h/24, 7j/7 et livre les commandes entre 10h et 14h59 les jours de la semaine :

...
{
  "@type": "OpeningHoursSpecification",
  "opens": "T00:00:00",
  "closes": "T23:59:59",
  "deliveryHours": {
    "@type": "AdvanceServiceDeliveryHoursSpecification",
    "opens": "T10:00:00", // Delivery starts at 10:00AM
    "closes": "T15:00:00", // Delivery ends at 3:00PM. Delivery from 10AM-2:59:59PM.
    "dayOfWeek": [
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday"
    ],
    "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart
    "advanceBookingRequirement": {
      "minValue": 60,   // The slot should be at least 60 mins away
      "maxValue": 8640, // but not more than 6 days away
      "unitCode": "MIN"
    }
  }
}
...

L'exemple de service suivant accepte les commandes tous les jours de 8h à 16h59:59. Les clients peuvent choisir une livraison dans l'heure ou l'un des créneaux horaires suivants :

...
{
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",  // Ordering opens at 8:00AM
  "closes": "T17:00:00",  // Ordering closes at 5:00PM, last order at 4:59:59PM
  "deliveryHours": [
    {
      "@type": "ServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "deliveryLeadTime": {
        "@type": "QuantitativeValue",
        "value": "60", // If no exact deliveryLeadTime, put a maximum time
        "unitCode": "MIN"
      }
    },
    {
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "serviceTimeInterval": "PT15M", // in slots spaced 15 minutes apart
      "advanceBookingRequirement": {
        "minValue": 90,   // The slot should be at least 90 mins away
        "maxValue": 8640, // but not more than 6 days away
        "unitCode": "MIN"
      }
    }
  ]
}
...

L'exemple suivant montre un cas où le magasin est ouvert de 8h à 16h59:59 en semaine, mais de 8h à 18h30 le week-end. Les commandes ne sont pas acceptées 24h/24, 7j/7.

...
{
  // On weekdays, ordering open from 8AM-4:59:59PM.
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",
  "closes": "T17:00:00",
  "dayOfWeek": [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday"
  ],
  "deliveryHours": [
    {
      // Fulfillment between 8AM-4:59:59PM on weekdays.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "dayOfWeek": [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    },
    {
      // Fulfillment between 8AM-6:59:59PM on weekends (even for orders placed on a
      // weekday).
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T19:00:00",
      "dayOfWeek": [
        "Saturday",
        "Sunday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    }
  ]
},
{
  // On weekends, one can place orders upto 6:59:59PM.
  "@type": "OpeningHoursSpecification",
  "opens": "T08:00:00",
  "closes": "T19:00:00",
  "dayOfWeek": [
    "Saturday",
    "Sunday"
  ],
  "deliveryHours": [
    {
      // But fulfillment on weekdays is only till 4:59:59PM.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T17:00:00",
      "dayOfWeek": [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    },
    {
      // Fulfillment on weekends is till 6:59:59PM.
      "@type": "AdvanceServiceDeliveryHoursSpecification",
      "opens": "T08:00:00",
      "closes": "T19:00:00",
      "dayOfWeek": [
        "Saturday",
        "Sunday"
      ],
      "serviceTimeInterval": "PT15M",
      "advanceBookingRequirement": {
        "minValue": 60,
        "maxValue": 8640,
        "unitCode": "MIN"
      }
    }
  ]
}
...