Recibe tu primer pago
¡Hola! En unos minutos vas a recibir tu primer pago de prueba con SPIDI, de punta a punta: lo creas, lo llevas a pagado y confirmas que te llegó el aviso. Que el dinero fluya. 🐦
Este tutorial usa el simulador hosteado de SPIDI (https://sim-productos.abiertolab.com). Te registras y completas un pago de prueba sin dinero real y sin credenciales en vivo. → Usar el simulador.
Cada comando de este tutorial se ejecuta contra el simulador en cada cambio (prueba simulador/test/func/tutorial.func.test.ts). Si algo dejara de funcionar, el CI lo detecta — la doc no miente.
Antes de empezar
- Acceso al simulador hosteado (
https://sim-productos.abiertolab.com) — te registras en el Paso 0. curlpara los ejemplos y un poco de Node para el receptor del aviso.- No necesitas credenciales reales: el registro te entrega una cuenta de prueba.
Paso 0 — Regístrate
Objetivo: obtener tu token y tu webhook_secret.
curl -X POST https://sim-productos.abiertolab.com/register \
-H "Content-Type: application/json" \
-d '{"short_name":"mi-tienda","password":"demo"}'
Respuesta: { "account_id": "...", "token": "...", "webhook_secret": "whsec_..." }. Guarda los tres.
En producción no haces este paso: obtienes el
tokencon tu login real (/api/spidipagos/login). En el simulador, el registro te lo entrega directo. (¿Perdiste el token? El mismo login te lo re-emite.)
Paso 1 — Crea tu acuerdo de pago
Objetivo: definir cómo recibes el dinero (agreement_id).
curl -X POST https://sim-productos.abiertolab.com/api/v1/ext/agreements \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"title": "Acuerdo sin Split",
"description": "Sin distribución de fondos",
"split": false,
"payment_methods": { "immediate_debit": true, "crypto": false, "mobile_payment": true },
"default_bank_account_id": "uuid_sofitasa_001",
"rules": [{ "origin_bank_code": "0105", "destination_bank_account_id": "uuid_mercantil_007" }]
}'
Qué observar: la respuesta trae agreement_id. Checkpoint: tienes un agreement_id.
Paso 2 — Crea la sesión de Botón
Objetivo: generar el enlace de pago (payment_url).
curl -X POST https://sim-productos.abiertolab.com/api/v1/ext/payment-sessions/buttons \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"amount_reference": 50,
"currency_reference": "USD",
"identifier_label": "Pedido",
"identifier": "ORD-0001",
"description": "Mi primer pago de prueba",
"success_url": "https://miapp.com/pago-exitoso",
"failure_url": "https://miapp.com/pago-fallido",
"agreement_id": "<agreement_id>",
"webhook_url": "https://tu-app.example/webhooks/spidi"
}'
El
webhook_urldebe ser accesible desde internet para que el simulador hosteado te entregue el aviso (usa un túnel tipo ngrok o tu servicio desplegado). Si tu receptor corre en local (p. ej.http://localhost:4020), registra comowebhook_urlla URL pública del túnel que apunta a ese puerto — nunca ellocalhostdirecto, que el simulador hosteado no puede alcanzar.
Qué observar: payment_url y status: pending. Checkpoint: tienes una payment_url y el session_id (el data.id).
Los campos
identifier_label,identifierydescriptionson obligatorios (además de montos y URLs) — el contrato los exige.
Paso 3 — Lleva a quien paga al payment_url
Objetivo: que la persona complete el pago en la página de SPIDI.
<a href="<payment_url>" class="btn-spidi">Pagar con SPIDI</a>
En el simulador puedes abrir la payment_url en el navegador para ver la pantalla de pago (botones Pagar / Rechazar / Expirar), o forzar el desenlace desde el plano de control (siguiente paso).
Paso 4 — Confirma el pago del lado del servidor
La redirección a tu success_url no garantiza que te pagaron — solo dice que la persona volvió. Pregúntale a SPIDI desde tu backend.
# 1) en el simulador, forzamos el desenlace (sin pago real):
curl -X POST https://sim-productos.abiertolab.com/control/sessions/<session_id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"leg":1,"outcome":"paid"}'
# 2) consultamos el estado REAL:
curl https://sim-productos.abiertolab.com/api/v1/ext/payment-sessions/status/<session_id> \
-H "Authorization: Bearer <token>"
Qué observar: status: paid. Checkpoint: tu backend confirmó paid. Si sigue pending: nunca des por bueno el pago por la sola redirección.
Paso 5 — Recibe y valida el aviso de pago (webhook)
Al llegar a paid, el simulador envía a tu webhook_url el evento payment_session.paid con los headers spidi-signature (sello HMAC-SHA256 del cuerpo crudo), spidi-timestamp e idempotency-key.
// Node — verificar firma sobre el cuerpo CRUDO + idempotencia
import { createHmac, timingSafeEqual } from "crypto";
app.post("/webhooks/spidi", (req, res) => {
const firma = createHmac("sha256", WEBHOOK_SECRET).update(req.rawBody).digest("hex");
const ok = firma.length === req.headers["spidi-signature"].length &&
timingSafeEqual(Buffer.from(firma), Buffer.from(req.headers["spidi-signature"]));
if (!ok) return res.status(401).end(); // firma inválida
if (yaVisto(req.headers["idempotency-key"])) return res.status(200).end(); // repetido
procesar(req.body); marcarVisto(req.headers["idempotency-key"]);
res.status(200).end();
});
Qué observar: la firma coincide; un aviso repetido (mismo idempotency-key) no se procesa dos veces. Si falla: firma inválida → 401; repetido → 200 sin reprocesar.
Verifica siempre sobre el cuerpo crudo (los bytes recibidos), no sobre el JSON re-serializado: cualquier cambio de 1 byte invalida la firma. → Manejar notificaciones.
Paso 6 — ¡Listo! Y cómo te llega el dinero
Acabas de recibir tu primer pago con SPIDI: lo creaste, lo confirmaste del lado del servidor y procesaste el aviso de forma segura.
El flujo del dinero: el pago se debita (leg 1) y luego se acredita al beneficiario (leg 2, con su comisión) — y eso viaja por un segundo webhook, payment_session.accredited. Ojo: el status nunca pasa de paid por la acreditación; el leg 2 viaja solo por webhook. → Transacción a dos tiempos.
Próximos pasos: Solicitud de pago · Split · pasar a producción.