Recurso de pedido antecipado v1

É possível adicionar suporte ao fulfillment para que os usuários programem pedidos de retirada e entrega de comida com antecedência. Antes de implementar esse suporte no fulfillment, crie um feed de inventário de serviços que especifique o horário de funcionamento dos usuários para fazer pedidos antecipados, conforme descrito no esquema do feed de inventário (AdvanceServiceDeliveryHoursSpecification).

Slots de pedidos antecipados

O Google propõe horários de pedidos antecipados em incrementos de 15 minutos e até 7 dias de antecedência, com base nos tempos de atendimento de um restaurante ou serviço (conforme definido em AdvanceServiceDeliveryHoursSpecification).

Para recuperar os horários de pedido antecipado propostos, use os seguintes valores do campo fulfillmentPreference do objeto FoodCartExtension na finalização da compra:

  • PickupInfo.pickupTimeIso8601
  • DeliveryInfo.deliveryTimeIso8601

Implemente pedidos antecipados na finalização da compra

A tabela abaixo lista as possíveis maneiras de implementar a resposta do atendimento do pedido no momento da finalização da compra, quando os usuários tentam fazer pedidos.

Cenário Comportamento de atendimento
Os pedidos com antecedência podem ser atendidos para o espaço solicitado. Aceite o carrinho de P0M ("assim que possível") ou FUTURE_SLOT criando um ProposedOrder com o mesmo slot. Para conferir um exemplo de uma resposta de finalização de compra aceita um slot, consulte este snippet de código.
Não é possível atender ao pedido antecipado para o espaço solicitado. O fulfillment deve fazer o seguinte:
  1. Rejeite o carrinho de P0M ou FUTURE_SLOT solicitado e indique o motivo pelo qual o pedido não pode ser atendido no objeto FoodErrorExtension.
    • Se não for possível atender o pedido devido à capacidade, especifique um FoodOrderError do tipo de erro NO_CAPACITY.
    • Se não for possível atender o pedido porque o restaurante está fechado, especifique um FoodOrderError do tipo de erro CLOSED.
    • Se o pedido não puder ser atendido por algum outro motivo, especifique um FoodOrderError do tipo de erro UNAVAILABLE_SLOT.
  2. Se possível, forneça valores alternativos de P0M ou FUTURE_SLOT em correctedProposedOrder. Esses valores precisam ser todos os slots de fulfillment válidos nos próximos sete dias a partir do horário atual. Inclua o slot P0M sempre que aplicável.

Para conferir um exemplo de uma resposta de finalização de compra propondo espaços alternativos, consulte este snippet de código.

Slots alternativos para o atendimento do pedido

Na finalização da compra, se os slots de pedidos antecipados propostos pelo Google não forem adequados, o fulfillment poderá sugerir alternativas usando o objeto CheckoutResponseMessage.

Para especificar slots de pedidos antecipados alternativos, responda à solicitação de finalização de compra com um FoodErrorExtension e defina os seguintes valores:

  1. No parâmetro foodOrderErrors, especifique o tipo de erro (como UNAVAILABLE_SLOT, NO_CAPACITY ou CLOSED).
  2. No parâmetro correctedProposedOrder, forneça valores alternativos de P0M ou FUTURE_SLOT usando availableFulfillmentOptions.

Os horários alternativos precisam ser dos próximos sete dias a partir do momento da realização do pedido e incluir todos os espaços em que o carrinho solicitado pelo usuário pode ser atendido.

Por exemplo, digamos que os pratos executivos estejam disponíveis apenas de segunda a sexta-feira, das 11h às 13h. O usuário tenta adicionar pratos executivos ao carrinho, mas o horário selecionado não está disponível. Nesse caso, o atendimento do pedido precisa reter as promoções de almoço no carrinho e retornar apenas os horários das 11h às 13h nos próximos sete dias.

Omita o objeto correctedProposedOrder.Cart.fulfillmentPreference na resposta.

Se não houver slots disponíveis ou se o restaurante ou serviço não atender a pedidos antecipados, você não vai precisar fornecer um correctedProposedOrder.

Confira os exemplos abaixo para as mensagens JSON entre seu fulfillment e o Google durante a solicitação de finalização da compra e o fluxo de resposta de um pedido antecipado, quando o restaurante ou serviço estiver disponível para encomendas.

Exemplo: CheckoutRequest com slot de entrega

O snippet abaixo mostra um exemplo de solicitação de finalização de compra com um espaço de entrega de pedido antecipado.

{
  "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": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

Exemplo: CheckoutResponse que aceita o espaço

O snippet abaixo mostra um exemplo de resposta de finalização de compra em que o fulfillment aceita os espaços de pedidos antecipados propostos.

{
  "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": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

Exemplo: CheckoutResponse com slots alternativos

O snippet abaixo mostra um exemplo de resposta de finalização de compra em que o fulfillment propõe slots de pedido antecipado alternativos. O objeto correctedProposedOrder.Cart.fulfillmentPreference precisa ser omitido na resposta.

{
  "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": {
                ...
              }
            }
          }
        }
      ]
    }
  }
}

