Cuando algo falla
La tabla que resuelve casi todas las dudas
Sección titulada «La tabla que resuelve casi todas las dudas»| Lo que ves | ¿Reintentas? | Cómo |
|---|---|---|
status: "submitted" |
No | Espera el webhook. Ya está en curso |
status: "unknown" |
NUNCA | Se resuelve solo. Reintentar aquí es como se paga dos veces |
status: "failed" + reason de datos |
Sí, tras corregir | Corrige el beneficiario y pide otra cotización |
status: "failed" + reason de riel |
Sí | Otra cotización, sin cambiar nada más |
502 provider_error |
Sí | Reintenta con la misma Idempotency-Key |
503 no_provider_available |
Sí, más tarde | No hay riel ahora mismo para ese banco/instrumento |
429 rate_limited |
Sí, más despacio | Respeta Retry-After |
4xx de validación |
No sin corregir | El mismo cuerpo va a fallar igual |
Resultado ambiguo: unknown
Sección titulada «Resultado ambiguo: unknown»Qué hacemos mientras el pago está en duda:
-
No se compensa nada. El asiento queda como está.
-
Se consulta al riel con nuestra referencia, con espera creciente.
-
Se resuelve: pagó →
settled; no reconoce la referencia tras varias consultas → se compensa y quedafailed; devuelto →reversed. -
Te avisamos por webhook. No hace falta polling.
Para vigilarlo:
curl -s "$BASE/v1/payments?status=unknown&limit=100" -H "x-api-key: $KEY"Rechazo: failed
Sección titulada «Rechazo: failed»El asiento ya está revertido y no se te ha cobrado nada (feeAmount: "0",
compensated: true). Reintenta con otra cotización en cuanto arregles la causa,
que está en reason:
reason |
De quién es el problema | Reintento útil |
|---|---|---|
beneficiary_missing_phone |
Tuyo: falta el móvil (pasa también en transferencia) | Tras cargar el phone |
beneficiary_missing_account |
Tuyo: falta la cuenta | Tras cargarla |
beneficiary_mismatch |
Tuyo: nombre o cédula no coinciden con el titular | Tras corregir. Ningún riel lo aceptaría |
invalid_destination_account |
Tuyo: la cuenta no existe o está mal | Tras corregir |
sender_required |
Tuyo: no había remitente | Mandando el bloque sender |
amount_below_rail_minimum |
Del importe: por debajo del piso del riel | Subiendo el importe |
amount_above_rail_limit |
Del importe: por encima del techo | Partiéndolo |
bank_unreachable |
Del riel: no cubre ese banco | Ya se intentó otro; revisa el catálogo del instrumento |
rail_insufficient_balance |
Del riel: sin liquidez ahora | Más tarde |
no_route |
De la combinación: ningún riel candidato | Prueba el otro instrumento |
Se cae un canal
Sección titulada «Se cae un canal»Si el instrumento que pediste no está disponible y methodPolicy es preferred
(por defecto), el pago sale por el otro:
{ "status": "submitted", "method": "transferencia", "methodRequested": "pago_movil", "methodChanged": true }El estado sigue siendo submitted: el cambio de canal ya ocurrió, pero el envío
todavía está liquidando. Con methodPolicy: "strict" la dispersión falla en vez
de cambiar de canal.
Devolución de un pago ya liquidado
Sección titulada «Devolución de un pago ya liquidado»Días después, el banco puede devolver una transferencia (cuenta cerrada, datos
incorrectos). El evento se verifica y se deduplica, se revierte el asiento —el
pago pasa a reversed y los fondos vuelven a tu saldo— y recibes
payment.reversed con el motivo que reportó el banco. Lo que te toca es avisar a
tu usuario.
Si tu receptor de webhooks se cae
Sección titulada «Si tu receptor de webhooks se cae»Reintentamos con espera creciente (5 s → 6 h, 12 intentos). Al agotarlos el evento pasa a tu cola de fallidos, consultable y reentregable.
curl -s "$BASE/v1/events?status=dead" -H "x-api-key: $KEY"curl -s -X POST "$BASE/v1/events/retry-dead" -H "x-api-key: $KEY"Ver webhooks.
Reproducir todo esto antes de producción
Sección titulada «Reproducir todo esto antes de producción»Cada caso se provoca en el entorno de
pruebas: mode: "reject" con method para
el cambio de canal, mode: "timeout_after_submit" para el pago en duda,
/sandbox/bank/payout-settled y /sandbox/bank/payout-returned para la
confirmación tardía y la devolución, y /sandbox/webhooks/failing para tus
reintentos.
curl -s -X POST $BASE/sandbox/chaos -H 'content-type: application/json' \ -d '{"mode":"timeout_after_submit"}'