Atualização do pedido assíncrono

Depois que um cliente enviar um pedido de comida, você poderá enviar uma mensagem de atualização do pedido ao serviço de ponta a ponta para nos avisar sobre a mudança.

Veja alguns motivos comuns para o envio de atualizações de pedidos:

  • O tempo de atendimento estimado do pedido fica disponível ou muda.
  • O estado de um pedido muda.
  • O pedido não pode mais ser atendido.
  • O preço de um item do cardápio incluído no pedido mudou.
  • O cliente tem uma nova maneira de gerenciar o pedido, como um suporte ao cliente ou número de telefone de um restaurante.
  • O recibo do pedido será disponibilizado.

As próximas seções fornecem detalhes sobre como lidar com esses diferentes cenários usando atualizações de pedidos.

Como fazer a transição de estados de pedidos

Um pedido tem seis estados possíveis. Esses estados e as possíveis transições estão descritos no diagrama a seguir:

Ordenar transições de estado

Quando um cliente envia um pedido pela primeira vez, ele começa com um estado CREATED, CONFIRMED ou REJECTED. É possível enviar uma mensagem de atualização do pedido para atualizar o estado de um pedido, desde que a transição de estado seja válida. O estado CREATED é usado quando a plataforma do parceiro não pode confirmar ou rejeitar o pedido imediatamente. Um exemplo de caso de uso é quando um cliente faz o pedido usando um agregador de entrega. O agregador de entrega recebe a entrega do Google, e o agregador envia as informações ao restaurante. Depois que o restaurante receber e confirmar a disponibilidade do pedido, o estado poderá ser CONFIRMED, caso contrário, REJECTED.

Um pedido no estado CONFIRMED depois se move para o estado IN_PREPARATION. Dependendo de o pedido ser para retirada ou entrega, use o estado READY_FOR_PICKUP ou IN_TRANSIT. Quando a comida é entregue ou retirada, o pedido é definido no estado FULFILLED.

Se você permitir que os clientes cancelem pedidos, use o estado CANCELLED. É possível cancelar um pedido nos estados CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP ou IN_TRANSIT. Seu serviço de Pedidos de ponta a ponta emitirá reembolsos, dependendo da política de cancelamento e do estado dos pagamentos no momento do cancelamento.

Seu serviço de ordenação de ponta a ponta não precisa oferecer suporte a todos os estados e transições disponíveis. No entanto, o estado final do pedido precisa ser FULFILLED, REJECTED ou CANCELLED.

Fornecer um tempo de atendimento estimado

É possível fornecer aos usuários um período estimado para quando o pedido estará pronto para retirada (ou entrega). Use o campo estimatedFulfillmentTimeIso8601 de FoodOrderUpdateExtension para fornecer um período estimado para o momento em que o pedido de um cliente estará pronto para retirada ou entrega.

Envie o estimatedFulfillmentTimeIso8601 nos seguintes horários:

  • Quando o tempo estimado fica disponível, de preferência na ordem CREATED ou CONFIRMED.
  • Quando o tempo estimado muda, como atualizar o tempo estimado para ser mais preciso quando o pedido for IN_TRANSIT.

Para gerenciar as expectativas do usuário de maneira eficaz, seja conservador em suas estimativas e forneça um intervalo de data e hora em vez de uma data e hora fixas. Sempre que possível, considere as variações, como condições de trânsito. Por exemplo, é possível enviar uma estimativa de 12h45 (limite inferior) às 13h15 (limite superior) de um pedido em que o tempo de entrega estimado é 13h.

Como fornecer ações de gerenciamento de pedidos

Ao enviar uma atualização do pedido, você pode fornecer recursos aos clientes para ajudá-los a gerenciar o pedido na forma de um OrderManagementAction. Depois que um cliente faz um pedido, ele pode precisar entrar em contato com você ou com o restaurante que está atendendo o pedido para acompanhar o andamento, fazer alterações ou cancelar o pedido.

