Saltar al contenido principal

Visión General de Estados

Un pago avanza por varias fases desde su creación hasta la liquidación final. Cada fase tiene tres estados — Pendiente, En Procesamiento y Completado — ofreciéndote visibilidad granular en cada etapa del pipeline.

Flujo de Estados

Durante la fase de Cash In, FAILED y ERROR son estados terminales — no se recolectaron fondos, por lo que no se emite ningún reembolso. Para todas las fases siguientes (KYT Out, Cash Out), FAILED y ERROR son transitorios: el pago transiciona al flujo de reembolso (REFUND_PENDINGREFUND_PROCESSINGREFUNDED). REJECTED también activa el flujo de reembolso, ya que solo ocurre después del Cash In.

Descripción de Estados

EstadoFaseDescripciónMensaje Sugerido
CREATEDInicialPago recibido y persistido, en espera de entrar a la cola”Pago enviado”
CASH_IN_PENDINGCash InColección de fondos entrantes en cola”Procesando pago”
CASH_IN_PROCESSINGCash InRecolectando fondos del saldo pre-financiado”Recolectando fondos”
CASH_IN_COMPLETEDCash InFondos recolectados exitosamente”Fondos recolectados”
KYT_OUT_PENDINGKYT OutTransacción saliente en cola para verificación de cumplimiento”Verificación de salida pendiente”
KYT_OUT_PROCESSINGKYT OutRevisión de cumplimiento en progreso (saliente)“Verificación de salida en progreso”
KYT_OUT_COMPLETEDKYT OutVerificación de cumplimiento saliente aprobada”Listo para transferir”
CASH_OUT_PENDINGCash OutDispersión al beneficiario en cola”Transferencia en cola”
CASH_OUT_PROCESSINGCash OutFondos enviados al proveedor de pagos”Transferencia en progreso”
CASH_OUT_COMPLETEDCash OutProveedor de pagos confirmó la entrega”Transferencia entregada”
COMPLETEDTerminalPago totalmente liquidado”¡Pago entregado!”
REVIEW_NEEDEDRevisiónTransacción marcada para revisión manual de cumplimiento”En revisión — te notificaremos”
FAILEDEstado de ErrorOperación fallida. Terminal durante Cash In (no se recolectaron fondos). Inicia flujo de reembolso si ocurre en fase posterior.”Transferencia fallida — verifica datos del beneficiario”
REJECTEDEstado de ErrorPago bloqueado por revisión de cumplimiento. Siempre activa el flujo de reembolso, ya que solo ocurre después del Cash In.”Transferencia rechazada — problema de cumplimiento”
ERROREstado de ErrorError interno del sistema. Terminal durante Cash In (no se recolectaron fondos). Inicia flujo de reembolso si ocurre en fase posterior.”Error interno — contacta soporte”
REFUND_PENDINGReembolsoReembolso al saldo pre-financiado en cola”Reembolso iniciado”
REFUND_PROCESSINGReembolsoReembolso en progreso”Reembolso en progreso”
REFUNDEDTerminalFondos devueltos al saldo pre-financiado”Fondos reembolsados al saldo”

Obtener Estado del Pago

GET /api/v2/payouts/{id}
const obtenerEstadoPago = async (payoutId) => {
  const response = await fetch(
    `https://sandbox.killb.app/api/v2/payouts/${payoutId}`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );

  const pago = await response.json();
  return pago.status;
};

Polling de Estado

Usa polling como respaldo cuando los webhooks no estén disponibles:
const esperarPago = async (payoutId, timeoutMs = 300000) => {
  const estadosTerminales = ['COMPLETED', 'FAILED', 'REJECTED', 'ERROR', 'REFUNDED'];
  const inicio = Date.now();

  while (Date.now() - inicio < timeoutMs) {
    const response = await fetch(
      `https://sandbox.killb.app/api/v2/payouts/${payoutId}`,
      { headers: { 'Authorization': `Bearer ${token}` } }
    );

    const pago = await response.json();
    console.log('Estado:', pago.status);

    if (estadosTerminales.includes(pago.status)) {
      return pago;
    }

    // Esperar 15 segundos antes del siguiente check
    await new Promise(resolve => setTimeout(resolve, 15000));
  }

  throw new Error(`El pago ${payoutId} no se liquidó dentro del tiempo límite`);
};

const resultado = await esperarPago('payout-abc123');

if (resultado.status === 'COMPLETED') {
  console.log('Fondos entregados exitosamente');
} else {
  console.error('El pago terminó con estado:', resultado.status);
}
Usa webhooks como método de notificación principal. El polling cada 15–30 segundos es apropiado como respaldo.

