Saltar al contenido principal

Webhooks: cómo te avisa SPIDI

Un pago ocurre en dos fases, y las dos te importan. Un webhook es cómo te enteras de cada una sin tener que preguntar: un aviso que SPIDI te envía por POST en cuanto algo pasa, sin depender de que la persona vuelva a tu sitio.

Qué es vs cómo se maneja

Esta página explica qué son los avisos y cuáles existen. Para implementarlos —verificar la firma, deduplicar, responder— ve a → Manejar notificaciones.

Webhooks: cómo te avisa SPIDIGuion: .docx · .yaml

Qué trae un aviso

SPIDI hace POST a tu webhook_url con un cuerpo JSON y tres cabeceras:

  • spidi-signature — sello HMAC-SHA256 de spidi-timestamp + "." + el cuerpo crudo, con el secreto de tu cuenta.
  • spidi-timestamp — cuándo se generó. Entra en el cálculo de la firma, así que sin él no se puede verificar nada.
  • idempotency-key — identificador estable del envío, para deduplicar reintentos.
{ "event": "payment_session.paid", "data": { /* ... */ } }

El campo event es lo que tu código mira. Todo lo demás de esta página gira alrededor de él.

Los cinco eventos que declara el contrato

event que recibesCuándoFase
payment_session.createdLa sesión de pago se creóantes de la fase 1
payment_session.paidEl débito al pagador salió bienfase 1
payment_session.accreditedTerminaron las acreditacionesfase 2
payment_session.accreditation_to_recipient_completedSe acreditó a un receptor del repartofase 2, uno por receptor
payment_session.accreditation_to_recipient_failedFalló la acreditación a un receptorfase 2

En un pago sin reparto verás dos: paid y accredited.

La trampa: dos de ellos no se llaman como el contrato los titula

Compara contra el campo event, no contra el título del contrato

La clave que titula cada webhook en la OpenAPI es una etiqueta del documento: no viaja por el cable. Y en dos casos no coincide con lo que llega:

Clave en la OpenAPIevent que viaja de verdad
payment_session.payment_completedpayment_session.paid
payment_session.accreditations_completedpayment_session.accredited

El fallo es silencioso. Un switch escrito leyendo los títulos del contrato no entra nunca en esas dos ramas: los avisos llegan, tu endpoint responde 2xx, y tu lógica no se ejecuta. Nada da error.

Cinco declarados, dos observados

Los cinco están en el contrato. Nosotros solo hemos visto llegar paid y accredited, y el simulador solo emite esos dos. Los otros tres están aquí porque el contrato los declara y tu receptor puede recibirlos: ante un event que no conozcas, ignóralo y responde 2xx — nunca rompas.

El que más conviene mirar es accreditation_to_recipient_failed. Es el único que dice que el dinero no llegó a un receptor, y no tiene equivalente en GET status: si no lo escuchas, un reparto fallido se te queda invisible.

Cuántos avisos llegan por un pago

Llega uno por cada transacción de crédito, y eso depende de si repartes:

  • Sin reparto — una sola parte, un solo payment_session.accredited.
  • Con reparto — una acreditación por receptor, más el cierre. Todas a tu mismo webhook_url: un partner no tiene webhook propio.

Split

Entrega: reintentos, deduplicación, y qué pasa si estabas caído

Si tu endpoint no responde 2xx a tiempo, SPIDI reintenta con el mismo idempotency-key. Por eso tu receptor debe deduplicar: procesar una vez aunque llegue varias.

No escribas lógica que dependa de cuántos reintentos hay ni de cuánto esperan entre uno y otro. Diseña para recibir el mismo aviso un número indeterminado de veces, que es la única suposición que no se rompe.

Guarda el session_id: si SPIDI se rinde, es tu única llave de vuelta

Después de reintentar, SPIDI se rinde, y no hay reenvío. Cuando tu servidor vuelva no habrá un aviso esperándote — habrá una sesión que puedes consultar. Pero no hay forma de listar tus sesiones, así que sin el session_id guardado no tienes por dónde entrar.

El aviso es el atajo, no la única fuente. Las dos fases se consultan también en la sesión, con GET status: la fase 1 en data.status y la fase 2 en data.session_payment.receiver_credits. → Transacción a dos fases

Manejar notificaciones · Usar el simulador