Actualización de pedidos asíncrona

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 el cambio.

Estos son algunos de los motivos más comunes por los que se envían actualizaciones de pedidos:

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

En las siguientes secciones, se proporcionan detalles para abordar estas diferentes situaciones con actualizaciones de pedidos.

Estados de transición de los pedidos

Un pedido tiene seis estados posibles. Estos estados y sus posibles transiciones se describen en el siguiente diagrama:

Transiciones de estado del pedido

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

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

Si permites que los clientes cancelen pedidos, puedes usar el estado CANCELLED. Se puede cancelar un pedido cuando está en los estados CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP o IN_TRANSIT. Tu servicio de pedidos de extremo a extremo debe emitir reembolsos según tu política de cancelación y el estado de los pagos en el momento de la cancelación.

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

Proporciona un tiempo estimado de entrega

Puedes proporcionarles a los usuarios un intervalo de tiempo estimado para que sepan cuándo estará listo su pedido para retirarlo (o entregarlo). Usa el campo estimatedFulfillmentTimeIso8601 de FoodOrderUpdateExtension para proporcionar un período estimado en el que el pedido de un cliente estará listo para retirarse o entregarse.

Envía el estimatedFulfillmentTimeIso8601 en los siguientes horarios:

  • Cuando el tiempo estimado esté disponible, idealmente en el estado CREATED o CONFIRMED del pedido.
  • Cuando cambia el tiempo estimado, por ejemplo, cuando se actualiza 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 tus estimaciones y proporciona un rango de fecha y hora en lugar de una fecha y hora fijas. Siempre que sea posible, debes tener en cuenta las 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 cuyo tiempo de entrega estimado es de 1:00 p.m.

Proporciona acciones de administración de pedidos

Cuando envíes una actualización de pedido, puedes proporcionarles a los clientes recursos que los ayuden 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 lo prepara para hacer un seguimiento del progreso, realizar cambios o cancelarlo.

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

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

  • CUSTOMER_SERVICE: Proporciona a los clientes una acción para comunicarse con el servicio de atención al cliente. Este tipo de acción de administración es obligatorio para las actualizaciones de pedidos.
  • EMAIL: Proporciona a los clientes una acción para enviar un correo electrónico a la dirección de correo electrónico proporcionada.
  • CALL: Proporciona a los clientes una acción para llamar al número de teléfono proporcionado.
  • VIEW_DETAIL: Proporciona 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 está en el estado CONFIRMED, la acción CUSTOMER_SERVICE puede dirigir a tu número de teléfono de atención al cliente. Cuando ese estado del pedido se actualiza a IN_TRANSIT, la acción CUSTOMER_SERVICE puede apuntar al número de teléfono del restaurante de entrega.

Envía actualizaciones de pedidos

Usas el tipo de mensaje AsyncOrderUpdateRequestMessage para enviar una actualización del pedido al servicio de pedidos de extremo a extremo. Google responde con un AsyncOrderUpdateResponseMessage. Por ejemplo, si quieres informar a un cliente que su pedido es 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 de pedidos

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

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

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

CONFIRMED

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

{
  "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 el pedido se rechazó con un motivo.

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

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.

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

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

FULFILLED

En este ejemplo, se muestra una solicitud de actualización de pedido de muestra que notifica al usuario que se retiró o 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 Implementa actualizaciones de pedidos avanzadas.

Genera un token de autorización y envía el mensaje

Las actualizaciones de pedidos requieren un token de autorización para que el servicio de pedidos de extremo a extremo pueda verificar que el mensaje provenga de tu servicio web de pedidos de extremo a extremo.

Para implementar actualizaciones de pedidos en tu proyecto, sigue estos pasos:

  1. Sigue estos pasos para generar un token de autorización:
    1. Usa la biblioteca de Google Auth para leer las credenciales de tu archivo de cuenta de servicio.
    2. Solicita un token con el siguiente alcance de 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 como application/json como parte de tu solicitud.

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

Node.js

En este código, se usa la biblioteca de Google Auth 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 Google Auth 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 las actualizaciones de pedidos correctas 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 muestra un error.