Actualización del pedido asíncrono

Después de que un cliente envía un pedido de comida, puedes enviar un mensaje de actualización del pedido al servicio de Pedidos de extremo a extremo para notificarnos sobre el cambio.

A continuación, encontrarás algunos motivos comunes por los que deberías enviar actualizaciones de pedidos:

  • El tiempo de entrega estimado para el pedido estará disponible o cambiará.
  • El estado de un pedido cambia.
  • Ya no se puede completar el pedido.
  • Cambió el precio de un elemento de menú incluido en el pedido.
  • El cliente tiene una forma nueva de administrar su pedido, como la asistencia al cliente o el número de teléfono del restaurante.
  • El recibo del pedido estará disponible.

En las siguientes secciones, se proporcionan detalles sobre cómo abordar estas diferentes situaciones mediante las actualizaciones de pedidos.

Estados de pedido en transición

Un pedido tiene seis estados posibles. En el siguiente diagrama, se describen estos estados y sus posibles transiciones:

Transiciones de estados de orden

Cuando un cliente envía un pedido por primera vez, este comienza con un estado CREATED, CONFIRMED o REJECTED. Puedes enviar un mensaje de actualización de pedido para actualizar el estado de un pedido, siempre que la transición de estado sea válida. El estado CREATED se usa cuando la plataforma del socio no puede confirmar o rechazar el pedido de inmediato. Un caso de uso de ejemplo es cuando un cliente realiza un pedido a través de un agregador de entrega. El agregador de entregas recibe la entrega de parte de Google, y el agregador envía la información al restaurante. Una vez que el restaurante recibió y confirmó la disponibilidad del pedido, el estado ahora puede ser CONFIRMED; de lo contrario, el estado puede ser REJECTED.

Un pedido en el estado CONFIRMED pasa al estado IN_PREPARATION. Según si el pedido es para retiro o entrega, usa el estado READY_FOR_PICKUP o IN_TRANSIT. Cuando se entrega o se recoge la comida, el pedido se establece en el estado FULFILLED.

Si permites que los clientes cancelen pedidos, puedes usar el estado CANCELLED. Los pedidos se pueden cancelar en el estado CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP o IN_TRANSIT. El servicio de pedidos de extremo a extremo debe emitir reembolsos según la política de cancelación y el estado de los pagos al momento de la cancelación.

El servicio de pedidos de extremo a extremo no tiene que admitir todos los estados y transiciones disponibles. Sin embargo, el estado final del pedido debe ser FULFILLED, REJECTED o CANCELLED.

Cómo proporcionar un tiempo de entrega estimado

Puedes proporcionarles a los usuarios un intervalo de tiempo estimado en el que su pedido estará listo para retirar (o entregarse). Usa el campo estimatedFulfillmentTimeIso8601 de FoodOrderUpdateExtension a fin de proporcionar un intervalo de tiempo estimado en el que el pedido de un cliente estará listo para ser recogido o entregado.

Envía el estimatedFulfillmentTimeIso8601 en los siguientes horarios:

  • Cuando el tiempo estimado esté disponible, idealmente en el estado de orden CREATED o CONFIRMED
  • Cuando cambia la hora estimada, como actualizar el tiempo estimado para que sea más preciso cuando el pedido es IN_TRANSIT.

Para administrar las expectativas de los usuarios de manera eficaz, sé conservador en las estimaciones y proporciona un intervalo de fecha y hora en lugar de una fecha y hora fijas. Siempre que sea posible, debes tener en cuenta variaciones como las condiciones de tráfico. Por ejemplo, puedes enviar una estimación de 12:45 p.m. (límite inferior) a 1:15 p.m. (límite superior) para un pedido cuya hora de entrega estimada es 1:00 p.m.

Cómo proporcionar acciones de administración de pedidos

Cuando envías una actualización de pedido, puedes proporcionar recursos a los clientes para ayudarlos a administrar su pedido en forma de OrderManagementAction. Después de que un cliente realiza un pedido, es posible que deba comunicarse contigo o con el restaurante que realiza el pedido para hacer un seguimiento del progreso, realizar cambios o cancelarlo.

