Eventos disponibles
Cada evento tiene un tipo que llega en el header X-Ingalca-Event-Type y en la clave event del body. Suscribite al que necesites filtrando en tu handler.
Eventos de pagos
| Evento | Cuándo se dispara |
|---|---|
payment.confirmed | El pago se completó con éxito (tarjeta aprobada en Dinelco/Bancard, o transferencia aprobada). |
payment.failed | El provider rechazó el pago (tarjeta inválida, fondos insuficientes, etc.). |
payment.rejected | Un operador rechazó manualmente un comprobante de transferencia. |
payment.expired | El pago no se completó antes de expires_at y se marcó expirado. Ver Expiración y confirmaciones tardías. |
payment.cancelled | Vos cancelaste el pago vía API antes de su finalización. |
payment.reversed | Se ejecutó una reversa del pago (Dinelco o Bancard, antes del corte de liquidación). |
payment.receipt_uploaded | El cliente subió un comprobante de transferencia (pendiente de aprobación). |
Eventos de tarjetas
| Evento | Cuándo se dispara |
|---|---|
card.registered | El catastro fue exitoso, ya tenés tokenId para cobrar. |
card.failed | El catastro falló (tarjeta rechazada, OTP incorrecto). |
card.deleted | Se eliminó una tarjeta: por DELETE /v1/cards/{id} (borrado lógico) o por reemplazo automático al re-catastrar la misma tarjeta. |
Estructura del body
Payload por evento
payment.*
{ "event": "payment.confirmed", "payment_id": "pay_abc123def456", "reference": "ORDEN-001", "checkout_session_id": null, "provider": "transferencia", "payment_method": "transferencia", "environment": "live", "status": "confirmed", "amount": "150000.00", "currency": "PYG", "customer_id": "user_42", "metadata": { "order_id": 1042 }, "rejection_reason": null, "provider_data": { "operation_number": "0012345", "authorization_number": "123456", "response_code": "00" }, "confirmed_at": "2026-05-02T14:32:10+00:00", "failed_at": null, "expires_at": "2026-05-02T14:50:00+00:00", "expired_at": null, "confirmed_after_expiry": false, "recovered_from_status": null, "created_at": "2026-05-02T14:30:00+00:00", "timestamp": "2026-05-02T14:32:11+00:00"}| Campo | Notas |
|---|---|
amount | String decimal, no entero: "150000.00". Casteá antes de comparar. |
environment | live o test, según la API key con la que se creó el pago. |
provider_data | Datos del proveedor para ese intento (operation_number, authorization_number, response_code, provider_status, session_id). Solo trae las claves con valor. |
rejection_reason | En payment.rejected, el motivo del rechazo manual del comprobante. En declines de tarjeta, el código + detalle del proveedor. |
expires_at | El plazo del intento: hasta cuándo lo aceptamos. null si el pago no vence. |
expired_at | Cuándo lo marcamos expirado. Ojo con el par de nombres: uno es el plazo, el otro el hecho consumado. |
confirmed_after_expiry | Ver abajo. |
recovered_from_status | Ver abajo. |
timestamp | Momento del envío de este intento de entrega, no del evento. |
Si el pago nació de un checkout hospedado, checkout_session_id trae el cs_... de la sesión, payment_method el método usado, y reference es el de tu orden (el de la sesión) — no el interno del intento. Una sesión puede generar varios payment_id; conciliá por reference y deduplicá por X-Ingalca-Event-Id.
Para payment.reversed, vas a recibir el evento aún cuando la reversa la disparaste vos por API — el webhook confirma la transición de estado.
card.*
{ "event": "card.registered", "card_id": "card_xyz789", "customer_id": "user_42", "provider": "dinelco", "status": "registered", "masked_number": "411111******1096", "brand": "Visa", "card_type": "credit", "expiration": "12/30", "created_at": "2026-05-02T14:32:10+00:00", "last_synced_at": "2026-05-02T14:32:10+00:00", "timestamp": "2026-05-02T14:32:11+00:00"}Expiración y confirmaciones tardías
Un pago con expires_at deja de ofrecerse cuando vence: cerramos la página hospedada, negamos la carga de nuevos iframes y rechazamos nuestros propios endpoints. Cuando eso pasa recibís payment.expired, con expired_at poblado.
Cuando una confirmación aterriza sobre un pago que ya habíamos dado por perdido, llega un payment.confirmed normal con dos campos que lo distinguen:
confirmed_after_expiry—truesi el pago se confirmó después de suexpires_at, o si se rescató de un estado no abierto.recovered_from_status— el estado del que se lo rescató (expired,cancelled,failed), onullsi venía en curso normalmente.
{ "event": "payment.confirmed", "payment_id": "pay_abc123def456", "reference": "ORDEN-1042", "status": "confirmed", "expires_at": "2026-05-02T14:50:00+00:00", "expired_at": null, "confirmed_after_expiry": true, "recovered_from_status": "expired"}Qué hacer con eso depende de tu negocio. Si tenés stock, cupo o precio con vigencia, un confirmed_after_expiry: true significa que el pago entró fuera de la ventana que reservaste: el cupo pudo haberse liberado y revendido, o el precio pudo haber cambiado. Lo habitual es rutear esos casos a revisión manual en vez de despachar automático, y revertir el pago si ya no lo podés honrar.
Si tu flujo no tiene reservas por tiempo, podés ignorar los dos campos y tratarlo como cualquier payment.confirmed.
Cómo elegir qué escuchar
- Si solo procesás pagos one-shot:
payment.confirmed+payment.failedalcanzan. - Si aceptás transferencias: sumá
payment.receipt_uploadedpara enterarte cuando llegan,payment.rejectedpara los rechazos manuales. - Si trabajás con tarjetas guardadas: sumá
card.registeredycard.failed. - Si tenés política de devoluciones: sumá
payment.reversed.
No hay forma (todavía) de filtrar qué eventos te llegan. Recibís todos y filtrás del lado tuyo. Si hay demanda, agregamos suscripciones por tipo.