Webhooks
Te avisamos con un POST
firmado a tu URL cuando una factura se timbra o cuando cambia
su cancelación. El cuerpo trae la factura completa, así que no tienes que pedirla otra vez para saber qué pasó.
POST, y con el webhook te enteras sin tener que preguntar.
Eventos
- mx.invoice.stamped
- La factura quedó timbrada. Sale igual si la timbraste por la API, desde el panel o con la facturación masiva.
- mx.invoice.cancellation_requested
- El SAT recibió la solicitud de cancelación y el receptor tiene que contestarla. El comprobante sigue vigente: data.status ya dice canceled, y data.cancellation.status dice requested.
- mx.invoice.cancellation_accepted
- El receptor aceptó la cancelación. El comprobante quedó cancelado.
- mx.invoice.cancellation_final
- Cancelada sin la aceptación del receptor: no hacía falta, o dejó pasar el plazo. El comprobante quedó cancelado.
- mx.invoice.cancellation_rejected
- El receptor rechazó la cancelación. La factura vuelve a stamped y se puede cancelar otra vez.
Llegan eventos de todos los emisores de la cuenta.
data.issuer.id
dice de cuál es cada
uno.
Cuáles te sirven
Timbrar es síncrono. Si timbraste por la API, la respuesta de tu
POST
ya trae el UUID,
y mx.invoice.stamped
te repite lo mismo. Te sirve para enterarte de las facturas que
no salieron de tu integración, como las que alguien timbra desde el panel. Puede llegarte antes de que termine la
respuesta de tu propio POST.
Con la cancelación pasa algo parecido al principio. Al
cancelar una factura
sale un evento
con el estado que devolvió el SAT en ese momento, que es el mismo que trae la respuesta:
cancellation_requested
si el receptor tiene que contestar, o cancellation_final
si no hacía falta.
Lo que viene después sí es nuevo. Le preguntamos al SAT cada media hora por las cancelaciones que el receptor no ha
contestado, y cuando cambia la respuesta te mandamos cancellation_accepted,
cancellation_final
o cancellation_rejected. Una
respuesta que no cambia nada no manda evento. Si en 30 días no hubo respuesta dejamos de preguntar, y no llega
ningún evento: consulta la factura.
En sandbox
ninguna cancelación pide la
aceptación del receptor. Ahí llegan stamped,
cancellation_requested
y, en la siguiente consulta, cancellation_final.
cancellation_accepted
y cancellation_rejected
sólo llegan en producción.
Configurar un webhook
En el panel, en Cuenta → API, sección Webhooks. Igual que las llaves de API, están incluidos en los dos planes. Una cuenta puede tener varios, por ejemplo uno para el punto de venta y otro para la contabilidad. Si uno deja de contestar, los demás siguen recibiendo.
La URL tiene que usar https
en el puerto 443, sin usuario ni contraseña, y apuntar a
una dirección pública. En Eventos de facturas mexicanas
marca los que quieres. Ninguno viene
marcado: un evento mexicano sólo llega a un webhook que lo pidió.
Al guardarlo te mostramos su secreto, que empieza con whs_. Cópialo en ese momento:
no lo volvemos a mostrar. Con él verificas la firma de cada aviso.
Rotar secreto cambia el secreto en ese momento, sin periodo en que valgan los dos. Desde ahí todo se firma con el nuevo, también los reintentos de eventos anteriores.
La lista de eventos no se edita. Para cambiarla, agrega otro webhook y apaga el anterior. Un webhook no se borra, se apaga. El panel muestra sus últimas entregas con el código que contestó tu servidor.
La petición
- Oden-Event
- El tipo de evento. Es el mismo valor que type en el cuerpo.
- Oden-Delivery
- El intento: whd_<entrega>_<intento>. Cambia en cada reintento, así que no sirve para detectar duplicados.
- Oden-Signature
- t=<segundos unix>,v1=<firma en hexadecimal>. Se calcula de nuevo en cada intento.
- Content-Type
- application/json
- User-Agent
- OdenTax-Webhooks/1.0
- id
- El id del evento, evt_…. Es el mismo en cada reintento y si el mismo cambio se anuncia dos veces. Úsalo para descartar duplicados.
- type
- El tipo de evento.
- created_at
- Cuándo se generó el evento, en ISO 8601.
- data
- La factura, con la misma forma que devuelve GET /v1/mx/invoices/:id en ese momento.
data
es una foto de la factura cuando ocurrió el evento. Un reintento manda el mismo
cuerpo, aunque la factura haya cambiado mientras tanto.
Verificar la firma
La firma es un HMAC-SHA256, con el secreto del webhook, de t, un punto y el cuerpo tal
como llegó: "#{t}.#{cuerpo}". Calcúlalo tú y compáralo con
v1
en tiempo constante.
Usa los bytes del cuerpo antes de parsear el JSON. Un JSON parseado y vuelto a serializar puede cambiar el orden o los espacios, y la firma ya no coincide.
t
va dentro de lo firmado, así que no se puede cambiar sin romper la firma. Rechaza
los avisos con un t
de hace más de cinco minutos: así una petición interceptada no se
puede repetir después. Cada reintento lleva un t
nuevo, por lo que esta regla no
afecta a los reintentos.
Qué contestar
Cualquier 2xx
cuenta como entregado. Todo lo demás se reintenta, un 4xx
también. Contesta 2xx
a los eventos que no te
interesan, o se reintentarán hasta agotar los intentos.
Esperamos 3 segundos para conectar y 5 para la respuesta. Si tu proceso tarda más, contesta primero y procesa
después. No seguimos redirecciones: un 301
o un 302
cuenta como fallo. Lo que pongas en el cuerpo de la respuesta no se usa.
Reintentos
Siete intentos en unas 33 horas. Cada uno se cuenta desde el anterior.
- 1
- En cuanto ocurre el evento
- 2
- 1 minuto después
- 3
- 5 minutos después
- 4
- 30 minutos después
- 5
- 2 horas después
- 6
- 6 horas después
- 7
- 24 horas después. Es el último.
Si 20 entregas seguidas agotan sus siete intentos, apagamos el webhook y le escribimos al dueño de la cuenta. Una sola entrega exitosa reinicia el conteo. Para volver a recibir, arregla tu servidor y pulsa Encender en el panel.
Mientras un webhook está apagado no se le genera nada, y lo que tenía pendiente ya no se manda. Al encenderlo no se reenvía lo que pasó en ese tiempo: consulta las facturas que te interesen.
Orden y duplicados
No prometemos orden. Cada entrega va por separado, y un reintento puede llegar después de un evento posterior. La factura no trae una fecha de modificación con la cual quedarte con el más nuevo, así que cuando el orden importe, consulta la factura: la respuesta dice dónde está ahora.
Un mismo evento puede llegarte más de una vez, por ejemplo si tu servidor lo procesó pero tardó más de 5 segundos en
contestar. Descarta los repetidos por el id
del cuerpo, que no cambia, y no por Oden-Delivery, que cambia en cada intento.