Webhooks
Los webhooks son notificaciones HTTP POST que Avirato Payments envia automaticamente a tu sistema cuando ocurre un evento relevante: un pago se completa, un reembolso se procesa, una captura se confirma, etc.
Por que son importantes
Los webhooks son la forma mas fiable de saber que una operacion se ha completado. A diferencia del redirect (urlOk/urlKo), que depende de que el cliente vuelva a tu pagina:
- Los webhooks llegan siempre, incluso si el cliente cierra el navegador
- Llegan unos segundos despues de que el procesador confirme la operacion
- No dependen del comportamiento del usuario
Recomendacion: Usa los webhooks como fuente principal de confirmacion de operaciones. Usa el redirect y el polling como complementos.
Como configurar tu URL de webhooks
Desde Integraciones > Configuracion Webhooks en el dashboard puedes registrar una URL publica por entorno (Live y Test). Cada URL se puede activar/desactivar por separado. Recomendamos:
- URL HTTPS con certificado valido.
- Endpoint dedicado a Avirato Payments (por ejemplo
https://tu-dominio.com/avirato-payments/webhooks). - Necesitas tener una API key activa para el entorno antes de configurar su URL. Si no existe, el dashboard responde con 422.
Importante: solo se envian los webhooks generados a partir del momento en que la URL este configurada y activa. Eventos previos no se reenvian retroactivamente.
Verificacion de autenticidad (firma HMAC)
Todos los webhooks live llegan firmados con HMAC-SHA256 usando el mismo client_secret que se te muestra al crear (o rotar) la API key del entorno. Cabeceras:
| Cabecera | Valor |
|---|---|
X-Avirato-Timestamp | Timestamp Unix (segundos) del momento del envio |
X-Avirato-Signature | t={timestamp},v1={hmac} — donde hmac = hash_hmac('sha256', timestamp + "." + body, client_secret) |
Ejemplo de verificacion en PHP:
$timestamp = $_SERVER['HTTP_X_AVIRATO_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_AVIRATO_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
if (!preg_match('/^t=(\d+),v1=([a-f0-9]+)$/', $signature, $m)) {
http_response_code(400);
exit;
}
$expected = hash_hmac('sha256', $timestamp . '.' . $body, $clientSecret);
if (!hash_equals($expected, $m[2])) {
http_response_code(401);
exit;
}En Test, si tienes API key test creada, los webhooks de prueba se firman tambien con su secret. Si no, se envian sin firma para que puedas explorar el formato sin crear API key test todavia.
Politica de reintentos
Como funciona en una linea
Cada vez que Avirato Payments te envia un webhook y tu endpoint no responde con HTTP 200, lo reintenta mas tarde. La espera entre cada fallo y el siguiente reintento crece: empieza en 10 minutos y termina en 48 horas. Tras un total de 10 intentos sin exito Avirato Payments deja de reintentarlo automaticamente.
Cronograma completo
La tabla muestra, para un webhook que falla en cada intento, el momento exacto en el que tu endpoint recibe la siguiente llamada, contado desde el momento en el que se produjo el evento original (autorizacion, refund, etc.).
| Intento | Que es | Cuando lo recibes (contado desde el evento) |
|---|---|---|
| 1 | Envio inicial | en el momento (≈ 0) |
| 2 | 1.er reintento | 10 min despues |
| 3 | 2.º reintento | 30 min despues |
| 4 | 3.er reintento | 1 h 10 min despues |
| 5 | 4.º reintento | 2 h 40 min despues |
| 6 | 5.º reintento | 5 h 40 min despues |
| 7 | 6.º reintento | 11 h 40 min despues |
| 8 | 7.º reintento | 23 h 40 min despues |
| 9 | 8.º reintento | 1 dia 23 h 40 min despues |
| 10 | 9.º (y ultimo) reintento | ≈ 4 dias despues (95 h 40 min) |
| — | — | a partir de aqui ya no se reintenta |
En total son 10 intentos (1 envio inicial + 9 reintentos) repartidos en una ventana de unos 4 dias. El reloj NO se reinicia en cada intento: cuanto mas tarde respondes correctamente, mas se han espaciado los siguientes reintentos.
Como saber en que intento vamos
En el dashboard, en Integraciones > Live > Webhooks, cada fila del historial muestra:
Intentos: numero de intentos consumidos hasta ahora (1 al 10).Ultimo intento: la fecha y hora del ultimo envio.HTTP: el codigo HTTP que devolvio tu endpoint en ese ultimo envio.Estado:Entregado(success),Pendiente(todavia con reintentos por delante) oFallido(no aplicable en el flujo live mientras queden reintentos).
Que pasa si el endpoint sigue cayendo
Si tras los 10 intentos (≈ 4 dias) tu endpoint no ha respondido correctamente NI UNA sola vez, Avirato Payments deja de reintentarlo automaticamente aunque mas tarde repares tu sistema. En ese caso:
- Reactiva tu endpoint y verifica que ya responde correctamente con webhooks normales.
- Para los eventos que quedaron pendientes, abre Integraciones > Live > Webhooks, identifica los webhooks con estado
Pendientey fuerza el reenvio manual desde el boton "Reenviar". Cada reenvio manual hace una llamada HTTP nueva a tu endpoint con el mismo payload.
Que pasa si desactivas o eliminas tu URL temporalmente
- Mientras tu URL este desactivada (o no exista) Avirato Payments sigue encolando los webhooks de los eventos que se vayan produciendo.
- El cron sigue contando reintentos: si reactivas la URL dentro de la ventana de ≈ 4 dias, los pendientes se entregaran automaticamente en el siguiente tick (siempre que aun queden intentos disponibles para ese webhook concreto).
- Pasada esa ventana, los eventos quedan abandonados y NO se vuelven a enviar aunque vuelvas a activar la URL. Tendras que solicitarlos manualmente desde el dashboard si te interesan.
Comportamiento del reenvio manual
- Si reenvias manualmente un webhook que estaba pendiente de reintentos y el reenvio tiene exito, los reintentos automaticos se detienen — el webhook ya se considera entregado.
- Puedes reenviar un webhook que ya fue entregado correctamente. El dashboard te pedira confirmacion antes para evitar duplicados accidentales, pero el reenvio se ejecuta igualmente si lo confirmas.
Fuente de verdad del estado del pago (Live)
En el entorno Live, las notificaciones del procesador tienen la ultima palabra sobre el estado real del pago. Esto significa que un pago puede cambiar de estado despues de que tu sistema lo haya tratado como completado.
Casos tipicos en los que puede llegar un webhook posterior que revierte un exito previo:
- Chargeback: el titular reclama el cargo a su banco
- Fraude detectado a posteriori: el procesador identifica una transaccion fraudulenta dias despues
- Reversion bancaria: el banco emisor revierte la operacion
- Correccion del procesador: una notificacion previa contenia datos incompletos o incorrectos
Cuando recibimos uno de estos eventos en Live, actualizamos el estado del pago en nuestro sistema (por ejemplo, de AUTHORISED a FAILED) y te enviamos el webhook correspondiente. Tu integracion debe:
- Aceptar webhooks asincronos que cambien el resultado de un pago previamente exitoso
- Tratar los webhooks (no la respuesta inmediata ni el redirect) como la referencia para la conciliacion y el estado final
- Volver a consultar la API (
GET /payment/session/{sessionId}) cuando necesites el ultimo estado conocido
Test vs Live: en Test los webhooks los genera nuestro propio simulador y reflejan unicamente la simulacion que has lanzado. No hay chargebacks ni reverts en Test.
Formato general
Todos los webhooks llegan como POST a tu URL con este formato:
Cabeceras
| Cabecera | Valor |
|---|---|
Content-Type | application/json |
webcode | El webcode asociado al pago (ej: SHOP01) |
Body
{
"event_code": "AUTHORISATION",
"data": {
...campos especificos del evento...
}
}Respuesta esperada
Tu endpoint debe responder con HTTP 200. El body de la respuesta no se evalua — basta con el codigo de estado.
Si tu endpoint no responde con HTTP 200, el webhook se reintentara automaticamente con backoff exponencial.
---
Tipos de evento
AUTHORISATION — Pago completado (online o POS)
Se envia cuando un pago (online o POS) se autoriza correctamente o falla.
{
"event_code": "AUTHORISATION",
"data": {
"amount": 15000,
"currency": "EUR",
"paymentReference": "SHOP01-PXT-17195000005678",
"paymentStatus": "AUTHORISED",
"description": "Pedido #12345 - Servicio premium",
"customReference": "ORD-2026-12345",
"paymentSource": "external_api"
}
}| Campo | Siempre presente | Descripcion |
|---|---|---|
amount | Si | Importe en centimos |
currency | Si | Codigo ISO 4217 |
paymentReference | Si | Referencia del pago |
paymentStatus | Si | AUTHORISED si exitoso, FAILED si fallo |
description | Si | Descripcion del pago |
customReference | No | Tu referencia interna (si la proporcionaste al crear la sesion/pago) |
paymentSource | No | Origen del pago (ej: external_api) |
failedReason | No | Motivo del fallo (solo si el pago fallo) |
Como identificar el tipo de pago por la referencia:
| Infijo en la referencia | Tipo | Entorno |
|---|---|---|
-PXT- | Pago online | Live |
-PXTT- | Pago online | Test |
-POSX- | Pago POS | Live |
-POSXT- | Pago POS | Test |
Ejemplo de pago POS exitoso:
{
"event_code": "AUTHORISATION",
"data": {
"amount": 8500,
"currency": "EUR",
"paymentReference": "SHOP01-POSX-17195300001234",
"paymentStatus": "AUTHORISED",
"description": "Venta mostrador #205",
"customReference": "VENTA-205-20260628",
"paymentSource": "external_api"
}
}Ejemplo de pago fallido (rechazado):
{
"event_code": "AUTHORISATION",
"data": {
"amount": 15000,
"currency": "EUR",
"paymentReference": "SHOP01-PXT-17195000005678",
"paymentStatus": "FAILED",
"description": "Pedido #12345",
"failedReason": "Refused",
"paymentSource": "external_api"
}
}Importante: Los webhooks se envian tanto para pagos exitosos como fallidos. Comprueba siempre el campo
paymentStatuspara determinar el resultado.
---
REFUND — Reembolso procesado
Se envia cuando un reembolso se completa (exito o fallo) por el procesador.
{
"event_code": "REFUND",
"data": {
"amount": 5000,
"currency": "EUR",
"paymentReference": "SHOP01-PXT-17195000005678",
"refundReason": "CUSTOMER REQUEST",
"refundReference": "SHOP01-RFX-17195234567890",
"status": "success",
"customReference": "ORD-2026-12345",
"paymentSource": "external_api"
}
}| Campo | Siempre presente | Descripcion |
|---|---|---|
amount | Si | Importe reembolsado en centimos |
currency | Si | Codigo ISO 4217 |
paymentReference | Si | Referencia del pago original |
refundReason | Si | Motivo del reembolso (CUSTOMER REQUEST, FRAUD, RETURN, DUPLICATE, OTHER) |
refundReference | Si | Referencia de esta operacion de reembolso |
status | Si | success o failed |
customReference | No | Tu referencia interna |
paymentSource | No | Origen del pago |
failedReason | No | Motivo del fallo (solo si status: "failed") |
Ejemplo de reembolso fallido:
{
"event_code": "REFUND",
"data": {
"amount": 5000,
"currency": "EUR",
"paymentReference": "SHOP01-PXT-17195000005678",
"refundReason": "CUSTOMER REQUEST",
"refundReference": "SHOP01-RFX-17195234567890",
"status": "failed",
"failedReason": "Insufficient balance"
}
}---
PREAUTHORISATION — Resultado de captura, cancelacion o extension
Se envia cuando una operacion sobre una preautorizacion se completa. El campo action indica el tipo de operacion.
Captura completada
{
"event_code": "PREAUTHORISATION",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"action": "capture",
"result": "success",
"amount": 45000,
"currency": "EUR",
"customReference": "ORD-2026-67890",
"paymentSource": "external_api"
}
}Cancelacion completada
{
"event_code": "PREAUTHORISATION",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"action": "cancellation",
"result": "success",
"customReference": "ORD-2026-67890",
"paymentSource": "external_api"
}
}Extension completada
{
"event_code": "PREAUTHORISATION",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"action": "extend",
"result": "success",
"expirationDate": "2026-08-15T00:00:00Z",
"customReference": "ORD-2026-67890",
"paymentSource": "external_api"
}
}| Campo | Siempre presente | Descripcion |
|---|---|---|
paymentReference | Si | Referencia del pago preautorizado |
action | Si | capture, cancellation o extend |
result | Si | success o failure |
amount | Solo capture | Importe capturado en centimos |
currency | Solo capture | Codigo ISO 4217 |
expirationDate | Solo extend exitoso | Nueva fecha de expiracion |
customReference | No | Tu referencia interna |
paymentSource | No | Origen del pago |
failedReason | No | Motivo del fallo (solo si result: "failure") |
Ejemplo de captura fallida (importe excede el autorizado):
{
"event_code": "PREAUTHORISATION",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"action": "capture",
"result": "failure",
"failedReason": "Payment already captured, cannot capture again"
}
}Nota sobre el flujo de modificaciones: Todas las modificaciones (reembolso, captura, cancelacion, extension) son asincronas. La respuesta inmediata de la API devuelve estado
received, indicando que la peticion se ha aceptado para procesamiento. El resultado final (successofailed) llega posteriormente via webhook. El integrador no debe considerar la operacion como completada hasta recibir el webhook de confirmacion.
---
CHARGEBACK — Contracargo
Se envia cuando un titular de tarjeta inicia un contracargo contra un pago. Los contracargos son procesados por el emisor de la tarjeta y pueden tardar dias o semanas en resolverse.
{
"event_code": "CHARGEBACK",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"chargebackReference": "SHOP01-CHB-17195123456789",
"action": "chargeback",
"result": "success",
"amount": 15000,
"currency": "EUR",
"reason": "Fraudulent transaction",
"chargebackReasonCode": "10.4",
"chargebackSchemeCode": "VISA_10_4",
"defensePeriodEndsAt": "2026-06-15",
"defendable": true,
"disputeStatus": "Undefended",
"paymentSource": "external_api"
}
}| Campo | Siempre presente | Descripcion |
|---|---|---|
paymentReference | Si | Referencia del pago original |
chargebackReference | Si | Referencia unica del contracargo |
action | Si | Siempre chargeback |
result | Si | success o failure |
amount | Si | Importe en centimos |
currency | Si | Moneda ISO 4217 |
reason | Si | Motivo del contracargo |
chargebackReasonCode | No | Codigo de motivo del esquema de tarjeta |
chargebackSchemeCode | No | Codigo del esquema (Visa, Mastercard, etc.) |
defensePeriodEndsAt | No | Fecha limite para defender el contracargo |
defendable | No | true si puedes disputar el contracargo |
disputeStatus | No | Estado de la disputa (Undefended, Defended, etc.) |
failedReason | No | Motivo si result es failure |
paymentSource | No | Origen del pago (external_api, etc.) |
---
NOTIFICATION_OF_CHARGEBACK — Aviso previo de contracargo
Aviso anticipado de que un contracargo puede estar en camino. No todos los procesadores lo envian. Te permite prepararte antes de que el contracargo formal se procese.
{
"event_code": "NOTIFICATION_OF_CHARGEBACK",
"data": {
"paymentReference": "SHOP01-PXT-17195123456789",
"chargebackReference": "SHOP01-CHB-17195123456789",
"action": "notification_of_chargeback",
"result": "success",
"amount": 15000,
"currency": "EUR",
"reason": "Cardholder disputes transaction",
"chargebackReasonCode": "10.4",
"chargebackSchemeCode": "VISA_10_4",
"defensePeriodEndsAt": "2026-06-15",
"defendable": true,
"disputeStatus": "Undefended",
"autoDefended": false,
"arn": "74027600000012345678901",
"paymentSource": "external_api"
}
}Contiene los mismos campos que CHARGEBACK mas dos campos adicionales opcionales:
| Campo | Descripcion |
|---|---|
autoDefended | true si el procesador defendio automaticamente el contracargo |
arn | Acquirer Reference Number — identificador de la transaccion del adquirente |
---
Webhooks en entorno Test
En el entorno Test, los webhooks no provienen del procesador de pagos real. Son generados automaticamente por el backend de Avirato Payments inmediatamente despues de completar la operacion simulada.
Diferencias con Live
| Aspecto | Live | Test |
|---|---|---|
| Quien envia | El procesador de pagos | El backend de Avirato Payments |
| Cuando llega | Segundos a minutos despues de la operacion | 1-3 segundos despues de la respuesta HTTP |
| Formato | Identico | Identico |
| Campos | Identicos | Identicos |
| Reintentos automaticos | Si (backoff exponencial, hasta ~4 dias) | No: el simulador hace un unico envio |
| Reenvio manual | Si, desde el dashboard | Si, desde el dashboard (mismo boton) |
| Criterio de exito HTTP | HTTP 200 | HTTP 200 |
Lo que debes saber
- El formato es exactamente el mismo: Puedes desarrollar tu logica de procesamiento de webhooks en Test y funcionara en Live sin cambios
- Llegan para pagos exitosos Y fallidos: Tanto en Live como en Test, recibiras webhooks cuando un pago se rechaza o una operacion falla
- URLs independientes por entorno: Los webhooks de Test se envian a la URL configurada para el entorno Test, y los de Live a la URL de Live. Configura ambas desde el panel de webhooks del dashboard
- Las referencias usan sufijos Test: Las
paymentReferenceyoperationReferencecontendran los infijos de test (-PXTT-,-POSXT-,-RFXT-, etc.) - Criterio de exito simple: Tanto en Live como en Test, basta con que tu endpoint responda HTTP 200 para que el webhook se marque como entregado. Cualquier otro codigo de estado se considera fallo y activara los reintentos (en Live) o quedara como fallido en el dashboard (en Test).
- Reenvio manual disponible en Test y Live: Desde el panel de webhooks del dashboard puedes reenviar manualmente un webhook concreto. Tanto en Test como en Live, el reenvio utiliza la URL actualmente configurada para ese entorno, no la URL que se uso en el envio original. Si reenvias un webhook ya entregado, el dashboard te pedira confirmacion explicita para evitar duplicados accidentales.
Ejemplo de webhook de test (pago online exitoso)
{
"event_code": "AUTHORISATION",
"data": {
"amount": 15000,
"currency": "EUR",
"paymentReference": "SHOP01-PXTT-17195000005678",
"paymentStatus": "AUTHORISED",
"description": "Pedido test #12345",
"customReference": "TEST-ORD-12345",
"paymentSource": "external_api"
}
}Ejemplo de webhook de test (POS rechazado)
{
"event_code": "AUTHORISATION",
"data": {
"amount": 10077,
"currency": "EUR",
"paymentReference": "SHOP01-POSXT-17195300001234",
"paymentStatus": "FAILED",
"description": "Venta test #100",
"failedReason": "Refused",
"paymentSource": "external_api"
}
}---
Referencia rapida
| Operacion | event_code | action (si aplica) | Como identificar External API |
|---|---|---|---|
| Pago online completado | AUTHORISATION | — | paymentReference contiene -PXT- (Live) o -PXTT- (Test) |
| Pago POS completado | AUTHORISATION | — | paymentReference contiene -POSX- (Live) o -POSXT- (Test) |
| Reembolso procesado | REFUND | — | refundReference contiene -RFX- (Live) o -RFXT- (Test) |
| Captura procesada | PREAUTHORISATION | capture | Operacion sobre pago External API |
| Cancelacion procesada | PREAUTHORISATION | cancellation | Operacion sobre pago External API |
| Extension procesada | PREAUTHORISATION | extend | Operacion sobre pago External API |
| Contracargo recibido | CHARGEBACK | — | Contracargo iniciado por el titular de la tarjeta |
| Notificacion de contracargo | NOTIFICATION_OF_CHARGEBACK | — | Aviso previo de un posible contracargo |
---
Buenas practicas
Idempotencia en tu endpoint
Tu endpoint de webhook puede recibir la misma notificacion mas de una vez (por reintentos o duplicados del procesador). Asegurate de que procesar el mismo webhook dos veces no cause problemas: usa paymentReference o refundReference como clave de deduplicacion.
Respuesta rapida
Responde al webhook lo antes posible (idealmente en menos de 5 segundos). Si necesitas hacer procesamiento pesado, encola el trabajo y responde inmediatamente con HTTP 200.
No dependas de un solo canal
Usa los webhooks como canal principal, pero implementa tambien:
- Verificacion post-redirect: Cuando el cliente vuelva a
urlOk, consultaGET /payment/session/{id}para confirmar - Polling de seguridad: Un proceso periodico que consulte sesiones en estado
pendingantiguas para detectar pagos completados que no hayas procesado