Un OrderManagementAction les permite a los clientes enviar correos electrónicos, llamar o vincular a una URL directamente desde su dispositivo. Usa la misma información en OrderManagementAction que en la confirmación de pedido por correo electrónico que le envías al usuario.

Las acciones de administración de pedidos incluyen los siguientes tipos:

  • CUSTOMER_SERVICE: Brinda a los clientes una acción para comunicarse con el equipo de atención al cliente. Este tipo de acción de administración es obligatorio para las actualizaciones de pedidos.
  • EMAIL: Brinda a los clientes una acción para enviar un correo electrónico a la dirección proporcionada.
  • CALL: Brinda a los clientes una acción para llamar al número de teléfono proporcionado.
  • VIEW_DETAIL: Brinda a los clientes una acción para ver los detalles de su pedido.

Cada actualización de pedido debe contener al menos una acción de administración de pedidos. Sin embargo, las acciones de administración de pedidos proporcionadas pueden variar según el estado del pedido. Por ejemplo, cuando un pedido tiene el estado CONFIRMED, la acción CUSTOMER_SERVICE puede apuntar al número de teléfono de tu servicio de atención al cliente. Cuando el estado de ese pedido se actualiza a IN_TRANSIT, la acción CUSTOMER_SERVICE puede apuntar al número de teléfono del restaurante de entregas.

Enviando actualizaciones de pedidos

Usa el tipo de mensaje AsyncOrderUpdateRequestMessage para enviar una actualización de pedido al servicio Ordering End-to-End. Google responde con una AsyncOrderUpdateResponseMessage. Por ejemplo, si quieres informar a un cliente que su pedido era válido y se aceptó, puedes enviar un AsyncOrderUpdateRequestMessage para cambiar el estado del pedido a CONFIRMED con la etiqueta Accepted by restaurant.

Diagrama de actualización del pedido

Cómo configurar el mensaje de actualización del pedido

Cuando envías un AsyncOrderUpdateRequestMessage a Google, debes incluir información sobre el estado del pedido mediante el campo OrderUpdate.

En los siguientes ejemplos, se muestra un AsyncOrderUpdateRequestMessage de muestra para cada estado de pedido:

CONFIRMED

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que el pedido se confirma con un recibo y un tiempo estimado de entrega.

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

RECHAZADA

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que se rechaza el pedido con un motivo de rechazo.

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

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que el pedido se canceló con un motivo de cancelación.

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

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que la comida se está preparando actualmente.

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

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que la comida está lista para retirarla.

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

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que el pedido está en tránsito con un tiempo 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"
      }
    }
  }
}
  

CUMPLIDA

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que se retiró o se entregó el pedido:

{
  "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 ver más ejemplos de solicitudes de actualización de pedidos en diferentes casos de uso, consulta Cómo implementar actualizaciones de pedidos avanzadas.

Generar un token de autorización y enviar el mensaje

Las actualizaciones de pedido requieren un token de autorización para que el servicio de Ordering End-to-End pueda verificar que el mensaje sea del servicio web de Ordering End-to-End.

A fin de implementar actualizaciones de pedidos para tu proyecto, sigue estos pasos:

  1. Genera un token de autorización con los siguientes pasos:
    1. Usa la biblioteca de Google Auth para leer las credenciales del archivo de tu cuenta de servicio.
    2. Token de solicitud con el siguiente alcance de la API: https://www.googleapis.com/auth/actions.fulfillment.conversation
  2. Usa este token para enviar una solicitud HTTP POST autenticada al siguiente extremo: https://actions.googleapis.com/v2/conversations:send
  3. Establece el encabezado Content-Type en application/json como parte de tu solicitud.

En los siguientes ejemplos, se muestra cómo implementar actualizaciones de pedidos:

Node.js

Este código usa la biblioteca de autenticación de 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

Este código usa la biblioteca de autenticación de 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

Este código usa la biblioteca de autenticación de 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 que los pedidos se actualicen correctamente y sin errores, Google muestra una respuesta HTTP 200 con una carga útil vacía. Si hubo un problema, por ejemplo, si la actualización tiene un formato incorrecto, Google mostrará un error.