Volver a Guías
Integración de API de Pasarela de Pago: Guía para Desarrolladores en España
Integrar una pasarela de pago es fácil de hacer mal. Se salta la idempotencia, se procesan los webhooks en línea, se omite la firma en redes "de confianza" y seis meses después un incidente en producción acaba trazándose a un cargo duplicado o a un callback perdido.
Esta guía recorre los patrones que importan: autenticación de la API, claves de idempotencia, webhooks firmados con HMAC, semántica de reintentos, manejo de errores, paridad sandbox y las particularidades de la integración con una pasarela cripto autorizada bajo MiCA en España. Recomendaciones concretas a nivel de código, sin viñetas de marketing. Incluye referencias a obligaciones DORA (Reglamento UE 2022/2554) para entidades financieras y sus proveedores TIC críticos.
Impulse su negocio aceptando pagos criptográficos
El camino feliz es el 5 % del trabajo
Una integración a primera vista parece sencilla: POST para crear un cargo, redirigir al comprador, recibir un webhook, marcar el pedido como pagado. Se demuestra en un fin de semana.
La integración real en producción es otra cosa. El 95 % del código que envía (y el 100 % del que provoca incidentes) gestiona:
- Peticiones duplicadas (reintentos de red, botón atrás del navegador, doble clic).
- Entregas de webhook duplicadas (semántica at-least-once).
- Webhooks perdidos (firewall, timeout, despliegue en curso).
- Fallos parciales (cargo OK, pero fallo al escribir en BBDD).
- Condiciones de carrera entre respuesta API y webhook.
- Reembolsos, disputas y transiciones tardías de estado.
- Divergencias sandbox-producción cuando un caso borde solo aparece en prod.
Construya pensando en ese mundo desde el día uno.
Autenticación: claves, secretos, OAuth2
Las pasarelas autentican sus llamadas de una de tres formas:
- API keys (secreta + publicable). Lo más común. La clave secreta vive en el servidor; la publicable en el cliente para tokenizar campos del formulario. Nunca ponga la secreta en el navegador. Rote ambas con calendario y ante cualquier sospecha.
- OAuth2 (client credentials). Se usa en pasarelas tipo plataforma donde su aplicación actúa en nombre de un comerciante. Tokens de acceso de corta duración más refresh tokens. Más piezas móviles, frontera más fuerte.
- Peticiones firmadas con HMAC. Cada petición se firma con un secreto. Ideal para server a server con exigencias altas de seguridad; más código repetitivo.
Sea cual sea el esquema: guarde los secretos en un secret manager (AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, 1Password Secrets Automation), nunca en código, nunca en .env versionados en git. Ajuste los permisos de cada clave al mínimo (solo lectura, solo facturas, solo reembolsos). Rote trimestralmente. Bajo DORA, la gestión de claves y secretos forma parte del marco de gestión de riesgo TIC.
Idempotencia: el patrón que evita cargos duplicados
Una llamada a la API puede fallar de tres formas: el servidor nunca la recibió, la procesó y la respuesta se perdió, o la procesó y devolvió error. El cliente no puede distinguir cuál. Si reintenta, arriesga un segundo cargo.
La idempotencia lo resuelve. El cliente genera una clave única por petición lógica (un UUID atado al pedido) y la envía en la cabecera Idempotency-Key. El servidor guarda la clave con la respuesta; un reintento con la misma clave devuelve la respuesta original sin reejecutar el trabajo.
POST /v1/charges
Idempotency-Key: 8b7cfa4d-1e6c-4a12-9a3e-...
Content-Type: application/json
{"amount": 9900, "currency": "eur", "source": "tok_..."}
Reglas de implementación:
- Genere la clave en el cliente, una sola vez por petición lógica. No por reintento.
- Persista la clave con el pedido antes de hacer la llamada.
- Use UUIDv4 o una cadena aleatoria fuerte. Nada de timestamps. Nada de auto-increment.
- La idempotencia no es eterna: las pasarelas cachean las claves entre 24 y 72 horas. Después un nuevo intento es una nueva petición.
- Pruebe los reintentos explícitamente. Envíe la misma clave dos veces; verifique que hay un solo cargo.
Webhooks: firmados, asíncronos, idempotentes
Los webhooks son la contraparte asíncrona de la API: la pasarela llama a su endpoint cuando ocurre un evento (pago completado, disputa abierta, payout liquidado). Mal hechos, provocan fallos silenciosos; bien hechos, son la columna vertebral del estado de pago.
Los innegociables:
- Verifique la firma. HMAC-SHA256 del cuerpo crudo con un secreto compartido, comparado contra la cabecera
X-Signature. Rechace cualquier petición cuya firma no valide. Nunca confíe solo en la IP de origen. - Devuelva 200 rápido. Acuse recibo en menos de 500 ms. Saque el trabajo pesado (escrituras a BBDD, envío de correos, llamadas a terceros) a una cola en segundo plano. Los handlers lentos expiran y se reintentan, amplificando carga.
- Sea idempotente por ID de evento. Cada webhook trae un ID de evento único. Regístrelo antes de procesar. Si ve un duplicado, devuelva 200 sin reprocesar.
- Maneje eventos fuera de orden. El webhook "pago completado" puede llegar antes que la respuesta API que creó el cargo. Diseñe máquinas de estado que toleren cualquier orden.
- Exponga una herramienta de reenvío. El equipo de ops necesita reenviar un evento concreto desde el panel cuando su endpoint estuvo caído.
// Pseudocódigo Express.js
app.post('/webhooks/gateway', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['x-signature'];
const expected = hmac('sha256', SECRET, req.body).toString('hex');
if (!timingSafeEqual(sig, expected)) return res.status(401).end();
const event = JSON.parse(req.body);
if (await seenEvent(event.id)) return res.status(200).end();
await enqueue(event);
return res.status(200).end();
});
Manejo de errores y reintentos
Clasifique los errores por si merece la pena reintentarlos:
| Categoría | Ejemplo | ¿Reintentar? |
|---|---|---|
| 4xx cliente | Tarjeta inválida, parámetro ausente | No - corrija la petición |
| 402 payment required | Denegación del emisor | No - informe al usuario |
| 409 conflict | Colisión de idempotency key | No - trátelo como duplicado, obtenga el existente |
| 429 rate limit | Demasiadas peticiones | Sí - backoff exponencial, respete Retry-After |
| 5xx servidor | Gateway timeout, error interno | Sí - backoff, siempre con la misma idempotency key |
| Timeout de red | Connection reset, fallo DNS | Sí - reintento con idempotency key |
Para errores reintenttables, use backoff exponencial con jitter. Un calendario razonable: 1 s, 3 s, 10 s, 30 s, 2 min, 10 min, y luego pare y alerte. Nunca reintente síncronamente dentro del handler de la petición; encole siempre.
Sandbox, tarjetas de prueba y estrategia de test
Toda pasarela mayor ofrece entorno sandbox con tarjetas de prueba que disparan escenarios específicos. Ejemplos habituales (válidos en la mayoría de PSP, incluyendo Redsys sandbox):
- 4242 4242 4242 4242 - éxito genérico.
- 4000 0000 0000 0002 - denegación genérica.
- 4000 0000 0000 9995 - fondos insuficientes.
- 4000 0027 6000 3184 - 3DS2 obligatorio (PSD2/SCA en España).
- 4000 0000 0000 0341 - tokeniza, pero deniega al cargar.
Disciplina de pruebas:
- Toda ruta de código en producción tiene test en sandbox. Sin excepciones para código "simple".
- Simule cada evento de webhook, incluidas entregas fuera de orden y duplicadas.
- Fuerce errores por diseño: un helper de test que reproduzca un pago con un código de denegación elegido.
- Pruebe la idempotencia: envíe la misma petición dos veces, verifique que hay exactamente un cargo.
- Pruebe reembolsos, reembolsos parciales y disputas (contracargos en tarjeta, refund on-chain en cripto).
- Valide con un test de caos: mate el worker mientras procesa un webhook y compruebe que reprocesa limpio al reiniciar. DORA exige pruebas de resiliencia operativa periódicas.
Particularidades de la API cripto
Los patrones anteriores aplican igual a las pasarelas cripto. Las diferencias están en el modelo de eventos, no en la fontanería:
- Ciclo de vida de factura, no de cargo. Cree una factura con importe en EUR; el comprador elige activo; la pasarela devuelve dirección de depósito o URI de pago. Eventos:
invoice.created,payment.detected,payment.completed,payment.settled,invoice.expired,invoice.underpaid. - Confirmaciones como eventos separados. Una buena pasarela emite un evento al detectar en mempool y otro al confirmarse. Trátelos por separado en su máquina de estado.
- Sin tokens de tarjeta. Sin vaulting, sin 3DS, sin network tokenization. Se sustituye por firmas de monedero, gestionadas por el wallet del comprador.
- Reembolsos como transacciones salientes. Un reembolso obliga a iniciar una transacción on-chain desde su wallet o la de la pasarela a la dirección del comprador. Prevea que la dirección puede haber cambiado desde el pago original.
- Eventos de off-ramp.
payout.initiated,payout.paid,payout.failed. Concílielos con sus extractos bancarios; la correspondencia es un payout por muchas facturas.
El código del camino feliz en una integración cripto es más corto que en tarjeta porque no hay 3DS, ni vaulting, ni flujo de contracargos. Los casos borde son distintos, no más numerosos.
Checklist previa al lanzamiento
- Secretos en secret manager, nunca en código.
- Idempotency keys generadas y persistidas para cada POST que mueva dinero.
- Verificación de firma de webhook con tests unitarios contra payloads manipulados.
- Handlers de webhook devuelven 200 en menos de 500 ms y procesan asíncrono.
- IDs de evento deduplicados antes de procesar.
- Máquinas de estado tolerantes a eventos fuera de orden.
- Backoff exponencial con jitter en todos los errores reintenttables.
- Paneles para webhooks fallidos, pagos fallidos y payouts fallidos.
- Alertas sobre tasas de error sostenidas, no sobre fallos únicos.
- Runbooks para los 5 tipos de incidente más probables.
- Pruebas de carga a 5 veces el pico esperado.
- Prueba de desastre: mate workers en plena transacción y verifique recuperación limpia. Obligatorio bajo DORA si es entidad financiera o proveedor TIC crítico.
Impulse su negocio aceptando pagos criptográficos
Comenzar
Preguntas frecuentes
Idempotencia significa que una petición repetida con la misma idempotency key produce el mismo resultado sin doble cargo. El cliente envía una cabecera Idempotency-Key única por petición lógica; el servidor detecta duplicados y devuelve la respuesta original.
Calcule un HMAC-SHA256 sobre el cuerpo crudo con el secreto compartido y compárelo (con comparación en tiempo constante) contra la cabecera de firma. Rechace cualquier petición cuya firma no valide, independientemente de la IP de origen.
HTTP 200 lo antes posible, idealmente bajo 500 ms. Saque el trabajo real a una cola en segundo plano. Los handlers que hacen lógica de negocio en línea expiran bajo carga y provocan reintentos en cascada.
Con reintentos automáticos y backoff exponencial. Calendarios típicos: 1 s, 5 s, 30 s, 2 min, 15 min, 1 h, 6 h, 24 h. Agotados los reintentos, el webhook se marca como fallido y queda disponible para reenvío manual desde el panel.
Confíe en webhooks como fuente de verdad. Use polling como red de seguridad: un job programado que concilie cualquier transacción en vuelo cuyo estado final falte. Nunca confíe en polling como señal primaria.
La publicable puede ir en el navegador y sirve para tokenizar tarjeta en cliente. La secreta vive en su servidor y se usa para el resto. Nunca exponga la secreta; rote ambas ante cualquier sospecha.
Use las tarjetas 3DS designadas por la pasarela (típicamente 4000 0027 6000 3184 u otras bien documentadas). El sandbox simula el reto PSD2/SCA y devuelve éxito sin fricción o requerimiento de reto según corresponda.
Mismos patrones REST + webhooks. Nombres de evento distintos (ciclo de factura en lugar de cargo), sin 3DS ni vaulting, y los reembolsos son transacciones on-chain salientes. La disciplina de integración (idempotencia, webhooks firmados, proceso asíncrono) es idéntica.
Llame a POST /refunds con el ID del cargo original. En cripto, incluya la dirección de retorno del comprador (que puede no ser la que pagó). Los reembolsos son asíncronos; espere refund.completed o refund.failed antes de notificar al cliente.
Hacer lógica de negocio dentro del handler del webhook en lugar de encolar. Funciona en dev, falla bajo carga en prod. Devuelva 200 rápido y procese en asíncrono; su equipo de operaciones lo agradecerá.