Implementar ações avançadas de fulfillment

Mapeamento de feeds do menu e itens do carrinho de atendimento do pedido

Quando os clientes adicionam itens do seu feed de cardápio ao carrinho e finalizam a compra, o Google os envia ao endpoint de atendimento do pedido para verificar o preço e a disponibilidade. Depois que o preço e a disponibilidade forem validados, o cliente poderá fazer o pedido. Esta seção ilustra como mapear itens do feed de menu para itens do carrinho de atendimento do pedido.

Os exemplos desta seção são versões simplificadas do feed de cardápio e do esquema do carrinho. Somente os campos relevantes para ilustrar o mapeamento entre o feed de cardápio e o objeto "Carrinho" são exibidos. Para esquemas completos, consulte Menu e Cart.

Os itens no feed Menu adicionados a um carrinho são enviados no objeto Cart para finalização da compra e envio do pedido.

  • Um MenuItem simples é representado como um LineItem na matriz lineItems, com o offerId sendo a offer.id do item de menu selecionado no feed de menu.
  • Um MenuItem com um MenuItemOption obrigatório é representado como um LineItem na matriz lineItems, com offerId sendo a opção de item selecionada offer.id no feed de menu.
  • Um AddOnMenuItem de um LineItem é representado como um FoodItemOption na matriz options do FoodItemExtension. Cada opção tem um offerId que corresponde ao offer.id do item de menu do complemento selecionado no feed de menu. Um AddOnMenuItem também pode ter AddOnMenuItem(s) aninhados que são representados como subOptions dentro de cada opção.

Os exemplos a seguir mapeiam itens de menu entre o feed de cardápio e o carrinho de fulfillment.

JSON

Este exemplo contém uma lista de itens de menu simples.

Itens do cardápio em um feed:

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

Itens de menu mapeados para um carrinho de atendimento do pedido:

{
  "@type": "Cart",
  "lineItems": [
    {
      "offerId": "menuitem_offer_id_1",
      "price": {
        "amount": {
          "currencyCode": "USD",
          "units": "dollar(q_1*p_1)",
          "nanos": "cent(q_1*p_1)"
        }
      },
      "quantity": "q_1"
    },
    {
      "offerId": "menuitem_offer_id_2",
      "price": {
        "amount": {
          "currencyCode": "USD",
          "units": "dollar(q_2*p_2)",
          "nanos": "cent(q_2*p_2)"
        }
      },
      "quantity": "q_2"
    }
  ]
}

JSON

Este exemplo contém um item de menu com um ou mais AddOnMenuItems.

Itens do cardápio em um feed:

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

Itens de menu mapeados para um carrinho de atendimento do pedido:

{
  "@type": "Cart",
  "lineItems": [
    {
      "offerId": "menuitem_offer_id_1",
      "price": {
        "amount": {
          "currencyCode": "USD",
          "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))",
          "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))"
        }
      },
      "quantity": "q_1",
      "extension": {
        "@type": "FoodItemExtension",
        "options": [
          {
            "offerId": "menuitem_addon_offer_id_1",
            "price": {
                "currencyCode": "USD",
                "units": "dollar(addon_q_1*addon_p_1)",
                "nanos": "cent(addon_q_1*addon_p_1)"
            },
            "quantity": "addon_q_1"
          },
          {
            "offerId": "menuitem_addon_offer_id_2",
            "price": {
                "currencyCode": "USD",
                "units": "dollar(addon_q_2*addon_p_2)",
                "nanos": "cent(addon_q_2*addon_p_2)"
            },
            "quantity": "addon_q_2"
          }
        ]
      }
    },
    {
      "offerId": "menuitem_offer_id_2",
      "price": {
        "amount": {
          "currencyCode": "USD",
          "units": "dollar(q_2*p_2)",
          "nanos": "cent(q_2*p_2)"
        }
      },
      "quantity": "q_2"
    }
  ]
}

JSON

Este exemplo contém um item de menu com opções de item de menu, AddOnMenuItems e AddOnMenuItems aninhados

Itens do cardápio em um feed:

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

Itens de menu mapeados para um carrinho de atendimento do pedido:

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

Como processar os erros

Se você tiver problemas ao processar uma CheckoutRequestMessage, responda com uma CheckoutResponseMessage que contenha uma FoodErrorExtension em vez de uma CheckoutResponse. Use essa resposta para identificar um ou mais erros que ocorreram durante o processamento.