Um OrderManagementAction permite que os clientes enviem e-mails, liguem ou vinculem um URL diretamente do dispositivo. Use as mesmas informações em OrderManagementAction e na confirmação do pedido por e-mail enviada ao usuário.

As ações de gerenciamento de pedidos incluem os seguintes tipos:

  • CUSTOMER_SERVICE: ofereça aos clientes uma ação para entrar em contato com o atendimento ao cliente. Esse tipo de ação de gerenciamento é obrigatório para atualizações de pedidos.
  • EMAIL: ofereça aos clientes uma ação para enviar um e-mail ao endereço fornecido.
  • CALL: ofereça aos clientes uma ação para ligar para o número de telefone fornecido.
  • VIEW_DETAIL: ofereça aos clientes uma ação para ver os detalhes do pedido.

Cada atualização de pedido precisa conter pelo menos uma ação de gerenciamento de pedidos. No entanto, as ações de gerenciamento de pedidos fornecidas podem variar de acordo com o estado do pedido. Por exemplo, quando um pedido está no estado CONFIRMED, a ação CUSTOMER_SERVICE pode apontar para o número de telefone do atendimento ao cliente. Quando esse estado do pedido é atualizado para IN_TRANSIT, a ação CUSTOMER_SERVICE pode apontar para o número de telefone do restaurante de atendimento do pedido.

Enviando atualizações de pedidos

Você usa o tipo de mensagem AsyncOrderUpdateRequestMessage para enviar uma atualização de pedido para o serviço de ponta a ponta. O Google responde com AsyncOrderUpdateResponseMessage. Por exemplo, se você quiser informar um cliente que o pedido dele era válido e aceito, envie um AsyncOrderUpdateRequestMessage para mudar o estado do pedido para CONFIRMED com o rótulo Accepted by restaurant.

Diagrama de atualização do pedido

Configuração da mensagem de atualização do pedido

Ao enviar um AsyncOrderUpdateRequestMessage ao Google, é necessário incluir informações sobre o estado do pedido usando o campo OrderUpdate.

Os exemplos a seguir mostram um exemplo de AsyncOrderUpdateRequestMessage para cada estado de pedido:

CONFIRMADO

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que o pedido foi confirmado com um recibo e um tempo de entrega estimado.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CONFIRMED",
        "label": "Provider confirmed"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "2017-07-17T13:00:00Z/2017-07-17T13:30:00Z"
      }
    }
  }
}
    

REJEITADA

Neste exemplo, mostramos um exemplo de solicitação de atualização do pedido que notifica o usuário de que o pedido foi rejeitado, com um motivo para a rejeição.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "REJECTED",
        "label": "Order rejected"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "rejectionInfo": {
        "type": "UNKNOWN",
        "reason": "Sorry, the restaurant cannot take your order right now."
      },
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
      "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
      "foodOrderErrors": [
        {
        "error": "NO_CAPACITY",
        "description": "Sorry, the restaurant cannot take your order right now."
        }
      ]
      }
    }
  }
}
    

CANCELADA

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que o pedido foi cancelado com um motivo de cancelamento.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CANCELLED",
        "label": "Order cancelled"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "cancellationInfo": {
        "reason": "Customer requested"
      },
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

IN_PREPARATION

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que a comida está sendo preparada.

{
  "isInSandbox":true,
  "customPushMessage":{
    "orderUpdate":{
      "actionOrderId":"sample_action_order_id",
      "orderState":{
        "state":"IN_PREPARATION",
        "label":"Order is being prepared"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime":"2018-04-15T11:30:00Z",
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension":{
        "@type":"type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601":"PT20M"
      }
    }
  }
}
    

READY_FOR_PICKUP

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que a comida está pronta para retirada.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "READY_FOR_PICKUP",
        "label": "Order is ready for pickup"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2018-04-15T12:00:00Z",
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
    

