Saltar al contenido principal

Webhooks

ONVO usa webhooks para notificar a tu aplicación cuando se produce un resultado procesando una intención de pago, un cargo recurrente, una sesión de checkout o una transferencia entrante.

Para recibirlos, configurá una URL de callback en tu cuenta de ONVO. Enviaremos una petición POST a esa URL cada vez que se procese un evento soportado. El payload incluye un atributo type, que identifica el evento, y un objeto data con la información relacionada.

Referencia API relacionada

Flujo recomendado

  1. Exponé un endpoint HTTPS.
  2. Registrá el endpoint en el Dashboard de ONVO, en la sección de desarrolladores.
  3. Verificá el origen del evento con el secret del webhook.
  4. Procesá el evento de forma idempotente.
  5. Respondé con estado 2xx cuando el procesamiento sea correcto.

Eventos soportados

EventoCuándo ocurre
payment-intent.succeededUna intención de pago se procesa con éxito.
payment-intent.failedUna intención de pago falla.
payment-intent.deferredUna intención de pago queda a la espera de aprobación, por ejemplo en un flujo SINPE.
subscription.renewal.succeededUn cargo recurrente se renueva con éxito.
subscription.renewal.failedLa renovación de un cargo recurrente falla.
checkout-session.succeededUna sesión de checkout se procesa con éxito.
mobile-transfer.receivedSe recibe una transferencia entrante en un número SINPE Móvil personalizado.

mobile-transfer.received es una notificación adicional para comercios con esta funcionalidad activa. No representa por sí sola un pago satisfactorio y no se envía por defecto. Para habilitarla, contactá a soporte.

Formato del payload

ONVO envía todos los webhooks como POST con Content-Type: application/json. El cuerpo siempre tiene esta estructura:

{
"type": "payment-intent.succeeded",
"data": {
"id": "clpiment0001"
}
}
CampoTipoDescripción
typestringNombre del evento recibido. Usalo para decidir qué lógica ejecutar.
dataobjectSnapshot del objeto relacionado con el evento. Su forma depende del valor de type.

Los montos se envían como enteros en la unidad mínima de la moneda. Por ejemplo, 500000 representa ₡5,000.00 en CRC y 5000 representa $50.00 en USD. Las fechas se envían en formato ISO 8601 en UTC. Los ejemplos muestran los campos más importantes para integrar; según el método de pago o el flujo, data puede incluir campos adicionales o valores null.

Payloads por evento

payment-intent.succeeded

data contiene la intención de pago actualizada. Cuando aplica, incluye el cliente y la lista de cargos generados para esa intención.

{
"type": "payment-intent.succeeded",
"data": {
"id": "clpiment0001",
"accountId": "clacct0001",
"mode": "test",
"amount": 500000,
"baseAmount": 500000,
"baseExchangeRate": 1,
"capturableAmount": 0,
"receivedAmount": 500000,
"currency": "CRC",
"status": "succeeded",
"confirmationAttempts": 1,
"description": "Orden #1001",
"paymentMethodId": "clpm0001",
"customerId": "clcus0001",
"metadata": {
"orderId": "order_1001"
},
"customer": {
"id": "clcus0001",
"name": "Ana Mora",
"email": "ana@example.com",
"phone": "+50688888888"
},
"charges": [
{
"id": "clch0001",
"amount": 500000,
"baseAmount": 500000,
"baseExchangeRate": 1,
"refNumber": "123456",
"mode": "test",
"currency": "CRC",
"status": "succeeded",
"failureCode": null,
"failureMessage": null,
"isApproved": true,
"isCaptured": true,
"createdAt": "2026-04-29T16:10:00.000Z",
"updatedAt": "2026-04-29T16:10:01.000Z"
}
],
"lastPaymentError": null,
"createdAt": "2026-04-29T16:09:50.000Z",
"updatedAt": "2026-04-29T16:10:01.000Z"
}
}

payment-intent.failed

data contiene la intención de pago y un objeto error con el motivo del fallo.

{
"type": "payment-intent.failed",
"data": {
"id": "clpiment0002",
"accountId": "clacct0001",
"currency": "CRC",
"capturableAmount": 0,
"status": "requires_payment_method",
"metadata": {
"orderId": "order_1002"
},
"customer": {
"id": "clcus0002",
"name": "Luis Vega",
"email": "luis@example.com",
"phone": "+50688887777"
},
"error": {
"id": "clerr0001",
"mode": "test",
"paymentMethodType": "card",
"type": "processing_error",
"code": "declined",
"message": "Transaction declined due to test payment method used",
"createdAt": "2026-04-29T16:12:00.000Z",
"updatedAt": "2026-04-29T16:12:00.000Z"
}
}
}

payment-intent.deferred

data contiene la intención de pago en estado pendiente de confirmación externa, por ejemplo cuando ONVO espera confirmación de una transferencia.