Há duas maneiras de lidar com os erros:

  • Erros recuperáveis: o usuário não precisa editar o carrinho para enviar o pedido. Por exemplo, se você determinar que um item na Cart tem uma mudança de preço, poderá responder com uma FoodOrderError do tipo de erro PRICE_CHANGED, além de correctedProposedOrder e paymentOptions. O Google informa o usuário sobre a mudança, mas permite que ele o envie com o correctedProposedOrder. O usuário também pode voltar e editar o carrinho. Você vai receber um novo CheckoutRequestMessage ou um SubmitOrderRequestMessage.
  • Erros irrecuperáveis: o usuário precisa editar o carrinho antes de enviar o pedido. Por exemplo, se você determinar que o restaurante está fechado, responda com uma FoodOrderError do tipo de erro CLOSED. O Google informa o usuário e gerencia a interação para atualizar para um novo restaurante. Você receberá um novo CheckoutRequestMessage para um novo carrinho.

Em geral, torne os erros no carrinho irrecuperáveis e os erros no item que possam ser recuperados. Para ver uma lista completa dos tipos de erro e os significados deles, consulte FoodOrderError.

Como processar mudanças de preço

Mudanças no preço durante a finalização da compra

Se você encontrar um problema de preço ao processar a solicitação de checkout de um cliente, faça o seguinte:

  1. Responda à CheckoutRequestMessage com uma CheckoutResponseMessage que contenha um FoodErrorExtension, conforme descrito em Como processar erros.
  2. Na resposta de erro, use correctedProposedOrder.cart para atualizar o preço com o valor correto. O Google recebe o pedido corrigido e pode emitir um novo CheckoutRequestMessage.

Após a finalização da compra, o Google exibe uma página de confirmação do pedido para o usuário final, independentemente de o ProposedOrder ter sido alterado ou não.

Se o ProposedOrder for corrigido, o Google poderá mostrar mais avisos para informar o usuário sobre as mudanças. Se o usuário concordar em fazer o pedido, não haverá mais solicitações de finalização de compra. O fluxo continua pedindo o envio, com o ProposedOrder corrigido.

No entanto, o usuário pode mudar de ideia e editar o carrinho a qualquer momento. Quando o carrinho é atualizado dessa forma, o Google envia uma nova CheckoutRequestMessage.

Alterações de preço durante o envio do pedido

Se você encontrar um problema de preço ao processar o envio de pedido (a intent actions.intent.TRANSACTION_DECISION foi acionada), não responda com um erro nem atualize o preço na resposta. Se os preços, as quantidades ou outros detalhes no SubmitOrderRequestMessage não corresponderem aos seus dados, responda com o orderState definido como REJECTED para indicar que o pedido não pode ser feito conforme solicitado.

Em seguida, se os detalhes do pedido e da forma de pagamento forem válidos, defina orderState como CREATED ou CONFIRMED. Além disso, inclua um actionOrderId para representar o ID do pedido no seu sistema. Esse ID precisa ser usado ao enviar atualizações subsequentes.

Se não for possível processar o pagamento e já tiver enviado o SubmitOrderRequestMessage, você poderá enviar um AsyncOrderUpdateRequestMessage com orderState definido como REJECTED para informar ao usuário que o pedido não será processado.

Alterações de preço após o envio do pedido

Se você determinar que um preço mudou em relação ao usado quando um cliente enviou o pedido, emita um AsyncOrderUpdateRequestMessage, conforme descrito em Como implementar atualizações assíncronas de pedidos, com o novo preço.

Para atualizar preços usando atualizações de pedidos assíncronas:

  1. Mudar o preço em lineItemUpdates[x].price. Esse valor reflete o custo total do item, incluindo complementos, e multiplicado pela quantidade. Para saber mais, consulte a descrição do campo price de LineItem.
  2. Insira uma explicação em lineItemUpdates[x].reason.
  3. Defina lineItemUpdates[x].orderState como CONFIRMED.

É possível tentar cobrar o instrumento de pagamento antes ou depois de enviar a AsyncOrderUpdateRequestMessage, a seu critério. Se a transação falhar, possivelmente porque o delta de preço está muito alto, envie uma AsyncOrderUpdateRequestMessage com as seguintes configurações em OrderUpdate para informar o Google sobre a falha:

  • Defina orderState como REJECTED.
  • Descreva a falha no campo label.

