Saltar al contenido principal

Transacción a dos fases

Los números de esta página
QuéCuántoDe quién
acreditación real, tras el pago50–103 sSPIDI
retardo de acreditación en el simulador5000 msSimulador
tiempo máximo de respuesta de tu receptor5 sSPIDI
reintentos de SPIDI1m → 5m → 15m → 60mSPIDI

Una transacción con SPIDI ocurre en dos fases. Entender eso es lo que separa una integración que funciona de una que parece funcionar.

Transacción a dos fasesGuion: .docx · .yaml

Las Dos Fases​

Fase 1 — El Pago​

Tu cliente paga y el dinero sale de sus manos. La sesión pasa de pending a uno de tres desenlaces:

  • paid — el pago salió bien.
  • failed — fue rechazado (solo aplica al Botón).
  • expired — la sesión venció sin pagarse.

Se emite el webhook payment_session.paid.

Dónde está el dinero en este punto, que es lo que hay que retener: no queda en poder de SPIDI. Entra en una cuenta propia del banco, y el banco es su custodio. Tu cliente ya no lo controla y no puede echarse atrás.

Fase 2 — La Acreditación​

El dinero se mueve desde esa cuenta del banco hasta la tuya — o hasta tantas cuentas como hayas pedido que se reparta, si el acuerdo por el que lo enrutaste se bifurca.

Aquí se aplica la comisión del banco, y por eso lo que se acredita es el monto neto. Se emite el webhook payment_session.accredited.

Un aviso de acreditación por cada parte del acuerdo

Llega un evento por cada transacción de crédito, uno por cada parte del acuerdo: sin split, un solo payment_session.accredited; con split, una acreditación por receptor más el cierre — todas a tu mismo webhook_url. → Split.

Puedes Entregar en paid​

No esperes a la acreditación para habilitar tu servicio o despachar tu producto. En cuanto la sesión llega a paid, puedes entregar.

Y el motivo es el mecanismo de arriba, no una promesa: el dinero está en un banco y el banco lo custodia. paid representa una garantía de derecho irrevocable sobre él. La fase 2 no decide si el dinero es tuyo, sino cuándo lo ves en tu cuenta — es una liquidación entre el banco y tú, no un riesgo que corras con tu cliente.

La acreditación sirve para otra cosa, y también la necesitas:

Para estoUsa
Decidir si entregaspaid (fase 1)
Cuadrar contra tu estado de cuentaaccredited (fase 2) y su bank_reference_id — compuesto: solo la parte a la izquierda del | casa con el banco

Cuánto Tarda la Fase 2​

Entre uno y dos minutos, no segundos. Medido en transacciones reales: el crédito entra entre 50 y 103 segundos después del pago, y el aviso llega unos 20 segundos después de que el dinero ya está.

Eso tiene dos consecuencias directas para tu código:

  • Consultar el estado justo después de pagar devuelve receiver_credits: null. No es un fallo — es que la fase 2 todavía no ocurrió. Si sondeas, insiste.
  • En un split, los receptores no reciben a la vez. En la medición, el partner recibió su parte 12 segundos antes que el owner. No asumas un orden ni simultaneidad.
En el simulador son 5 segundos

Está puesto así a propósito, para que la secuencia completa se vea en una sola pantalla. Es didáctico, no realista: no escribas lógica que dependa de que la acreditación llegue rápido.

Cómo Te Enteras de Cada Fase​

La sesión es el registro de la transacción, y las dos fases se ven ahí. Una sola consulta te da el estado completo:

GET /api/v1/ext/payment-sessions/status/{session_id}
→ data.status ← la fase 1
→ data.session_payment.receiver_credits ← la fase 2
→ data.session_payment.receiver_credits_summary

El aviso no es la única fuente: es el atajo. Te avisa en cuanto ocurre, y así no tienes que preguntar. Eso es todo lo que hace de más.

El status nunca refleja la fase 2

Tras paid, el status se queda en paid — la acreditación no lo cambia. No existe un estado accredited: si sondeas esperándolo, esperas para siempre.

Lo que cambia no es el status, sino que receiver_credits deja de venir null. Es otro bucle y otra condición de salida.

Y por eso importa que la sesión lo tenga todo. Si tu servidor estuvo caído, reintentamos — y luego se rinde, sin reenvío. Cuando vuelvas no habrá un aviso esperándote: habrá una sesión que puedes consultar.

Guarda el session_id de cada transacción

Es tu única llave de vuelta: no hay forma de listar tus sesiones. Si lo pierdes, esa transacción es irrecuperable por API.

Las Dos Trampas de Forma​

El mismo dato viaja por los dos canales, y no viene igual.

La que se ve. receiver_credits cuelga de sitios distintos:

CanalDónde está
Webhookdata.receiver_credits
GET statusdata.session_payment.receiver_credits

Un parser escrito para uno devuelve null con el otro. Molesta, pero la ves.

La que NO se ve: el identificador del receptor cambia de valor

split_recipient_agreement_id —el campo natural para cruzar un aviso con su receptor— devuelve dos identificadores distintos del mismo objeto según de dónde lo leas: un UUID en el webhook, el rcv_… que tú enviaste en el GET status.

Cruzar por él falla en silencio: no da error, concilia mal y no avisa. Si lees las dos fuentes, normaliza antes de cruzar.

Antes de Dar por Conciliado un Split​

Cuadra el número de créditos contra los receptores que pediste

receiver_credits_summary.total_credits debe coincidir con el número de receptores del reparto más el owner. Si pediste repartir a un partner y ves total_credits: 1, no concilies: el dinero puede haberse movido igual y estarías a un paso de pagarle dos veces a alguien que ya recibió su parte.

Es una comprobación de una línea y es la que detecta el caso malo.

Pruébalo​

En el simulador puedes forzar cada fase por separado y ver los dos webhooks:

# fase 1 (el pago)
curl -X POST https://sim.mispidi.com/control/sessions/<id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" -d '{"leg":1,"outcome":"paid"}'
# fase 2 (la acreditación)
curl -X POST https://sim.mispidi.com/control/sessions/<id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" -d '{"leg":2,"outcome":"accredited"}'

El campo se llama leg en el plano de control: es el nombre del API para lo que aquí llamamos fase.

→ Usar el simulador · Manejar notificaciones