Atualização de pedidos assíncrona

Depois que um cliente envia um pedido de comida, você pode enviar uma mensagem de atualização de pedido para o serviço de pedidos de ponta a ponta para nos notificar sobre a alteração.

Confira alguns motivos comuns para enviar atualizações de pedidos:

  • O tempo estimado de atendimento do pedido fica disponível ou muda.
  • O estado de um pedido muda.
  • Não é mais possível atender o pedido.
  • O preço de um item do menu incluído no pedido mudou.
  • O cliente tem uma nova maneira de gerenciar o pedido, como um número de telefone de restaurante ou de suporte ao cliente.
  • O recibo do pedido fica disponível.

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

Estados de transição do pedido

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

Transições de estado do pedido

Quando um cliente envia um pedido pela primeira vez, ele começa com um estado de CREATED, CONFIRMED ou REJECTED. Você pode enviar uma mensagem de atualização de 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 um pedido por um agregador de entregas. O agregador de entregas recebe a entrega do Google e 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, será REJECTED.

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

Se você permitir que os clientes cancelem pedidos, use o estado CANCELLED. Um pedido pode ser cancelado enquanto estiver no estado CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP ou IN_TRANSIT. O serviço de atendimento completo de pedidos precisa emitir reembolsos de acordo com sua política de cancelamento e o estado dos pagamentos no momento do cancelamento.

O serviço de pedidos 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.

Como informar um tempo de atendimento estimado

Você pode informar aos usuários um intervalo de tempo estimado para quando o pedido estará pronto para retirada (ou entrega). Use o campo estimatedFulfillmentTimeIso8601 de FoodOrderUpdateExtension para fornecer um intervalo de tempo estimado para quando o pedido de um cliente estará pronto para retirada ou entregue.

Envie o estimatedFulfillmentTimeIso8601 nos seguintes horários:

  • Quando o tempo estimado estiver disponível, idealmente no estado CREATED ou CONFIRMED do pedido.
  • Quando o tempo estimado muda, como atualizar o tempo estimado para ser mais preciso quando o pedido é IN_TRANSIT.

Para gerenciar as expectativas dos usuários de maneira eficaz, seja conservador nas estimativas e forneça um período de datas e horários em vez de uma data e um horário fixos. É preciso considerar variações, como condições de trânsito, sempre que possível. Por exemplo, é possível enviar uma estimativa de 12h45 (limite inferior) a 13h15 (limite superior) para um pedido em que o tempo de entrega estimado é 13h.

Como realizar ações de gerenciamento de pedidos

Ao enviar uma atualização de pedido, você pode fornecer recursos aos clientes que os ajudam 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á realizando o pedido para acompanhar o progresso, fazer alterações ou cancelar o pedido.

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

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

  • CUSTOMER_SERVICE: oferecer 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: oferecer aos clientes uma ação para enviar um e-mail para o endereço fornecido.
  • CALL: oferecer aos clientes uma ação para ligar para o número de telefone fornecido.
  • VIEW_DETAIL: ofereça aos clientes uma ação para conferir 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 seu atendimento ao cliente. Quando o estado do pedido é atualizado para IN_TRANSIT, a ação CUSTOMER_SERVICE pode apontar para o número de telefone do restaurante de atendimento.

Como enviar atualizações de pedidos

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

Diagrama de atualização de pedidos

Como definir a mensagem de atualização do pedido

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

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

CONFIRMADO

Este exemplo mostra uma 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"
      }
    }
  }
}
    

REJEITADO

Este exemplo mostra uma solicitação de atualização de pedido que notifica o usuário de que o pedido foi rejeitado com um motivo de 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."
        }
      ]
      }
    }
  }
}
    

CANCELADO

Este exemplo mostra uma 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

Este exemplo mostra um pedido 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

Este exemplo mostra um pedido 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

Este exemplo mostra uma 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"
      }
    }
  }
}
  

FULFILLED

Este exemplo mostra um pedido de atualização de exemplo que notifica o usuário de que o pedido foi 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 pedidos em diferentes casos de uso, leia Implementar atualizações avançadas de pedidos.

Gerar 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 é do seu serviço da Web de pedidos de ponta a ponta.

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

  1. Para gerar um token de autorização, siga estas etapas:
    1. Use a Biblioteca Auth do Google 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 para o 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 a seguir demonstram como implementar atualizações de pedidos:

Node.js

Este 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 estar com formato incorreto, o Google vai retornar um erro.