Validação da finalização de compra

Conforme discutido na Etapa 4: implementar a finalização da compra, seu endpoint de fulfillment precisa realizar a validação em cada CheckoutRequestMessage recebida e responder com uma CheckoutResponseMessage.

Confira um exemplo de CheckoutResponseMessage para uma validação bem-sucedida:

Caso de uso Como fazer a implementação
Caso de uso 1: validação bem-sucedida Retorne o CheckoutResponse. Ela precisa ter ProposedOrder e PaymentOptions. ProposedOrder inclui tributos, taxas e o preço total do carrinho.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "checkoutResponse": {
              "proposedOrder": {
                "id": "sample_proposed_order_id_1",
                "otherItems": [
                  {
                    "name":"New customer discount",
                    "price": {
                      "type":"ESTIMATE",
                      "amount": {
                        "currencyCode":"USD",
                        "units":"-5",
                        "nanos": -500000000
                      }
                    },
                    "type": "DISCOUNT"
                  },
                  {
                    "name": "Delivery fee",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "3",
                        "nanos": 500000000
                      }
                    },
                    "type": "DELIVERY"
                  },
                  {
                    "name": "Tax",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "1",
                        "nanos": 500000000
                      }
                    },
                    "type": "TAX"
                  }
                ],
                "cart": {
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Falafel Bite"
                  },
                  "lineItems": [
                    {
                      "name": "Pita Chips",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_1",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id1",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "2",
                          "nanos": 750000000
                        }
                      },
                      "subLines": [
                        {
                          "note": "Notes for this item."
                        }
                      ],
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension",
                        "options": [
                          {
                            "id": "sample_addon_offer_id_1",
                            "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1",
                            "name": "Honey Mustard",
                            "price": {
                              "currencyCode": "USD"
                            },
                            "quantity": 1
                          },
                          {
                            "id": "sample_addon_offer_id_2",
                            "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2",
                            "name": "BBQ Sauce",
                            "price": {
                              "currencyCode": "USD",
                              "nanos": 500000000
                            },
                            "quantity": 1
                          }
                        ]
                      }
                    },
                    {
                      "name": "Chicken Shwarma Wrap",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_2",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id2",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "8"
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    },
                    {
                      "name": "Greek Salad",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_3",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id3",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "9",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    },
                    {
                      "name": "Prawns Biryani",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_4",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id4",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "15",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      }
                    },
                    "location": {
                      "coordinates": {
                        "latitude": 37.788783,
                        "longitude": -122.41384
                      },
                      "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States",
                      "zipCode": "94043",
                      "city": "Mountain View",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94043",
                        "administrativeArea": "CA",
                        "locality": "Mountain View",
                        "addressLines": [
                          "1350 Charleston Road"
                        ]
                      },
                      "notes": "Gate code is #111"
                     }
                   }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    // Represents $36.73
                    "currencyCode": "USD",
                    "units": "36",
                    "nanos": 730000000
                  }
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  "availableFulfillmentOptions": [
                    {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      },
                      "expiresAt": "2017-07-17T12:30:00Z"
                    }
                  ]
                }
              },
              "paymentOptions": {
                "googleProvidedOptions": {
                  "tokenizationParameters": {
                    "tokenizationType": "PAYMENT_GATEWAY",
                    "parameters": {
                      "gateway": "stripe",
                      "stripe:publishableKey": "pk_live_stripe_client_key",
                      "stripe:version": "2017-04-06"
                    }
                  },
                  "supportedCardNetworks": [
                    "AMEX",
                    "DISCOVER",
                    "MASTERCARD",
                    "JCB",
                    "VISA"
                  ],
                  "prepaidCardDisallowed": true
                }
              }
            }
          }
        }
      ]
    }
  }
}

Validação do endereço de entrega

Seu endpoint de fulfillment precisa validar o endereço de entrega contido em cada CheckoutRequestMessage.

Se houver um problema com o endereço de entrega, como estar fora do alcance do serviço de entrega, o CheckoutResponseMessage retornado pelo fulfillment precisa conter um FoodOrderError do tipo adequado.

