Fonctionnalité de commande à l'avance v1

Vous pouvez ajouter une assistance dans votre traitement pour que les utilisateurs puissent planifier à l'avance les commandes de repas à emporter et à livrer. Avant d'intégrer cette compatibilité dans le traitement, créez un flux d'inventaire de services qui spécifie les horaires pendant lesquels les utilisateurs peuvent passer des commandes à l'avance, comme décrit dans le schéma du flux d'inventaire (AdvanceServiceDeliveryHoursSpecification).

Avancer les créneaux horaires

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

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

  • PickupInfo.pickupTimeIso8601
  • DeliveryInfo.deliveryTimeIso8601

Mettre en œuvre les commandes à l'avance lors du paiement

Le tableau ci-dessous répertorie les façons dont vous pouvez implémenter la réponse de votre traitement au moment du règlement lorsque les utilisateurs tentent de passer des commandes.

Scénario Comportement de traitement
La commande anticipée peut être traitée pour le créneau 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 emplacement, consultez cet extrait de code.
Impossible de traiter la commande anticipée pour le créneau demandé. Le traitement doit :
  1. Refusez le panier P0M ou FUTURE_SLOT demandé, et 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 sa capacité, spécifiez une FoodOrderError de type d'erreur NO_CAPACITY.
    • Si la commande ne peut pas être traitée, car le restaurant est fermé, spécifiez une 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, indiquez d'autres valeurs P0M ou FUTURE_SLOT dans correctedProposedOrder. Ces valeurs doivent correspondre à tous les emplacements de traitement valides pour les sept prochains jours à partir de l'heure actuelle. Incluez l'emplacement P0M le cas échéant.

Pour obtenir un exemple de réponse de règlement proposant d'autres emplacements, consultez cet extrait de code.

Autres créneaux possibles pour le traitement des commandes

Lors du règlement, si les créneaux de commande à l'avance proposés par Google ne sont pas adaptés, votre traitement peut suggérer des alternatives à l'aide de l'objet CheckoutResponseMessage.

Pour spécifier d'autres créneaux de commande anticipée, répondez à la demande de règlement avec un élément 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 autres emplacements doivent concerner les sept jours suivants à compter de la date de passage de la commande et inclure tous les emplacements dans lesquels le panier demandé par l'utilisateur peut être traité.

Par exemple, supposons que les plats du jour ne soient disponibles que du lundi au vendredi de 11h à 13h. Il essaie ensuite d'ajouter des plats du jour à son panier, mais le créneau sélectionné 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 horaires 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 un correctedProposedOrder.

Consultez les exemples ci-dessous pour les messages JSON entre votre traitement et Google lors de la requête de paiement et du flux de réponse pour une commande à l'avance, lorsque le restaurant ou le service est disponible pour les précommandes.

Exemple: Requête de paiement 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 pour 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 accepte l'emplacement

L'extrait de code ci-dessous montre un exemple de réponse au règlement pour laquelle votre traitement 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 d'autres emplacements

L'extrait de code ci-dessous montre un exemple de réponse au règlement dans laquelle le traitement propose d'autres créneaux de commande anticipée. Notez que l'objet correctedProposedOrder.Cart.fulfillmentPreference doit être omis dans la 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": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

Mettre en œuvre les commandes à l'avance lors de l'envoi de la commande

Lors de l'envoi de la commande, en cas de problème avec les emplacements de commande anticipée, votre fichier SubmitOrderResponseMessage doit inclure le motif (UNAVAILABLE_SLOT ou UNKNOWN, par exemple) dans l'objet RejectionInfo.

Une fois la commande acceptée par le fournisseur, remplacez l'état de la commande CREATED par CONFIRMED dans l'objet OrderState. Incluez le créneau sélectionné dans l'e-mail de confirmation envoyé à l'utilisateur.

Si votre traitement envoie la commande au restaurant ultérieurement, 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 la commande ou des mises à jour asynchrones ultérieures de la commande, incluez un estimatedFulfillmentTimeIso8601 avec la valeur 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 prévue par l'utilisateur pour sa commande anticipée.
  • Lorsqu'il existe un délai de livraison estimé plus précis par le restaurant ou le service, définissez la valeur sur l'heure de livraison ou de retrait en question.

Exemple: SubmitOrderRequest avec un créneau de livraison

L'extrait ci-dessous présente un exemple de demande d'envoi de commande indiquant le créneau de commande anticipée sélectionné 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: acceptation de la commande SubmitOrderResponse

L'extrait de code ci-dessous montre un exemple de réponse d'envoi de commande pour laquelle le traitement confirme que la commande anticipée de l'utilisateur a été acceptée.

{
  "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: refus de la commande SubmitOrderResponse en raison de l'indisponibilité du créneau

L'extrait ci-dessous présente un exemple de réponse d'envoi de commande dans laquelle le traitement refuse la commande anticipée d'un utilisateur en raison d'un créneau 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 anticipées

Le type AdvanceServiceDeliveryHoursSpecification permet de spécifier les heures de livraison ou de retrait afin 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 le moment où les utilisateurs peuvent passer une commande, et la période de traitement, qui spécifie le moment où la commande est traitée. L'objet OpeningHoursSpecification définit le moment où l'utilisateur peut passer la commande. Ses délais de traitement enfants (ServiceDeliveryHoursSpecification ou AdvanceServiceDeliveryHoursSpecification) définissent le moment où la commande peut être exécutée.

L'exemple suivant définit les heures de service d'acceptation des commandes anticipées, 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 le jour même le jour de Noël, mais fermé pour les commandes à l'avance planifiées pour ce jour-là. Cet exemple prend en charge les scénarios suivants:

  • Les utilisateurs peuvent passer une commande le 25 décembre pour une livraison le jour même.
  • Les utilisateurs peuvent effectuer une commande anticipée le 25 décembre pour une livraison prévue le 27 décembre.
  • Les utilisateurs ne peuvent pas effectuer 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 à l'avance planifiées pour Noël, mais qu'il est ouvert aux commandes à l'avance planifiées pour une date ultérieure. Cet exemple est compatible avec les scénarios suivants:

  • Les utilisateurs ne peuvent pas effectuer de commande le 25 décembre pour une livraison le jour même.
  • Les utilisateurs peuvent effectuer une commande anticipée le 25 décembre pour une livraison prévue le 27 décembre.
  • Les utilisateurs ne peuvent pas effectuer 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 effectue une livraison de 10h à 14h59:59 en 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 choisir l'un des créneaux horaires :

...
{
  "@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 ouvre de 8h à 16h59:59 en semaine, mais de 8h à 18h59 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"
      }
    }
  ]
}
...