Implementar pedidos antecipados no envio do pedido

Ao enviar o pedido, se houver um problema com os slots de pedidos antecipados, o SubmitOrderResponseMessage precisará incluir o motivo (como UNAVAILABLE_SLOT ou UNKNOWN) no objeto RejectionInfo.

Atualize o estado do pedido de CREATED para CONFIRMED no objeto OrderState quando o pedido for aceito pelo provedor. Inclua o horário selecionado no e-mail de confirmação enviado ao usuário.

Se o fulfillment enviar o pedido ao restaurante mais tarde, informe ao Google uma atualização usando a ação assíncrona de atualização de pedido.

No objeto OrderUpdate da resposta do pedido de envio do fulfillment ou nas atualizações de pedido assíncronas subsequentes, inclua um estimatedFulfillmentTimeIso8601 com o valor definido da seguinte maneira:

  • Quando o status do pedido for CREATED ou CONFIRMED, defina o valor como o horário de entrega ou retirada que o usuário agendou para o pedido antecipado.
  • Quando houver um tempo de entrega estimado mais preciso do restaurante ou do serviço, defina o valor como o tempo estimado para entrega ou retirada.

Exemplo: SubmitOrderRequest com espaço de entrega

O snippet abaixo mostra um exemplo de solicitação de envio de pedido que indica o slot do pedido avançado que o usuário selecionou.

{
  "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": {
                ...
              }
            }
          }
        }
      ]
    }
  ]
}

Exemplo: SubmitOrderResponse aceitando o pedido

O snippet abaixo mostra um exemplo de resposta de envio de pedido em que o fulfillment confirma que aceitou o pedido antecipado do usuário.

{
  "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"
              }
            }
          }
        }
      ]
    }
  }
}

Exemplo: SubmitOrderResponse recusa o pedido por indisponibilidade de horário

O snippet abaixo mostra um exemplo de resposta de envio de pedido em que o fulfillment recusa o pedido antecipado de um usuário devido a um espaço indisponível.

{
  "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": [
                ...
              ]
            }
          }
        }
      ]
    }
  }
}

Exemplos de pedidos avançados

O tipo AdvanceServiceDeliveryHoursSpecification pode ser usado para especificar os horários de entrega ou retirada para que os usuários programem o pedido com antecedência.

Observação : há dois períodos que precisam ser especificados para o fulfillment do serviço: a janela de pedido, que especifica quando os usuários podem fazer um pedido, e a janela de fulfillment, que especifica quando o pedido será atendido. O objeto OpeningHoursSpecification define quando o usuário pode fazer o pedido. Os tempos de fulfillment filhos (ServiceDeliveryHoursSpecification ou AdvanceServiceDeliveryHoursSpecification) definem quando o pedido pode ser atendido.

O exemplo a seguir define o horário de funcionamento de um serviço para aceitar pedidos antecipados, com intervalos de atendimento de 15 minutos.

{
  "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"
          }
        }
      ]
    }
  ]
}

O exemplo a seguir mostra como especificar que o serviço está aberto para pedidos no mesmo dia no Natal, mas fechado para pedidos avançados programados para esse dia. Este exemplo é compatível com os seguintes cenários:

  • Os usuários podem fazer um pedido em 25 de dezembro para entrega no mesmo dia.
  • Os usuários podem fazer um pedido antecipado em 25 de dezembro para entrega programada para 27 de dezembro.
  • Os usuários não podem fazer um pedido antecipado em 22 de dezembro para entrega programada para 25 de dezembro.
{
  "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"
  }
}

O exemplo a seguir mostra como especificar que o serviço está fechado para pedidos no mesmo dia ou pedidos antecipados programados para o Natal, mas aberto para pedidos avançados programados para um dia posterior. Este exemplo é compatível com os seguintes cenários:

  • Os usuários não podem fazer um pedido em 25 de dezembro para entrega no mesmo dia.
  • Os usuários podem fazer um pedido antecipado em 25 de dezembro para entrega programada para 27 de dezembro.
  • Os usuários não podem fazer um pedido antecipado em 22 de dezembro para entrega programada para 25 de dezembro.
{
  "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"
    }
  ]
}

O exemplo de serviço a seguir aceita pedidos 24 horas por dia, 7 dias por semana, e entregas das 10h às 14h59:59 nos dias úteis:

...
{
  "@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"
    }
  }
}
...

O exemplo de serviço a seguir aceita pedidos todos os dias das 8h às 16h59:59. Os clientes podem optar por uma entrega no prazo de uma hora ou escolher um dos horários:

...
{
  "@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"
      }
    }
  ]
}
...

O exemplo a seguir mostra um caso em que a loja abre das 8h às 16h59:59 nos dias úteis, mas das 8h às 18h59 nos fins de semana. Pedidos não são aceitos 24 horas por dia, 7 dias por semana.

...
{
  // 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"
      }
    }
  ]
}
...