{
"type": "payment-intent.deferred",
"data": {
"id": "clpiment0003",
"accountId": "clacct0001",
"mode": "test",
"amount": 250000,
"currency": "CRC",
"status": "processing",
"paymentMethodId": "clpm0003",
"customerId": "clcus0003",
"metadata": {
"orderId": "order_1003"
},
"createdAt": "2026-04-29T16:13:00.000Z",
"updatedAt": "2026-04-29T16:13:02.000Z"
}
}

subscription.renewal.succeeded

data contiene la renovación asociada al período cobrado. Cada renovación está ligada a un cargo recurrente y a la intención de pago creada para ese período.

{
"type": "subscription.renewal.succeeded",
"data": {
"id": "clinv0001",
"accountId": "clacct0001",
"mode": "test",
"status": "paid",
"currency": "CRC",
"attemptCount": 1,
"attempted": true,
"description": "Plan mensual",
"total": 1200000,
"subTotal": 1200000,
"originalTotal": null,
"periodStart": "2026-04-01T00:00:00.000Z",
"periodEnd": "2026-05-01T00:00:00.000Z",
"paymentIntentId": "clpiment0004",
"subscriptionId": "clsub0001",
"customerId": "clcus0004",
"metadata": {
"plan": "pro"
},
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:05.000Z"
}
}

subscription.renewal.failed

data contiene el estado de la renovación, el estado del cargo recurrente, la próxima fecha de intento y el error recibido al procesar la intención de pago.

{
"type": "subscription.renewal.failed",
"data": {
"subscriptionId": "clsub0002",
"paymentIntentId": "clpiment0005",
"currency": "CRC",
"lastPaymentAttempt": "2026-04-29T16:20:00.000Z",
"attemptCount": 2,
"invoiceStatus": "open",
"subscriptionStatus": "past_due",
"metadata": {
"accountTier": "pro"
},
"invoiceMetadata": {
"period": "2026-04"
},
"invoicePeriodStart": "2026-04-01T00:00:00.000Z",
"invoicePeriodEnd": "2026-05-01T00:00:00.000Z",
"currentPeriodStart": "2026-04-01T00:00:00.000Z",
"currentPeriodEnd": "2026-05-01T00:00:00.000Z",
"nextPaymentAttempt": "2026-04-30T16:20:00.000Z",
"customer": {
"id": "clcus0005",
"name": "María Solís",
"email": "maria@example.com",
"phone": "+50688886666"
},
"error": {
"id": "clerr0002",
"mode": "test",
"paymentMethodType": "card",
"type": "processing_error",
"code": "declined",
"message": "Transaction declined due to test payment method used",
"createdAt": "2026-04-29T16:20:00.000Z",
"updatedAt": "2026-04-29T16:20:00.000Z"
}
}
}

checkout-session.succeeded

data contiene la sesión de Checkout completada, sus ítems y el cliente tomado de la intención de pago asociada.

{
"type": "checkout-session.succeeded",
"data": {
"id": "clcs0001",
"url": "https://checkout.onvopay.com/pay/clcs0001",
"mode": "test",
"status": "complete",
"paymentStatus": "paid",
"paymentMode": "payment",
"amountSubTotal": 350000,
"amountTotal": 350000,
"currency": "CRC",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel",
"paymentIntentId": "clpiment0006",
"customerId": "clcus0006",
"metadata": {
"cartId": "cart_123"
},
"customer": {
"id": "clcus0006",
"name": "Sofía Rojas",
"email": "sofia@example.com",
"phone": "+50688885555"
},
"lineItems": [
{
"name": "Producto de prueba",
"description": "Descripción del producto",
"currency": "CRC",
"amount": 350000,
"priceId": "clprice0001"
}
],
"createdAt": "2026-04-29T16:25:00.000Z",
"updatedAt": "2026-04-29T16:25:10.000Z"
}
}

mobile-transfer.received

data contiene la información de la transferencia entrante. Este evento no confirma una intención de pago por sí solo.

{
"type": "mobile-transfer.received",
"data": {
"amount": 1450000,
"currency": "CRC",
"description": "PAGO DE SERVICIOS",
"SINPERefNumber": "2025110312774577852010",
"authorizationDate": "2026-04-29T16:30:00.000Z",
"originId": "01-1393-1919",
"originName": "JUAN PEREZ CASTRO",
"originPhone": "72940567"
}
}

Seguridad

Cada webhook tiene un secret asignado. Podés verlo en el Dashboard de ONVO junto a la configuración del webhook.

ONVO incluye ese valor en el header X-Webhook-Secret. El backend genera secrets con el prefijo webhook_secret_. Usalo para verificar que la solicitud viene de ONVO antes de procesar el evento.

X-Webhook-Secret: webhook_secret_...

Respuestas y errores

Cuando un evento representa un error, el objeto del payload puede incluir un atributo error. Ese objeto puede contener:

CampoDescripción
messageDescripción legible del error.
codeCódigo del error, cuando aplica.
typeTipo de error, cuando aplica.

Tu endpoint debe responder con un código 2xx solamente cuando el evento fue recibido y procesado correctamente. Si respondés con otro estado, ONVO registra la entrega como fallida.

Tu sistema debe tolerar eventos recibidos fuera de orden.