Saltar al contenido principal

Solicitud

La Solicitud es la sesión para cuando el pago tiene que esperar: el enlace sigue vivo hasta una fecha que pones tú, así que puedes mandarlo por WhatsApp, por correo o como un QR y que lo paguen mañana. Se crean por lote, porque cuando mandas facturas normalmente no mandas una sola. → Las formas de recibir un pago

Una Solicitud no se marca failed

Solo tiene dos finales: o se paga (paid) o vence (expired). Si tu código reacciona a failed, esa rama nunca se ejecuta aquí. → Ciclo de vida.

Solicitud de pago: cuando el pago tiene que esperarGuion: .docx · .yaml

Crear un Lote de Solicitudes​

curl -X POST https://devsim.mispidi.com/api/v1/ext/payment-sessions/request/batch \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"continue_on_error": true,
"items": [{
"title": "Pago de Rafael",
"currency_reference": "USD",
"amount_reference": 100,
"agreement_id": "<agreement_id>",
"identifier_label": "Nombre Cliente",
"identifier": "Rafael",
"description": "Pago de servicio",
"due_date_session": "2026-12-31T23:59:59Z",
"due_date_reached_behavior": "keep_active",
"late_notice_message": "Tu enlace de pago está por vencer",
"internal_reference": "REF-0001",
"success_url": "https://miapp.com/pago-exitoso",
"failure_url": "https://miapp.com/pago-fallido",
"webhook_url": "https://miapp.com/webhooks/spidi"
}]
}'
  • continue_on_error — si un ítem del lote falla, sigue con los demás.
  • items — una entrada por solicitud; cada una nace pending con su payment_url.
  • due_date_session / due_date_reached_behavior — hasta cuándo vale el enlace, y qué pasa al llegar la fecha:
due_date_reached_behaviorQué pasa al pasar la fecha
expireEl enlace deja de funcionar
keep_activeEl enlace sigue activo y se muestra el mensaje de aviso
Manda siempre el comportamiento, no te apoyes en el defecto

Si envías due_date_session sin due_date_reached_behavior, estás dejando en manos del defecto algo que tiene dos desenlaces opuestos: un enlace que muere solo o uno que sigue admitiendo pagos pasada la fecha.

Escríbelo explícito en cada sesión. Son cuatro caracteres de más en el cuerpo y te ahorran la clase de sorpresa que solo aparece cuando ya hay dinero de por medio.

Quien paga lo ve vencido; tu sistema no, hasta que preguntes

Son dos cosas distintas y conviene no mezclarlas:

  • Para quien abre el enlace, la fecha basta. Pasada la fecha con expire, la página muestra «Esta sesión ya no está disponible» y no deja pagar. Nadie tiene que hacer nada para que eso ocurra. (Comprobado en el sandbox el 9-sep-2026.)
  • Para tu sistema, no. Si das por válido un enlace sin consultar GET /api/v1/ext/payment-sessions/status/<session_id>, vas a seguir tratándolo como si aún admitiera un pago — y va a parecer que due_date_session no funciona, cuando lo que falta es la consulta.

Consulta el estado antes de dar por bueno un enlace. Es el mismo hábito que te salva en Success URL, aquí por un segundo motivo.

Pruébalo tú mismo, y ojo con la fecha que usas

En el sandbox de SPIDI, la fecha tiene que estar en el futuro. Una due_date_session ya pasada se rechaza al crear la sesión, con 400 y "Due date must be in the future". Así que la receta que funciona es: pon el vencimiento a un minuto de ahora, espera a que pase, y consulta.

# due_date_session: dentro de ~60 s · due_date_reached_behavior: "expire"
curl https://sandbox.api.spidipagos.com/api/v1/ext/payment-sessions/status/<session_id> \
-H "Authorization: Bearer <token>"
# antes de la fecha -> { "data": { "status": "pending" } }
# después -> { "data": { "status": "expired" } } y user_message: "La sesión expiró…"

Con keep_active seguirá pending pasada la fecha, que es justo la diferencia.

En el simulador sí puedes usar una fecha en el pasado y ver el expired en la misma consulta, sin esperar. Es una diferencia deliberada para que probar no cueste un minuto de reloj, y está declarada en /fidelidad.

Y fíjate en lo que no pasa: hasta que no consultas, tu sistema no sabe nada. Esa es la lección, no un detalle del entorno.

  • late_notice_message — texto del aviso cuando el enlace está por vencer (obligatorio).
  • internal_reference — tu referencia interna de la solicitud (obligatorio).

La respuesta resume el lote: data.processed_count, data.successful_count y data.failed_count, un arreglo data.items[] (una sesión creada por ítem, cada una con su session_id y su payment_url) y data.errors[] con los ítems que no se pudieron crear.

Confirmar el Pago​

Igual que el Botón: confirma con GET status y/o el webhook payment_session.paid. → Success URL: verificar status · Manejar notificaciones.

paid no es el dinero acreditado

paid confirma el débito (fase 1) — no que el dinero ya está en manos del receptor. La acreditación (fase 2) no cambia el status: te llega por el webhook payment_session.accredited, o la consultas en data.session_payment.receiver_credits, en la misma respuesta del status. → Transacción a dos fases.

Probar en el Simulador​

# fuerza el pago de una sesión del lote (su session_id sale en data.items[].session_id)
curl -X POST https://devsim.mispidi.com/control/sessions/<session_id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"leg":1,"outcome":"paid"}'

Este flujo (crear lote → pending → paid) está verificado contra el simulador.

→ Usar el simulador