Registra un endpoint
Configura la URL del endpoint y su secreto de firma en el panel de socios. Cada modo tiene su propio par, así que tus eventos de prueba nunca llegan a tu endpoint de producción y ningún despliegue de staging puede enterarse de un pedido real. Listo haráPOST de JSON a esa URL con tres encabezados que importan:
Authorization ni id de cuenta en el cuerpo. La firma es la
identificación: es lo que prueba que la petición vino de Listo y no fue alterada
en el camino, y es lo único que lo prueba.
El sobre
Cada evento tiene los mismos cuatro campos, sea cual sea su tipo.delivery.courier_location, data es el objeto
completo, con la misma forma con la que responden los endpoints REST, así que un
evento es directamente comparable con lo que ya guardaste. El data de arriba
está recortado; uno real trae todos los campos que tiene un envío.
Un evento que pides tú mismo con un webhook de prueba
trae un quinto campo,
"test": true, y describe objetos que no existen. Los
eventos reales nunca lo traen.
store.updated es una señal, no una respuesta: el objeto de tienda no trae un
campo de disponibilidad de tarjeta, así que cuando te llegue uno, vuelve a leer
GET /v1/stores/{id}/stripe_status.
store.connected se dispara en cuanto el dueño aprueba, y no espera a su
navegador: puede llegarte antes de que el navegador regrese con el código, y te
sigue llegando cuando el navegador nunca regresa. Ve
conecta un restaurante que ya usa Listo.
Verifica antes de confiar
Revisa la firma antes de parsear el cuerpo, y rechaza con un400 cualquier cosa
que no cuadre. Cualquiera puede hacer POST a tu endpoint; la firma es lo que
separa a Listo de los demás.
verify_webhook_signature.rb
Responde y después trabaja
Contesta2xx en cuanto la firma cuadre y haz el trabajo real después. Listo
espera 5 segundos una respuesta y no sigue redirecciones, así que un endpoint
lento se lee como una entrega fallida y una redirección se lee como un error.
Tres propiedades sobre las que construir:
- Al menos una vez. El mismo evento puede llegar dos veces. Deduplica por
id, que es estable en cada reenvío de ese evento. - Sin orden. Dos eventos enviados casi al mismo tiempo pueden llegar en cualquier orden. Ordena por las marcas de tiempo del propio objeto y no por la llegada, y trata un estado por el que ya pasaste como noticia vieja, no como un retroceso.
- Aditivo. Pueden aparecer tipos de evento nuevos y campos nuevos dentro de v1. Ignora lo que no reconozcas en lugar de fallar con ello.
2xx se reintenta con huecos crecientes, unos seis
intentos en total. Un delivery.courier_location nunca se reintenta: ya viene en
camino otra posición, y reenviar una vieja atrasaría tu mapa en lugar de ponerlo
al día.
Cuando te perdiste algunos
Si tu endpoint estuvo caído, vuelve a leer los envíos: ellos son el registro, y los eventos solo fueron un aviso sobre ellos. Un evento reenviado no puede hacer esto por ti, porque su cuerpo es la instantánea del momento en que ocurrió el evento, así que un cambio de estatus de hace un día te habla de un estado que el pedido dejó atrás hace mucho. LeerGET /v1/deliveries siempre te da cómo están
las cosas ahora.
Para ver qué te perdiste, o para enviar un evento otra vez mientras arreglas un
manejador, ve depurar tu integración: puedes listar
cada evento y cada intento de entregarlo, reenviar un evento y enviar un evento de
prueba de cualquier tipo.
reconcile_deliveries.rb
data, un
booleano has_more y un cursor opaco que regresas para obtener la siguiente
página. Las páginas traen hasta 50 filas y vienen de la más reciente a la más
antigua. El cursor es una cadena sin significado propio, así que nunca construyas
uno ni lo analices.
El dinero tiene su propio registro, exactamente igual. GET /v1/ledger lista
cada movimiento de tu saldo, así que un periodo se puede conciliar sin ninguno de
los eventos que lo anunciaron. Ve
saldo y retiros.
Estados a los que vale la pena reaccionar
pickup_ready_at
y sin nada que Listo le tenga que cobrar al cliente, que es todo pedido
inmediato en efectivo o prepagado, nace en dispatching y nunca pasa por
scheduled. scheduled significa una recolección futura y nada más, y un
pedido con tarjeta también llega ahí si se paga antes de que se abra su ventana.
awaiting_location es un pedido que la tienda tomó por la consola de Listo o por
su asistente de WhatsApp y cuyo punto de entrega Listo todavía no da por bueno.
Queda retenido, sin pedir repartidor, hasta que el cliente confirma la ubicación
(en un enlace con mapa o compartiéndola en el chat de WhatsApp) o la tienda la
confirma desde su consola; entonces pasa a scheduled o a dispatching. También puede terminar en no_courier, cuando no
se puede pedir un repartidor para esa ubicación (queda fuera de donde reparte la
tienda, o la tienda misma no puede pedir un repartidor por ahora), o en
canceled. Un pedido con tarjeta llega ahí una vez pagado. Un envío que creas
por la API nunca está en este estado, así que solo lo ves en tiendas cuyos dueños
usan la consola o el asistente.
returning es un envío todavía vivo, no final: la entrega falló y el repartidor
está regresando el pedido a la tienda. Termina en returned cuando el pedido ya
está de vuelta. El cargo del regreso ya viene en charges cuando empieza, y las
posiciones del repartidor siguen llegando.
delivered, canceled y returned son finales. A canceled se puede llegar
desde cualquier punto anterior.
no_courier es el único callejón sin salida del que sí puedes salir: nadie tomó
el pedido. Reinténtalo con
POST /v1/deliveries/{id}/retries
o cancélalo, y lee failure_reason para decidir cuál. Ese campo es uno de cuatro
valores, agrupados según de qué lado está el problema:
Un envío
returned es el mismo envío de principio a fin: el pedido regresó a la
tienda en lugar de convertirse en un segundo pedido, y las posiciones del
repartidor siguen llegando durante el tramo de regreso. El evento delivery.status_changed a returning trae
el código de devolución de la tienda, que tu sistema tiene que mostrarle al
personal de la tienda (ve el código de devolución).
En una cancelación, cancellation.party dice quién la pidió: partner por tu
lado del pedido, courier por la persona que lo lleva, y platform por todo lo
demás que Listo se atribuye a sí mismo. cancellation.reason y
cancellation.description traen el resto.
Lo que nunca va en un payloadLos eventos de envío llevan el nombre, el teléfono y la dirección de tu cliente,
porque tú se los diste a Listo y el repartidor los necesita. Nada más viaja con
ellos: ningún dato de tarjeta, ningún identificador interno, y nada que nombre a
quien realiza el viaje. Los estados, los errores, los enlaces de rastreo y las
referencias de soporte son vocabulario propio de Listo de principio a fin.
El estado final no siempre es el último evento
Un envío que termina encanceled o en returned puede costar dinero después de
terminar. Una cancelación tan tarde que ya había un repartidor trabajando, o un
viaje de devolución tras una entrega fallida, se te cobra a la tarifa de envío
por cada tramo que se manejó, y ese cargo apenas queda resuelto cuando ya se
procesó la cancelación o la devolución. Eso normalmente es más tarde que el
cambio de estado que lo provocó, a veces por un buen rato.
A veces eso se resuelve junto con la cancelación misma, y el
delivery.status_changed final ya llega con charges lleno. A veces no, y el
cargo se registra después, en un envío cuyo evento de estado final atendiste
hace rato. Entonces llega como un delivery.updated:
charges viene en todo envío y es [] hasta que se registra algo, que en casi
todo pedido es para siempre. type es cancellation_fee o return_fee, y
amount_cents es lo que se te cobró por él: la tarifa de envío por cada tramo
que se manejó, menos tu propio margen de tienda. Es un campo del objeto de envío
como cualquier otro, así que viene en toda respuesta que traiga uno:
GET /v1/deliveries/{id} tiene la
lista completa.
De cualquiera de las dos formas se te avisa una sola vez: un cargo que ya venía
en el evento final no genera además un evento propio. Lo que no puedes es dar
por hecho cuál de los dos lo trajo, así que lee charges del evento que te
toque.
Trata el arreglo como la verdad actual de ese envío y no como una lista de
cargos nuevos desde el último evento, y arma tu facturación sobre el id del
envío. Los eventos llegan al menos una vez, así que el mismo charges lleno te
puede llegar dos veces, y quien vaya sumando lo que trajo cada evento termina
cobrando el mismo viaje otra vez. delivery.courier_location es el único evento
que nunca trae nada de esto: su cuerpo es el objeto de posición recortado, sin
envío adentro.