Caso de uso Como fazer a implementação
Caso de uso 1:a validação falhou porque o endereço de entrega está fora do intervalo ou há um problema com o endereço de entrega Retorne FoodErrorExtension com FoodOrderError do tipo de erro OUT_OF_SERVICE_AREA.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "OUT_OF_SERVICE_AREA",
                  "description": "Sorry, the restaurant cannot deliver to your address."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Validação do valor mínimo do pedido

Seu endpoint de fulfillment precisa validar o valor mínimo do pedido de cada CheckoutRequestMessage.

Se o valor mínimo do pedido não for atingido, os CheckoutResponseMessage retornados pelo fulfillment precisarão conter um FoodOrderError do tipo de erro REQUIREMENTS_NOT_MET.

Caso de uso Como fazer a implementação
Caso de uso 1:ocorreu uma falha na validação porque o valor mínimo do pedido não foi atendido Retorne FoodErrorExtension com FoodOrderError do tipo de erro REQUIREMENTS_NOT_MET.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "REQUIREMENTS_NOT_MET",
                  "description": "The cart subtotal must be over $20."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Validação da janela de pedidos

Seu endpoint de fulfillment precisa validar todos os fatores que podem afetar a janela de ordenação de cada CheckoutRequestMessage.

Por exemplo, se o restaurante estiver fechado ou não receber mais pedidos no momento, o CheckoutResponseMessage retornado pelo atendimento do pedido precisará conter um FoodOrderError do tipo de erro CLOSED ou NO_CAPACITY, respectivamente.

Caso de uso Como fazer a implementação
Caso de uso 1:a validação falhou porque o restaurante está fechado ou não é mais compatível Retorne FoodErrorExtension com FoodOrderError do tipo de erro CLOSED.
Caso de uso 2:a validação falhou porque o restaurante está ocupado e não aceita pedidos no momento Retorne FoodErrorExtension com FoodOrderError do tipo de erro NO_CAPACITY.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "CLOSED",
                  "description": "The restaurant is closed."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "NO_CAPACITY",
                  "description": "Sorry, the restaurant is busy at the moment."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Validação de itens do carrinho

Seu endpoint de fulfillment precisa validar o preço e a disponibilidade de cada item de carrinho contido em um CheckoutRequestMessage.

Se a disponibilidade ou os preços tiverem mudado, o CheckoutResponseMessage retornado pelo fulfillment deve conter um FoodOrderError do tipo de erro AVAILABILITY_CHANGED ou PRICE_CHANGED, respectivamente.