Webhooks para Actualizaciones de Estado

Suscríbete al tipo de evento PAYOUT para recibir notificaciones en tiempo real en cada cambio de estado:
// Suscribirse a eventos de pago
await fetch('/api/v2/webhooks', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://tu-app.com/webhooks/killb',
    secret: 'tu-secreto-webhook',
    events: ['PAYOUT'],
    active: true
  })
});
Maneja los eventos webhook de pago entrantes:
app.post('/webhooks/killb', (req, res) => {
  const { event, data } = req.body;

  switch (event) {
    // Eventos de progreso de fase — actualiza el indicador de estado en tu interfaz
    case 'payout.cash_in_pending':
    case 'payout.cash_in_processing':
    case 'payout.cash_in_completed':
    case 'payout.kyt_out_pending':
    case 'payout.kyt_out_processing':
    case 'payout.kyt_out_completed':
    case 'payout.cash_out_pending':
    case 'payout.cash_out_processing':
    case 'payout.cash_out_completed':
    case 'payout.refund_pending':
    case 'payout.refund_processing': {
      const status = event.replace('payout.', '').toUpperCase();
      actualizarEstadoPago(data.id, status);
      break;
    }

    case 'payout.completed':
      actualizarEstadoPago(data.id, 'COMPLETED');
      notificarDestinatario(data);
      break;

    case 'payout.review_needed':
      // Transacción marcada para revisión manual — no se requiere acción
      actualizarEstadoPago(data.id, 'REVIEW_NEEDED');
      alertarEquipoCumplimiento(data);
      break;

    case 'payout.error':
      // Error interno del sistema — no se dispersaron fondos
      actualizarEstadoPago(data.id, 'ERROR');
      alertarEquipoOps(data);
      break;

    case 'payout.failed':
      // Datos de cuenta del beneficiario inválidos o incorrectos
      actualizarEstadoPago(data.id, 'FAILED');
      notificarBeneficiarioInvalido(data);
      break;

    case 'payout.rejected':
      // Bloqueado por revisión de cumplimiento
      actualizarEstadoPago(data.id, 'REJECTED');
      escalarACumplimiento(data);
      break;

    case 'payout.refunded':
      // Fondos devueltos al saldo pre-financiado
      actualizarEstadoPago(data.id, 'REFUNDED');
      reconciliarSaldo(data);
      break;
  }

  res.status(200).json({ received: true });
});

Mejores Prácticas

Siempre configura un endpoint webhook para eventos PAYOUT. Esto te da actualizaciones instantáneas de estado sin sobrecarga de polling y reduce llamadas innecesarias a la API.
Si falla la entrega de tu webhook, consulta GET /api/v2/payouts/{id} cada 15–30 segundos. Establece un timeout razonable (ej. 5 minutos) y alerta a tu equipo si un pago permanece en cualquier estado de procesamiento más tiempo del esperado.
Cada estado de fallo tiene una causa distinta. La fase en que ocurre FAILED o ERROR determina si se emite un reembolso:
  • FAILED / ERROR durante Cash In — Terminal. No se recolectaron fondos, por lo que no se emite reembolso. Para ERROR, contacta soporte si recurre.
  • FAILED / ERROR después de Cash In (KYT Out, Cash Out) — Transitorio. Como los fondos ya fueron recolectados, el pago transiciona automáticamente a REFUND_PENDINGREFUND_PROCESSINGREFUNDED.
  • REJECTED — Bloqueado por revisión de cumplimiento. No reintentar automáticamente; escalar internamente. Siempre activa el flujo de reembolso ya que los fondos fueron recolectados.
  • REVIEW_NEEDED — Una verificación KYT marcó la transacción para revisión manual. El pago se reanuda automáticamente o transiciona a REJECTED; no se requiere acción de tu parte.
Obtén todos los pagos del día anterior vía GET /api/v2/payouts y reconcilia con tu libro mayor interno. Compara los estados terminales esperados vs. reales para detectar discrepancias temprano.
const { payouts } = await listarPagos({ limit: 100 });
const necesitanAtencion = payouts.filter(p =>
  ['ERROR', 'FAILED', 'REJECTED'].includes(p.status)
);

if (necesitanAtencion.length > 0) {
  console.warn(`${necesitanAtencion.length} pagos necesitan atención`, necesitanAtencion);
}

Próximos Pasos

Configurar Webhooks

Configura y asegura las notificaciones webhook

Manejo de Errores

Gestiona fallos de API con elegancia