Saltearse al contenido

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

EventoCuándo se dispara
payment.confirmedEl pago se completó con éxito (tarjeta aprobada en Dinelco/Bancard, o transferencia aprobada).
payment.failedEl provider rechazó el pago (tarjeta inválida, fondos insuficientes, etc.).
payment.rejectedUn operador rechazó manualmente un comprobante de transferencia.
payment.expiredEl pago no se completó antes de expires_at y se marcó expirado. Ver Expiración y confirmaciones tardías.
payment.cancelledVos cancelaste el pago vía API antes de su finalización.
payment.reversedSe ejecutó una reversa del pago (Dinelco o Bancard, antes del corte de liquidación).
payment.receipt_uploadedEl cliente subió un comprobante de transferencia (pendiente de aprobación).

Eventos de tarjetas

EventoCuándo se dispara
card.registeredEl catastro fue exitoso, ya tenés tokenId para cobrar.
card.failedEl catastro falló (tarjeta rechazada, OTP incorrecto).
card.deletedSe 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,
"card": null,
"provider_data": {
"operation_number": "0012345",
"authorization_number": "123456",
"response_code": "00"
},
"confirmed_at": "2026-05-02T14:32:10+00:00",
"provider_approved_at": null,
"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"
}
CampoNotas
amountString decimal, no entero: "150000.00". Casteá antes de comparar.
environmentlive o test, según la API key con la que se creó el pago.
provider_dataDatos del proveedor para ese intento (operation_number, authorization_number, response_code, provider_status, session_id, y con Bancard ticket_number y response_description). Solo trae las claves con valor. Qué significa cada una: datos del proveedor.
rejection_reasonEn payment.rejected, el motivo del rechazo manual del comprobante. En payment.failed, siempre presente: en un rechazo de tarjeta, "<código>: <texto del proveedor>" (o solo el texto si el proveedor no manda código); si cerramos el pago porque el proveedor nunca informó el resultado, el texto lo explica.
cardCon qué tarjeta se pagó: { "type": "credit"|"debit", "brand", "masked_number" }. null en transferencias. Con Bancard solo viene type, y credit puede ser un débito no procesado por Bancard. Ver tarjeta usada.
confirmed_atCuándo confirmamos nosotros el pago (y salió este evento).
provider_approved_atCuándo aprobó el proveedor la transacción, según el proveedor. Dinelco lo informa; con Bancard y transferencia es null. Ver abajo.
expires_atEl plazo del intento: hasta cuándo lo aceptamos. null si el pago no vence.
expired_atCuándo lo marcamos expirado. Ojo con el par de nombres: uno es el plazo, el otro el hecho consumado.
confirmed_after_expiryVer abajo.
recovered_from_statusVer abajo.
timestampMomento 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": "445653******1096",
"brand": "visa",
"card_type": "credit",
"expiration": null,
"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: la página hospedada y la del checkout_url sacan el formulario de tarjeta aunque el comprador lo tenga abierto, negamos la carga de nuevos iframes y rechazamos nuestros propios endpoints. Cuando eso pasa recibís payment.expired, con expired_at poblado.

Hay otro caso que llega igual de tarde: el comprador pagó a tiempo, pero el aviso del proveedor se perdió o se demoró, y nos enteramos (consultando al proveedor) después del plazo.

En los dos casos te llega un payment.confirmed normal, con estos campos para distinguirlo:

  • confirmed_after_expiry — true si nosotros confirmamos el pago después de su expires_at, o si se rescató de un estado no abierto. Es decir: te estás enterando fuera de la ventana.
  • recovered_from_status — el estado del que se lo rescató (expired, cancelled, failed), o null si nunca te habíamos dicho que estaba cerrado.
  • provider_approved_at — cuándo aprobó el proveedor, según el proveedor. Te dice si el comprador pagó a tiempo aunque te enteres tarde. Dinelco lo informa; Bancard no (viene null).
{
"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, y de cuándo liberás la reserva:

  • Si liberás el stock o el cupo con tu propio reloj al llegar expires_at: un confirmed_after_expiry: true significa que el pago te llega fuera de la ventana que reservaste, y el cupo pudo haberse revendido. Mandalo a revisión manual en vez de despachar automático. Si provider_approved_at es anterior a expires_at, el comprador pagó a tiempo y el aviso llegó tarde: ese dato te sirve para decidir con quién queda el lugar. Si ya no lo podés honrar, revertí el pago.
  • Si liberás solo cuando te llega nuestro payment.expired: mirá recovered_from_status. Si es null, nunca te dijimos que el pago había vencido, así que no liberaste nada y lo podés despachar normal. Si es expired o cancelled, ya lo habías cerrado: revisión manual.

Antes de mandarte payment.expired le preguntamos al proveedor cómo terminó el pago, así que un pago aprobado en el borde del plazo te llega como payment.confirmed, no como vencido.

Ejemplo: el comprador pagó a las 14:44 con un plazo hasta las 14:45, el aviso del proveedor se perdió y lo confirmamos a las 14:46:

{
"event": "payment.confirmed",
"payment_id": "pay_abc123def456",
"status": "confirmed",
"expires_at": "2026-05-02T14:45:00+00:00",
"confirmed_at": "2026-05-02T14:46:05+00:00",
"provider_approved_at": "2026-05-02T14:44:02+00:00",
"confirmed_after_expiry": true,
"recovered_from_status": null
}

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.failed alcanzan.
  • Si aceptás transferencias: sumá payment.receipt_uploaded para enterarte cuando llegan, payment.rejected para los rechazos manuales.
  • Si trabajás con tarjetas guardadas: sumá card.registered y card.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.