IN_TRANSIT

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que o pedido está em trânsito com um tempo de entrega estimado.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "IN_TRANSIT",
        "label": "Order is on the way"
      },
      "inTransitInfo": {
        "updatedTime": "2017-07-17T12:00:00Z"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
  

ATUALIZADO

Neste exemplo, mostramos um exemplo de solicitação de atualização de pedido que notifica o usuário de que o pedido é retirado ou entregue:

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
      "state": "FULFILLED",
      "label": "Order delivered"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "fulfillmentInfo": {
        "deliveryTime": "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_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

Para mais exemplos de solicitações de atualização de pedido em diferentes casos de uso, leia Implementar atualizações avançadas de pedidos.

Gerar o token de autorização e enviar a mensagem

As atualizações de pedidos exigem um token de autorização para que o serviço de Pedidos de ponta a ponta possa verificar se a mensagem vem do seu serviço da Web de Pedidos de ponta a ponta.

Para implementar atualizações de ordem no projeto, siga estas etapas:

  1. Para gerar um token de autorização, siga estas etapas:
    1. Use a biblioteca do Google Auth para ler as credenciais do arquivo da conta de serviço.
    2. Solicite o token usando o seguinte escopo da API: https://www.googleapis.com/auth/actions.fulfillment.conversation
  2. Use esse token para enviar uma solicitação POST HTTP autenticada ao seguinte endpoint: https://actions.googleapis.com/v2/conversations:send
  3. Defina o cabeçalho Content-Type como application/json como parte da solicitação.

Os exemplos abaixo demonstram como implementar atualizações de pedidos:

Node.js

Esse código usa a biblioteca de autenticação do Google para Node.js.

const {auth} = require('google-auth-library')
const request = require('request');
// The service account client secret file downloaded from the Google Cloud Console
const serviceAccountJson = require('./service-account.json')
// order-update.json is a file that contains the payload
const jsonBody = require('./order-update.json')

/**
 * Get the authorization token using a service account.
 */
async function getAuthToken() {
  let client = auth.fromJSON(serviceAccountJson)
  client.scopes = ['https://www.googleapis.com/auth/actions.fulfillment.conversation']
  const tokens = await client.authorize()
  return tokens.access_token;
}

/**
 * Send an order update request
 */
async function sendOrderUpdate() {
  const token = await getAuthToken()
  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    url: 'https://actions.googleapis.com/v2/conversations:send',
    body: jsonBody,
    json: true
  },
  (err, res, body) => {
    if (err) { return console.log(err); }
    console.log(`Response: ${JSON.stringify(res)}`)
  })
}
    

Python

Esse código usa a biblioteca de autenticação do Google para Python.

from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession
import json

# service-account.json is the service account client secret file downloaded from the
# Google Cloud Console
credentials = service_account.Credentials.from_service_account_file(
    'service-account.json')

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/actions.fulfillment.conversation'])

authed_session = AuthorizedSession(scoped_credentials)

# order-update.json is a file that contains the payload
json_payload=json.load(open('order-update.json'))

response = authed_session.post(
    'https://actions.googleapis.com/v2/conversations:send',
    json=json_payload)
    

Java

Esse código usa a biblioteca de autenticação do Google para Java.

/**
 * Get the authorization token using a service account.
 */
private static String getAuthToken() {
  InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json");
  ServiceAccountCredentials.Builder credentialsSimpleBuilder =
      ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder();
  credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/actions.fulfillment.conversation"));
  AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken();
  return accessToken.getTokenValue();
}

/**
 * Send an order update request
 */
public void sendOrderUpdate() {
  String authToken = getAuthToken();
  // Execute POST request
  executePostRequest("https://actions.googleapis.com/v2/conversations:send",
      authToken, "update_order_example.json",);
}
    

Para atualizações de pedidos bem-sucedidas sem erros, o Google retorna uma resposta HTTP 200 com um payload vazio. Se houver um problema, como a atualização malformada, o Google retornará um erro.