Caso de uso Como fazer a implementação
Caso de uso 1:a validação falhou porque alguns itens de menu e/ou as personalizações deles não são válidos ou estão esgotados Retorne FoodErrorExtension com correctedProposedOrder, PaymentOptions e FoodOrderError do tipo de erro AVAILABILITY_CHANGED. Os itens inválidos precisam ser removidos do CorrectedProposedOrder.
Caso de uso 2:a validação falhou porque alguns itens de menu e/ou as personalizações deles não são válidos ou estão esgotados. O carrinho corrigido não atende mais ao requisito de valor mínimo do pedido. Retorne FoodErrorExtension com FoodOrderError dos tipos de erro AVAILABILITY_CHANGED e REQUIREMENTS_NOT_MET.
Caso de uso 3:a validação falhou porque alguns itens de menu e/ou preços de personalização mudaram Retorne FoodErrorExtension com correctedProposedOrder, PaymentOptions e FoodOrderError do tipo de erro PRICE_CHANGED. Os preços desatualizados precisam ser atualizados em CorrectedProposedOrder.
Caso de uso 4:a validação falhou porque alguns itens de menu e/ou preços de personalização mudaram. O carrinho corrigido não atende mais ao requisito de valor mínimo do pedido Retorne FoodErrorExtension com FoodOrderError dos tipos de erro PRICE_CHANGED e REQUIREMENTS_NOT_MET.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "AVAILABILITY_CHANGED",
                  "id": "sample_item_offer_id_1",
                  "description": "The item is no longer available."
                },
                {
                  "error": "AVAILABILITY_CHANGED",
                  "id": "sample_item_offer_id_2",
                  "description": "The item is no longer available."
                }
              ],
              "correctedProposedOrder": {
                "id": "sample_corrected_proposed_order_id_1",
                "otherItems": [
                  {
                    "name":"New customer discount",
                    "price": {
                      "type":"ESTIMATE",
                      "amount": {
                        "currencyCode":"USD",
                        "units":"-5",
                        "nanos": -500000000
                      }
                    },
                    "type": "DISCOUNT"
                  },
                  {
                    "name": "Delivery fee",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "3",
                        "nanos": 500000000
                      }
                    },
                    "type": "DELIVERY"
                  },
                  {
                    "name": "Tax",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "1",
                        "nanos": 500000000
                      }
                    },
                    "type": "TAX"
                  }
                ],
                "cart": {
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Falafel Bite"
                  },
                  "lineItems": [
                    {
                      "name": "Greek Salad",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_3",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id3",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "9",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    },
                    {
                      "name": "Prawns Biryani",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_4",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id4",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "15",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      }
                    },
                    "location": {
                      "coordinates": {
                        "latitude": 37.788783,
                        "longitude": -122.41384
                      },
                      "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States",
                      "zipCode": "94043",
                      "city": "Mountain View",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94043",
                        "administrativeArea": "CA",
                        "locality": "Mountain View",
                        "addressLines": [
                          "1350 Charleston Road"
                        ]
                      },
                      "notes": "Gate code is #111"
                     }
                   }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    "currencyCode": "USD",
                    "units": "36",
                    "nanos": 730000000
                  }
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  "availableFulfillmentOptions": [
                    {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      },
                      "expiresAt": "2017-07-17T12:30:00Z"
                    }
                  ]
                }
              },
              "paymentOptions": {
                "googleProvidedOptions": {
                  "tokenizationParameters": {
                    "tokenizationType": "PAYMENT_GATEWAY",
                    "parameters": {
                      "gateway": "stripe",
                      "stripe:publishableKey": "pk_live_stripe_client_key",
                      "stripe:version": "2017-04-06"
                    }
                  },
                  "supportedCardNetworks": [
                    "AMEX",
                    "DISCOVER",
                    "MASTERCARD",
                    "JCB",
                    "VISA"
                  ],
                  "prepaidCardDisallowed": true
                }
              }
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "REQUIREMENTS_NOT_MET",
                  "description": "The cart subtotal must be over $20."
                },
                {
                  "error": "AVAILABILITY_CHANGED",
                  "id": "cart_lineitem_id"
                  "description": "cart_lineitem_id is no longer available."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "PRICE_CHANGED",
                  "id": "sample_item_offer_id_1",
                  "description": "The price has changed.",
                  "updatedPrice": {
                    "currencyCode": "USD",
                    "units": "2",
                    "nanos": 750000000
                  }
                },
                {
                  "error": "PRICE_CHANGED",
                  "id": "sample_item_offer_id_2",
                  "description": "The price has changed.",
                  "updatedPrice": {
                    "currencyCode": "USD",
                    "units": "8"
                  }
                }
              ],
              "correctedProposedOrder": {
                "id": "sample_corrected_proposed_order_id_1",
                "otherItems": [
                  {
                    "name":"New customer discount",
                    "price": {
                      "type":"ESTIMATE",
                      "amount": {
                        "currencyCode":"USD",
                        "units":"-5",
                        "nanos": -500000000
                      }
                    },
                    "type": "DISCOUNT"
                  },
                  {
                    "name": "Delivery fee",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "3",
                        "nanos": 500000000
                      }
                    },
                    "type": "DELIVERY"
                  },
                  {
                    "name": "Tax",
                    "price": {
                      "type": "ESTIMATE",
                      "amount": {
                        "currencyCode": "USD",
                        "units": "1",
                        "nanos": 500000000
                      }
                    },
                    "type": "TAX"
                  }
                ],
                "cart": {
                  "merchant": {
                    "id": "https://www.exampleprovider.com/merchant/id1",
                    "name": "Falafel Bite"
                  },
                  "lineItems": [
                    {
                      "name": "Pita Chips",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_1",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id1",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "2",
                          "nanos": 750000000
                        }
                      },
                      "subLines": [
                        {
                          "note": "Notes for this item."
                        }
                      ],
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension",
                        "options": [
                          {
                            "id": "sample_addon_offer_id_1",
                            "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1",
                            "name": "Honey Mustard",
                            "price": {
                              "currencyCode": "USD"
                            },
                            "quantity": 1
                          },
                          {
                            "id": "sample_addon_offer_id_2",
                            "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2",
                            "name": "BBQ Sauce",
                            "price": {
                              "currencyCode": "USD",
                              "nanos": 500000000
                            },
                            "quantity": 1
                          }
                        ]
                      }
                    },
                    {
                      "name": "Chicken Shwarma Wrap",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_2",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id2",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "8"
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    },
                    {
                      "name": "Greek Salad",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_3",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id3",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "9",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    },
                    {
                      "name": "Prawns Biryani",
                      "type": "REGULAR",
                      "id": "sample_item_offer_id_4",
                      "offerId": "https://www.exampleprovider.com/menu/item/offer/id4",
                      "quantity": 1,
                      "price": {
                        "type": "ESTIMATE",
                        "amount": {
                          "currencyCode": "USD",
                          "units": "15",
                          "nanos": 990000000
                        }
                      },
                      "extension": {
                        "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
                      }
                    }
                  ],
                  "extension": {
                    "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
                    "fulfillmentPreference": {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      }
                    },
                    "location": {
                      "coordinates": {
                        "latitude": 37.788783,
                        "longitude": -122.41384
                      },
                      "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States",
                      "zipCode": "94043",
                      "city": "Mountain View",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94043",
                        "administrativeArea": "CA",
                        "locality": "Mountain View",
                        "addressLines": [
                          "1350 Charleston Road"
                        ]
                      },
                      "notes": "Gate code is #111"
                     }
                   }
                },
                "totalPrice": {
                  "type": "ESTIMATE",
                  "amount": {
                    "currencyCode": "USD",
                    "units": "36",
                    "nanos": 730000000
                  }
                },
                "extension": {
                  "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
                  "availableFulfillmentOptions": [
                    {
                      "fulfillmentInfo": {
                        "delivery": {
                          "deliveryTimeIso8601": "P90M"
                        }
                      },
                      "expiresAt": "2017-07-17T12:30:00Z"
                    }
                  ]
                }
              },
              "paymentOptions": {
                "googleProvidedOptions": {
                  "tokenizationParameters": {
                    "tokenizationType": "PAYMENT_GATEWAY",
                    "parameters": {
                      "gateway": "stripe",
                      "stripe:publishableKey": "pk_live_stripe_client_key",
                      "stripe:version": "2017-04-06"
                    }
                  },
                  "supportedCardNetworks": [
                    "AMEX",
                    "DISCOVER",
                    "MASTERCARD",
                    "JCB",
                    "VISA"
                  ],
                  "prepaidCardDisallowed": true
                }
              }
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "REQUIREMENTS_NOT_MET",
                  "description": "The cart subtotal must be over $20."
                },
                {
                  "error": "PRICE_CHANGED",
                  "id": "cart_lineitem_id"
                  "description": "cart_lineitem_id price has been updated."
                  "updatedPrice": {
                    "currencyCode": "USD",
                    "units": "2",
                    "nanos": 750000000
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Enviar validação do pedido

Conforme discutido na Etapa 7: implementar o envio de pedido, seu endpoint de fulfillment precisa realizar a validação em cada SubmitOrderRequestMessage recebida e responder com uma SubmitOrderResponseMessage.

Confira um exemplo de SubmitOrderResponseMessage para uma validação bem-sucedida:

Caso de uso Como fazer a implementação
Caso de uso 1:o pedido foi criado Um SubmitOrderResponseMessage com estado de pedido CREATED. Ela precisa ter actionOrderId, userVisibleId, orderManagementActions e estimatedFulfillmentTime.
Caso de uso 2:o pedido foi recusado devido a problemas no pagamento Um SubmitOrderResponseMessage com estado de pedido REJECTED. Ele precisa ter actionOrderId, userVisibleId, orderManagementActions e rejectionInfo do tipo PAYMENT_DECLINED.
Caso de uso 3:o pedido é rejeitado porque o usuário é sinalizado como banido Um SubmitOrderResponseMessage com status do pedido REJECTED. Ele precisa ter actionOrderId, userVisibleId, orderManagementActions e rejectionInfo do tipo INELIGIBLE.
Caso de uso 4:o pedido é rejeitado porque as informações do usuário estão incompletas ou inválidas Um SubmitOrderResponseMessage com estado de pedido REJECTED. Ele precisa ter actionOrderId, userVisibleId, orderManagementActions e rejectionInfo do tipo INELIGIBLE.
Caso de uso 5: o pedido foi recusado por motivo desconhecido Um SubmitOrderResponseMessage com estado de pedido REJECTED. Ele precisa ter actionOrderId, userVisibleId, orderManagementActions e rejectionInfo do tipo UNKNOWN.

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "CREATED",
                "label": "Order received"
              },
              "updateTime": "2017-05-10T02:30:00.000Z",
              "orderManagementActions": [
                {
                  "type": "CUSTOMER_SERVICE",
                  "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                      "url": "mailto:support@example.com"
                    }
                  }
                },
                {
                  "type": "EMAIL",
                  "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                      "url": "mailto:person@example.com"
                    }
                  }
                },
                {
                  "type": "CALL",
                  "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                      "url": "tel:+16505554679"
                    }
                  }
                },
                {
                  "type": "VIEW_DETAILS",
                  "button": {
                    "title": "View order",
                    "openUrlAction": {
                      "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Order rejected"
              },
              "updateTime": "2017-05-10T02:30:00.000Z",
              "rejectionInfo": {
                 "type": "PAYMENT_DECLINED",
                 "reason": "Insufficient funds"
              },
              "orderManagementActions": [
                {
                  "type": "CUSTOMER_SERVICE",
                  "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                      "url": "mailto:support@example.com"
                    }
                  }
                },
                {
                  "type": "EMAIL",
                  "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                      "url": "mailto:person@example.com"
                    }
                  }
                },
                {
                  "type": "CALL",
                  "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                      "url": "tel:+16505554679"
                    }
                  }
                },
                {
                  "type": "VIEW_DETAILS",
                  "button": {
                    "title": "View order",
                    "openUrlAction": {
                      "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Order rejected"
              },
              "updateTime": "2017-05-10T02:30:00.000Z",
              "rejectionInfo": {
                 "type": "INELIGIBLE",
                 "reason": "Sorry, we are not able to take orders from this user"
              },
              "orderManagementActions": [
                {
                  "type": "CUSTOMER_SERVICE",
                  "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                      "url": "mailto:support@example.com"
                    }
                  }
                },
                {
                  "type": "EMAIL",
                  "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                      "url": "mailto:person@example.com"
                    }
                  }
                },
                {
                  "type": "CALL",
                  "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                      "url": "tel:+16505554679"
                    }
                  }
                },
                {
                  "type": "VIEW_DETAILS",
                  "button": {
                    "title": "View order",
                    "openUrlAction": {
                      "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Order rejected"
              },
              "updateTime": "2017-05-10T02:30:00.000Z",
              "rejectionInfo": {
                 "type": "INELIGIBLE",
                 "reason": "Sorry, the phone number must not be blank"
              },
              "orderManagementActions": [
                {
                  "type": "CUSTOMER_SERVICE",
                  "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                      "url": "mailto:support@example.com"
                    }
                  }
                },
                {
                  "type": "EMAIL",
                  "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                      "url": "mailto:person@example.com"
                    }
                  }
                },
                {
                  "type": "CALL",
                  "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                      "url": "tel:+16505554679"
                    }
                  }
                },
                {
                  "type": "VIEW_DETAILS",
                  "button": {
                    "title": "View order",
                    "openUrlAction": {
                      "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

JSON

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "orderUpdate": {
              "actionOrderId": "sample_action_order_id",
              "orderState": {
                "state": "REJECTED",
                "label": "Order rejected"
              },
              "updateTime": "2017-05-10T02:30:00.000Z",
              "rejectionInfo": {
                 "type": "UNKNOWN",
                 "reason": "Sorry, there is something wrong with this order."
              },
              "orderManagementActions": [
                {
                  "type": "CUSTOMER_SERVICE",
                  "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                      "url": "mailto:support@example.com"
                    }
                  }
                },
                {
                  "type": "EMAIL",
                  "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                      "url": "mailto:person@example.com"
                    }
                  }
                },
                {
                  "type": "CALL",
                  "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                      "url": "tel:+16505554679"
                    }
                  }
                },
                {
                  "type": "VIEW_DETAILS",
                  "button": {
                    "title": "View order",
                    "openUrlAction": {
                      "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}