Ciclo de vida de la sesión
Una sesión nace en pending y termina de una de tres formas: se paga, se rechaza o se vence. Saber cuál es cuál es lo que te dice cuándo confirmar y qué esperar.
Los cuatro estados
| Estado | Qué significa | Cómo se llega |
|---|---|---|
pending | Sesión creada, esperando pago | al crear la sesión |
paid | El débito salió bien (fase 1) | la persona paga |
failed | El pago fue rechazado (solo Botón) | intento fallido |
expired | La sesión venció sin pagarse | se acaba el plazo, o la expiras tú |
failed solo le pasa a un Botón
El contrato lo dice sin rodeos: «el valor failed solo se aplica para sesiones creadas con botón de pago». Una Solicitud tiene dos finales, no tres: o se paga, o vence.
Lo que eso te cambia: si tu código decide algo mirando failed —marcar la factura como fallida, avisar a alguien, reintentar— ese camino nunca se ejecuta para las Solicitudes. Lo que tienes que vigilar ahí es expired.
Y dentro de una Parada pasa lo mismo: al listar sus sesiones, el contrato solo devuelve pending, paid y expired.
De qué depende el vencimiento
Lo fija la forma de la sesión, que es la decisión de duración que ya tomaste al crearla:
| Vence cuando | |
|---|---|
| Botón | se acaban sus duration_minutes |
| Solicitud | pasa su due_date_session |
| Cualquiera de las dos | la expiras tú a propósito, con expirePaymentSession |
expired se refleja de verdad en GET status. No se queda en pending esperando a que preguntes. → Las formas de recibir un pago
La acreditación no es un estado
Y esto es lo que rompe más integraciones que ninguna otra cosa de esta página.
Tras paid, la acreditación (fase 2) ocurre —el dinero se mueve a tu cuenta— pero el status se queda en paid. No existe un estado accredited: si sondeas esperándolo, esperas para siempre.
Te enteras por el webhook payment_session.accredited, o consultando data.session_payment.receiver_credits en la misma respuesta del status, que trae el desglose y el bank_reference_id (compuesto: <referencia>|<comisión>).
Compruébalo en el simulador
Puedes forzar cada desenlace y verlo reflejado en el status:
# paid | failed | expired
curl -X POST https://sim-productos.abiertolab.com/control/sessions/<id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"leg":1,"outcome":"expired"}'
curl https://sim-productos.abiertolab.com/api/v1/ext/payment-sessions/status/<id> \
-H "Authorization: Bearer <token>" # -> { "data": { "status": "expired" } }