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 > Configuración > URLs de webhooks puedes registrar una URL pública por entorno (Live y Test). Cada URL puede activarse y desactivarse por separado. Recomendamos:
- URL HTTPS (no HTTP) con certificado válido.
- Endpoint dedicado a Avirato Payments (por ejemplo
https://tu-dominio.com/avirato-payments/webhooks) que sepa procesar el formato común que se describe más abajo. - Necesitas tener una API key activa para el entorno antes de configurar su URL. Si no existe, el dashboard te lo indica con un mensaje claro y un código 422.
Importante: solo se envían los webhooks generados a partir del momento en que la URL está configurada y activa. Eventos previos no se reenvían retroactivamente.
Verificación de autenticidad (firma HMAC)
Todos los webhooks live van firmados con HMAC-SHA256 usando el mismo client_secret que se te muestra al crear (o rotar) la API key del entorno. Llegan estas dos cabeceras adicionales:
| Cabecera | Valor |
|---|---|
X-Avirato-Timestamp | Timestamp Unix (segundos) del momento del envío |
X-Avirato-Signature | t={timestamp},v1={hmac} — donde hmac = hash_hmac('sha256', timestamp + "." + body, client_secret) |
Ejemplo de verificación 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 también con su secret. Si no, se envían sin firma para que puedas explorar el formato sin obligación de crear API key test todavía.
Política de reintentos
Cómo funciona en una línea
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 éxito Avirato Payments deja de reintentarlo automáticamente.
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 que se produjo el evento original (autorización, refund, etc.).
| Intento | Qué es | Cuándo lo recibes (desde el evento) |
|---|---|---|
| 1 | Envío inicial | en el momento (≈ 0) |
| 2 | 1.er reintento | 10 min después |
| 3 | 2.º reintento | 30 min después |
| 4 | 3.er reintento | 1 h 10 min después |
| 5 | 4.º reintento | 2 h 40 min después |
| 6 | 5.º reintento | 5 h 40 min después |
| 7 | 6.º reintento | 11 h 40 min después |
| 8 | 7.º reintento | 23 h 40 min después |
| 9 | 8.º reintento | 1 día 23 h 40 min después |
| 10 | 9.º (y último) reintento | ≈ 4 días después (95 h 40 min) |
| — | a partir de aquí ya no se reintenta automáticamente | |
En total son 10 intentos (un envío inicial + 9 reintentos) repartidos en una ventana de unos 4 días. El reloj no se reinicia en cada intento: cuanto más tarde respondes correctamente, más se habrán espaciado los siguientes reintentos.
Cómo saber en qué intento vamos
En el dashboard, en Integraciones > Actividad > Webhooks enviados (entorno Live), cada fila del historial muestra:
- Intentos: número de intentos consumidos hasta ahora (1 a 10).
- Último intento: la fecha y hora del último envío.
- HTTP: el código HTTP que devolvió tu endpoint en ese último envío.
- Estado: Entregado (success), Pendiente (todavía con reintentos por delante).
Si el endpoint sigue cayendo tras los 10 intentos
Si tras los 10 intentos (≈ 4 días) tu endpoint no ha respondido correctamente ni una sola vez, Avirato Payments deja de reintentarlo automáticamente aunque más 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 > Actividad > Webhooks enviados, identifica los webhooks con estado Pendiente y fuerza el reenvío manual con el botón Reenviar. Cada reenvío manual hace una llamada HTTP nueva a tu endpoint con el mismo payload.
Si desactivas o eliminas tu URL de webhooks: los eventos siguen encolándose en Avirato Payments. El cron sigue contando reintentos para cada uno, así que si reactivas la URL dentro de la ventana de ≈ 4 días los pendientes se entregarán automáticamente en el siguiente tick (siempre que aún queden intentos disponibles para ese webhook). Pasada esa ventana los eventos quedan abandonados y no se vuelven a enviar.
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 y un body JSON:
{
"status": "success"
}Si tu endpoint no responde correctamente, 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 paymentStatus para 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"
}
}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 |
Pagos recurrentes
Los pagos recurrentes (recurso subscription en la API) envian sus propios eventos. Todos comparten estos campos en data:
| Campo | Descripcion |
|---|---|
subscriptionId | Id del pago recurrente (WEBCODE-SB-xxxxxxxx) |
status | Estado tras el evento: active, paused, error, pendingNewCard o cancelled |
planCode | Tu codigo de plan (puede ser null) |
environment | Solo en Test: siempre "test" |
- Los de Test solo van a tu URL Test y llevan
environment: "test". - En Live recibes tambien los eventos de los pagos recurrentes creados desde el panel para tu webcode, no solo los creados por API.
- Usa
subscriptionId+event_code+paymentReference(si viene) como clave de deduplicacion.
SUBSCRIPTION_CREATED — Primer pago correcto
El cliente ha pagado el primer importe y se ha guardado su tarjeta. El pago recurrente queda activo.
{
"event_code": "SUBSCRIPTION_CREATED",
"data": {
"subscriptionId": "SHOP01-SB-3f9a1c2e",
"status": "active",
"planCode": "GOLD",
"paymentReference": "SHOP01-PYS-1792069000123",
"pspReference": "T7KQ2N9X4M8P6R3Z",
"amount": 990,
"currency": "EUR"
}
}SUBSCRIPTION_RENEWED — Cobro periodico correcto
{
"event_code": "SUBSCRIPTION_RENEWED",
"data": {
"subscriptionId": "SHOP01-SB-3f9a1c2e",
"status": "active",
"planCode": "GOLD",
"paymentReference": "SHOP01-PYSS-1794912000567-1",
"pspReference": "T8LR3P0Y5N9Q7S4A",
"amount": 2990,
"currency": "EUR",
"paymentsCount": 2,
"lastChargeDate": "2026-11-05 06:00:12",
"nextChargeDate": "2026-12-05 06:00:00"
}
}SUBSCRIPTION_FAILED — Cobro rechazado
Se envia cuando falla el primer pago (el pago recurrente queda en error) o un cobro periodico que todavia tiene reintentos (sigue active). En este segundo caso incluye retryCount, maxRetries y nextRetryAt.
{
"event_code": "SUBSCRIPTION_FAILED",
"data": {
"subscriptionId": "SHOP01-SB-3f9a1c2e",
"status": "active",
"planCode": "GOLD",
"paymentReference": "SHOP01-PYSS-1794912000567-1",
"amount": 2990,
"currency": "EUR",
"error": "Not enough balance",
"errorCode": "NOT_ENOUGH_BALANCE",
"retryCount": 1,
"maxRetries": 3,
"nextRetryAt": "2026-11-05 11:00:12"
}
}SUBSCRIPTION_ERROR — Reintentos agotados (pausado)
El ultimo reintento tambien ha fallado. El pago recurrente pasa a paused con statusReason: payment_failed_max_retries y no se cobra mas hasta que lo reactives (con la tarjeta actual o pidiendo una nueva con update-card). En este caso no llega SUBSCRIPTION_FAILED.
{
"event_code": "SUBSCRIPTION_ERROR",
"data": {
"subscriptionId": "SHOP01-SB-3f9a1c2e",
"status": "paused",
"planCode": "GOLD",
"statusReason": "payment_failed_max_retries",
"requiresAction": "reactivate",
"lastError": "Expired card",
"lastErrorCode": "CARD_EXPIRED"
}
}Pausa, reactivacion, cancelacion y cambio de tarjeta
| event_code | Cuando | Campos extra |
|---|---|---|
SUBSCRIPTION_PAUSED | Lo pausas (API o panel) | pausedAt |
SUBSCRIPTION_REACTIVATED | Lo reactivas con la tarjeta actual | reactivatedAt, nextChargeDate |
SUBSCRIPTION_CANCELLED | Lo cancelas o llega su endAt | cancelledAt |
SUBSCRIPTION_CARD_UPDATED | El cliente paga con su nueva tarjeta (link de update-card). Solo Live; en Test llega SUBSCRIPTION_REACTIVATED | cardLast4, cardBrand, cardExpiryMonth, cardExpiryYear |
{
"event_code": "SUBSCRIPTION_CANCELLED",
"data": {
"subscriptionId": "SHOP01-SB-3f9a1c2e",
"status": "cancelled",
"planCode": "GOLD",
"cancelledAt": "2026-12-20 10:15:03"
}
}Ejemplo en Test (mismo formato + environment):
{
"event_code": "SUBSCRIPTION_REACTIVATED",
"data": {
"subscriptionId": "SHOP01-SB-a07c55d1",
"status": "active",
"planCode": null,
"reactivatedAt": "2026-10-16 09:30:00",
"nextChargeDate": "2026-10-16 09:30:00",
"environment": "test"
}
}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 desde el dashboard | Si | Si |
| 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-,-RFXT-, etc.) - Criterio de exito simple: Tanto en Live como en Test, basta con que tu endpoint responda
HTTP 200para 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 en Test y Live: Desde el panel de webhooks puedes reenviar manualmente cualquier fila. 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 que ya aparece como Entregado, el dashboard te pedira confirmacion antes 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 |
| Pago recurrente: primer pago / cobro correcto | SUBSCRIPTION_CREATED / SUBSCRIPTION_RENEWED | — | subscriptionId contiene -SB-; Test lleva environment: "test" |
| Pago recurrente: cobro rechazado / reintentos agotados | SUBSCRIPTION_FAILED / SUBSCRIPTION_ERROR | — | Mira status y retryCount |
| Pago recurrente: pausa, reactivacion, cancelacion, nueva tarjeta | SUBSCRIPTION_PAUSED, _REACTIVATED, _CANCELLED, _CARD_UPDATED | — | subscriptionId contiene -SB- |
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