Skip to navigation

Migrar a Webhooks Unificados (intent.*)

Recurrente unificó todos los eventos de pago en un solo formato: intent.*. En lugar de un evento distinto por método de pago (payment_intent.*, bank_transfer_intent.*, etc.), recibís un único evento intent.* con una estructura común, discriminada por el campo type. Escribís un handler en vez de uno por tipo.

Las cuentas existentes conservan su contrato. Esta guía es para cuentas que reciben ambos formatos y quieren migrar su integración. Las cuentas nuevas reciben solo el unificado cuando hay reemplazo; no necesitan dar de baja el duplicado legacy.

Para reconocer cobros guardados antes de la migración, compara legacy_id con el ID anterior por tipo. Una vez vinculados, id y receipt_number identifican el mismo cobro en ambos formatos. Conserva también el tipo de evento: un cambio de estado posterior no es un duplicado.

Eventos nuevos a configurar

Suscribí tu endpoint a estos eventos desde tu dashboard de Webhooks:

EventoCuándo se emite
intent.succeededCobro exitoso (cualquier método de pago)
intent.pendingCobro iniciado o pendiente (transferencia, cripto)
intent.failedCobro fallido
intent.canceledIntent cancelado
intent.paidSolo para pagos con balance: le llega a la cuenta pagadora

Cómo funciona

  1. Para las cuentas existentes, Recurrente sigue enviando los dos formatos por cada pago compatible: el legacy por tipo (payment_intent.*, etc.) y el unificado (intent.*), como entregas separadas.
  2. En tu dashboard de Webhooks suscribís tu endpoint a los intent.* que te interesen.
  3. Tu handler ramifica según el campo type y procesa.

Si tu cuenta conserva ambos formatos y tu endpoint no filtra por tipo de evento, recibirá tanto los payment_intent.* como los intent.* para el mismo pago. Para no procesar el mismo cobro dos veces, suscribite a uno solo de los dos formatos (o chequeá el campo event_type en tu handler).

El payload unificado

{
"type": "payment", // payment | bank_transfer | crypto | balance | cash
"event_type": "intent.succeeded",
"id": "in_8c3a1f20",
"status": "succeeded", // pending | succeeded | failed | canceled | paid
"raw_status": "succeeded", // el estado concreto, sin normalizar
"amount_in_cents": 25000,
"currency": "GTQ",
"customer": { "id": "cus_...", "email": "...", "nit": "CF" },
"product": { "id": "prod_..." },
"checkout": { "...": "..." },
"payment": { "...": "..." },
"details": { "fee": 875, "installments": 1, "channel": "Tarjeta", "...": "..." }
}

El envelope y el núcleo común son idénticos para los cinco tipos; solo cambia type y el contenido de details. Ver la estructura completa en la documentación de Webhooks.

Mapeo: legacy → unificado

Evento legacyEvento unificadotype
payment_intent.succeededintent.succeededpayment
payment_intent.failedintent.failedpayment
payment_intent.requires_capture / requires_verificationintent.pendingpayment
bank_transfer_intent.{pending,succeeded,failed}intent.{pending,succeeded,failed}bank_transfer
crypto_intent.{pending,succeeded,failed}intent.{pending,succeeded,failed}crypto
balance_intent.succeededintent.succeededbalance
balance_intent.paidintent.paidbalance
cash_intent.{succeeded,failed,canceled}intent.{succeeded,failed,canceled}cash

Los estados intermedios de tarjeta (requires_capture, requires_verification) se normalizan a pending. El estado concreto siempre está disponible en raw_status.

Pasos para migrar

  1. Revisá el payload nuevo. Mirá la estructura en la documentación de Webhooks, o consultá un pago real con GET /api/intents/{id} (devuelve el mismo formato que el webhook).
  2. Actualizá tu handler para consumir intent.* y ramificar por type:
    switch (event.type) {
    case "payment": /* event.details.fee, .installments, ... */ break
    case "bank_transfer": /* event.details.bank_reference, .sender_comment */ break
    case "crypto":
    case "balance":
    case "cash": break
    }
    // el estado siempre en event.status: pending | succeeded | failed | canceled | paid
  3. Suscribí tu endpoint a los intent.* desde el dashboard de Webhooks.
  4. (Opcional) Dá de baja los eventos por tipo. Cuando tu handler unificado funcione, desuscribite de los payment_intent.*, bank_transfer_intent.*, etc., para dejar de recibir el formato duplicado.

Eventos que no cambian

subscription.*, refund.*, dispute.*, automated_bank_transfer_intent.* y setup_intent.* siguen exactamente igual — no son parte del recurso Intent unificado.