Errores de intención de pago
Por qué una intención de pago termina fallida o expirada, y qué razones hereda del pago que la cerró.
Una intención de pago lleva state_reason en sus dos estados sin éxito:
failed: no queda nada por pagar. O el token de pago nunca llegó a existir, o un pago cerró la intención de pago definitivamente.expired: la ventana se cerró sin que nadie pagara.
Lees el código en los webhooks payin_intent_failed y payin_intent_expired, y en la intención de pago misma con get o list.
Ejemplo de payload del webhook
{
"timestamp": "2026-02-11T14:22:07.913204Z",
"event": {
"type": "payin_intent_failed",
"data": {
"id": "cpii_034EASMIAFVq06j2NxhnIJ",
"state": "failed",
"state_reason": "payment_token_registration_failed",
"external_id": "order-88213",
"template_id": "cpit_034EASMHlq36oZISbc9W4b"
}
}
}Las razones propias de la intención de pago
Lo que le puede pasar a una intención de pago antes de que exista un pago, más el comodín para un pago que falló sin una explicación que valga la pena darle al pagador.
| Código | Descripción | Causa | Solución sugerida |
|---|---|---|---|
expired | Ventana cerrada | Nadie pagó antes del expires_at. Para Bre-B la ventana es de 30 minutos. | Crea una intención de pago nueva. La anterior libera su external_id al expirar. |
key_registration_failed | Falló el registro de la llave | Registrar la llave de pago propia de la intención de pago en la red agotó sus reintentos. | Crea la intención de pago otra vez. Si se repite, contacta a soporte con el id de la intención de pago. |
payment_token_registration_failed | Falló la creación del token de pago | La llave se registró, pero el token de pago no pudo armarse a partir de ella. | Crea la intención de pago otra vez. Si se repite, contacta a soporte con el id de la intención de pago. |
payment_failed | El pago falló | Un pago cerró la intención de pago por una razón que no le dice nada al pagador: una falla de infraestructura como un timeout, un fallo del ledger o una caída del proveedor. | Lee el state_reason del pago para el detalle; crea una intención de pago nueva para reintentar. |
unknown | Desconocido | El pago que cerró la intención de pago falló sin una razón conocida. | Contacta a soporte con el id de la intención de pago. |
Razones heredadas del pago
Cuando un pago cierra una intención de pago por una razón sobre la que el pagador o el comercio pueden actuar, la intención de pago adopta la razón del pago con el mismo nombre en vez de aplanarla en payment_failed. Cada una significa exactamente lo mismo que en el pago, así que la tabla de allá es la referencia:
| Grupo | Códigos |
|---|---|
| Límites de monto | amount_below_minimum, amount_exceeds_maximum, amount_below_total_minimum, amount_exceeds_total_maximum, intent_amount_mismatch |
| Restricciones del pagador | payer_not_allowed |
| Banco y riesgo | bank_rejected, risk_control |
| Condiciones de la plantilla | template_paid, template_expired, template_disabled, template_deleted, template_not_found, template_invalid_state |
La división es deliberada: lo que el pagador puede corregir (el monto, no ser él quien puede pagar) y lo que el comercio necesita distinguir (de qué forma dejó de recibir pagos el cobro, o que la cuenta receptora no puede recibir dinero) conservan su nombre; las fallas de infraestructura no, porque no significan nada para quien está mirando un checkout.
Hoy no son alcanzables con Bre-B
Estas razones heredadas sólo aparecen con métodos de pago que no re-arman la intención de
pago tras un pago fallido. Bre-B, el único método disponible hoy, sí re-arma: un pago fallido
devuelve la intención de pago a ready_to_pay para que el pagador reintente con la misma
llave mientras dure la ventana, y la intención de pago no se cierra. Hasta que exista otro
método, una intención de pago que termine failed llevará una de sus razones
propias. Manéjalas de forma defensiva en vez de
esperarlas.
Un pago fallido no siempre es una intención de pago fallida
La mayoría de los pagos fallidos no cierran la intención de pago. Con un método que re-arma, la intención de pago vuelve a ready_to_pay y emite payin_intent_attempt_failed. El intento fallido lo reporta el propio webhook payin_rejected o payin_failed del pago, que lleva la razón real.
O sea, hay dos preguntas distintas y dos eventos distintos:
| Pregunta | Evento | Dónde vive la razón |
|---|---|---|
| ¿Por qué falló este intento? | payin_rejected / payin_failed | El state_reason del pago (ver Errores de pago). |
| ¿Por qué esta orden ya no puede pagarse? | payin_intent_failed / payin_intent_expired | El state_reason de la intención de pago (esta página). |
Cómo manejarlos
app.post('/webhook', (req, res) => {
const { event } = req.body;
// La orden todavía puede pagarse: el pagador sólo tiene que reintentar
if (event.type === 'payin_intent_attempt_failed') {
// Mantén el checkout abierto; la llave sigue siendo válida
}
// La orden terminó
if (event.type === 'payin_intent_failed' || event.type === 'payin_intent_expired') {
const { external_id, state_reason } = event.data;
switch (state_reason) {
case 'expired':
// Nadie pagó a tiempo: ofrece un checkout nuevo
break;
case 'key_registration_failed':
case 'payment_token_registration_failed':
// La intención de pago nunca llegó a ser pagable: créala de nuevo
break;
default:
// Un pago la cerró: lee el pago para el detalle y emite una intención de pago nueva
}
}
res.status(200).send();
});Saber si una orden fue pagada
No infieras el pago por la ausencia de una falla. GET /v1/core/payins/intents/find_successful_by?external_id=... lo responde directo: existe a lo
sumo una intención de pago successful por identificador.
Siguientes pasos
- Errores de pago: la razón detrás de cada intento fallido.
- Errores de plantilla: por qué la plantilla detrás de la intención de pago dejó de ser pagable.
- Recaudar con intenciones de pago: el flujo de punta a punta.