Tabla de Contenidos
ToggleTL;DR: Una API de pagos con stablecoin confiable en producción se sostiene en tres decisiones técnicas: verificación de firma HMAC en cada webhook, idempotencia en cada request, y reintentos con backoff exponencial en lugar de reintentos inmediatos. El resto es detalle de implementación.
Este artículo documenta cómo está construida técnicamente una API de pagos con stablecoin, qué componentes debe tener para ser confiable en producción, y qué patrones de integración usan los equipos que ya la implementaron sin incidentes. No es una introducción conceptual, es la referencia técnica que un desarrollador necesita antes de comprometer tiempo de sprint a la integración: endpoints, formatos de request y respuesta, códigos de error, seguridad de webhooks y checklist de testing.
Arquitectura general
Una API de pagos con stablecoin bien diseñada separa tres responsabilidades. La creación del cobro, que genera una dirección de pago o un link único por transacción. La confirmación en cadena, que escucha la blockchain hasta que el pago recibe suficientes confirmaciones para considerarse válido. Y la notificación al backend del comercio, normalmente vía webhook, para que el sistema del negocio actualice el estado del pedido sin tener que hacer polling constante contra la API.
Cliente inicia pago
↓
API genera invoice (dirección + monto + red)
↓
Cliente envía stablecoin desde su wallet
↓
Nodo de la red confirma la transacción
↓
API espera N confirmaciones según la red
↓
Webhook notifica al backend del comercio
↓
Backend actualiza el estado del pedido
Redes y stablecoins soportadas
| Red | Stablecoin | Ventaja técnica |
|---|---|---|
| TRON (TRC20) | USDT | Fee de red más bajo, la más usada en LATAM para pagos frecuentes |
| Ethereum (ERC20) | USDT, USDC | Mayor liquidez y compatibilidad con exchanges institucionales |
| Polygon | USDC | Confirmación rápida y fee mínimo, útil para montos pequeños frecuentes |
La elección de red no es solo una preferencia técnica, cambia directamente el costo y la velocidad de cada transacción. Para pagos de monto bajo y alta frecuencia, una red con fee mínimo como TRON o Polygon evita que el costo de red se coma un porcentaje relevante del pago. Para transferencias de tesorería de monto alto, la liquidez y compatibilidad de Ethereum suele pesar más que el costo del fee.
Endpoints principales
Más allá de crear un cobro, una integración real necesita consultar el estado de una transacción, listar operaciones para conciliación, y en el caso de tesorería o nómina, enviar pagos salientes. Estos son los endpoints que forman el núcleo de cualquier integración seria.
| Endpoint | Método | Para qué sirve |
|---|---|---|
| /v1/payments | POST | Crea un cobro nuevo, devuelve dirección o link de pago |
| /v1/payments/{id} | GET | Consulta el estado actual de un cobro específico |
| /v1/payments | GET | Lista cobros con filtros por fecha, estado o referencia, para conciliación |
| /v1/payouts | POST | Envía un pago saliente a una dirección o cuenta, para nómina o proveedores |
| /v1/payouts/batch | POST | Envía varios pagos salientes en una sola llamada, cada uno con su propia referencia |
| /v1/payouts/{id} | GET | Consulta el estado de un pago saliente específico |
Autenticación y seguridad
Una integración de este tipo debe manejar tres capas de seguridad como mínimo. Autenticación por API key, distinta para entorno de pruebas y de producción, para evitar que una prueba de desarrollo afecte transacciones reales. Firma de webhooks mediante HMAC-SHA256, para que el backend del comercio pueda verificar que la notificación realmente proviene del proveedor y no de un tercero simulando la llamada. E idempotencia en la creación de cobros, usando una clave única por operación para evitar cobros duplicados si la petición se reintenta por un timeout de red.
POST /v1/payments
Headers:
Authorization: Bearer {api_key}
Idempotency-Key: {clave_unica_por_operacion}
Content-Type: application/json
Body:
{
"amount": "150.00",
"currency": "USD",
"settlement_currency": "USDC",
"network": "polygon",
"reference": "pedido_10432",
"webhook_url": "https://tuapp.com/webhooks/pagos"
}
La respuesta típica de una API bien diseñada devuelve una dirección o link de pago, el estado inicial de la transacción, y el tiempo de expiración del cobro, información que el frontend del comercio necesita para mostrar el checkout con su propio temporizador y estado.
{
"id": "pay_8f3a2c",
"status": "pending",
"payment_address": "0x9F2...c41A",
"network": "polygon",
"amount": "150.00",
"expires_at": "2026-09-02T14:30:00Z"
}
Códigos de estado y manejo de errores
Una API confiable devuelve códigos de estado consistentes, no un 200 genérico para todo. Diferenciar entre error del cliente (4xx) y error del servidor (5xx) es lo que determina si tu integración debe reintentar la llamada o corregir el request antes de volver a enviarlo.
| Código | Significado | Qué hacer |
|---|---|---|
| 400 | Request mal formado, campo faltante o inválido | Corregir el payload, no reintentar sin cambios |
| 401 | API key inválida o ausente | Verificar credenciales, no reintentar |
| 409 | Conflicto de idempotencia, la clave ya fue usada con otro payload | Usar una clave de idempotencia distinta para una operación nueva |
| 422 | Red o moneda no soportada para ese tipo de operación | Revisar parámetros según la documentación de redes soportadas |
| 429 | Límite de rate limit excedido | Reintentar con backoff exponencial, respetando el header Retry-After |
| 500 / 503 | Error interno o servicio no disponible temporalmente | Reintentar con backoff exponencial |
La regla general, consistente con cómo maneja esto cualquier API de pagos madura: solo reintentar automáticamente errores 429 y 5xx. Un error 4xx indica un problema en el request que no se resuelve reenviando el mismo payload, reintentar sin corregirlo solo genera ruido y, en el peor caso, operaciones duplicadas.
Rate limits
La mayoría de APIs de pagos aplican límites por ventana de tiempo para proteger la infraestructura de picos de tráfico, típicamente expresados como un número de requests por minuto por API key. Cuando se excede ese límite, la respuesta es un 429 con un header Retry-After indicando cuántos segundos esperar antes del siguiente intento. Diseñar tu integración para respetar ese header, en lugar de reintentar de inmediato, es lo que evita que tu propia integración empeore el problema durante un pico de tráfico.
Webhooks: entrega, verificación y reintentos
Los webhooks son, por diseño, poco confiables: la red falla, el servidor del comercio puede estar caído en el momento exacto del envío, y un mismo evento puede llegar duplicado. Una integración madura no asume que cada webhook llega una sola vez, asume lo contrario y se protege con tres mecanismos.
Verificación de firma HMAC
Cada webhook debe incluir una firma HMAC-SHA256 calculada sobre el payload con una clave secreta compartida. El backend del comercio recalcula esa firma y la compara con la recibida, usando una comparación de tiempo constante para evitar ataques de timing.
// Node.js
const crypto = require('crypto');
function verificarWebhook(payload, firmaRecibida, secreto) {
const firmaCalculada = crypto
.createHmac('sha256', secreto)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(firmaCalculada),
Buffer.from(firmaRecibida)
);
}
Nunca proceses un webhook sin verificar la firma primero. Sin esa verificación, cualquiera que descubra tu URL de webhook podría enviar eventos falsos y hacer que tu sistema marque pedidos como pagados sin que exista una transacción real.
Reintentos con backoff exponencial
Si tu endpoint responde con un código distinto de 2xx, o no responde dentro del timeout, el proveedor debe reintentar la entrega con intervalos crecientes en lugar de reintentos inmediatos, típicamente entre 5 y 10 intentos distribuidos a lo largo de varias horas o días antes de marcar el endpoint como fallido. Tu backend, del lado receptor, debe responder 200 lo antes posible, apenas verificada la firma, y procesar la lógica de negocio de forma asíncrona en una cola. Un endpoint que tarda demasiado en responder porque procesa todo de forma síncrona es la causa más común de reintentos innecesarios.
Idempotencia en el procesamiento
Como el mismo evento puede llegar más de una vez, tu sistema debe guardar el ID de cada evento procesado y descartar duplicados antes de ejecutar cualquier cambio de estado. Sin esto, un reintento legítimo del proveedor puede terminar marcando un pedido como pagado dos veces, o disparando una notificación duplicada al cliente.
Eventos de webhook que necesitas manejar
| Evento | Cuándo se dispara |
|---|---|
| payment.pending | El cobro fue creado y espera el pago del cliente |
| payment.confirming | La transacción llegó a la red y está acumulando confirmaciones |
| payment.completed | El pago alcanzó las confirmaciones necesarias y es definitivo |
| payment.expired | El cliente no completó el pago dentro del tiempo definido |
| payout.sent | Un pago saliente fue transmitido a la red |
| payout.failed | Un pago saliente no pudo completarse, por ejemplo por dirección inválida |
El punto que más equipos subestiman al integrar por primera vez es el estado intermedio, payment.confirming. Marcar un pedido como pagado apenas se detecta la transacción en la red, sin esperar las confirmaciones necesarias, expone al negocio a fraude si la transacción llegara a revertirse antes de confirmarse por completo. El número de confirmaciones necesarias varía por red: menos en redes rápidas como Polygon, más en Ethereum para montos altos.
SDKs y compatibilidad
Una API RESTful bien documentada, con JSON como formato estándar de request y respuesta, no obliga a usar un SDK específico. Sin embargo, la mayoría de equipos prefieren un cliente oficial para los lenguajes más comunes en fintech, Node.js, Python, PHP y Java, porque maneja automáticamente la firma de requests, el reintento con backoff y el parseo de errores, en lugar de reimplementar esa lógica desde cero. Si tu stack usa un lenguaje sin SDK oficial, la integración directa vía HTTP sigue siendo viable, simplemente exige implementar tú mismo la verificación de firma y el manejo de reintentos descritos arriba.
Casos de uso técnicos
Checkout de ecommerce
Se genera un cobro por cada carrito al momento del pago, con expiración corta, típicamente 15 a 30 minutos, y el frontend escucha el webhook payment.completed para redirigir al cliente a la confirmación del pedido sin que tenga que refrescar la página manualmente.
Facturación recurrente para SaaS
En lugar de un cobro único, se genera una dirección de pago reutilizable o un cobro programado por ciclo de facturación, con reconciliación automática contra el ID de cliente en el sistema de suscripciones.
Dispersión de pagos a proveedores o freelancers
Aquí el flujo se invierte: en lugar de recibir pagos, la API se usa para enviar, vía /v1/payouts/batch. Un lote de pagos permite despachar varias transferencias en una sola llamada, cada una con su propia referencia, útil para nómina o pagos a un lote de proveedores el mismo día.
Marketplaces con split de pagos
Un cobro único se distribuye automáticamente entre la plataforma y el vendedor según un porcentaje configurado, evitando que el marketplace tenga que operar una segunda transferencia manual después de cada venta.
Checklist antes de pasar a producción
- La verificación de firma HMAC está implementada y probada con una firma inválida a propósito, para confirmar que se rechaza.
- El endpoint de webhook responde 200 en menos de 5 segundos, y el procesamiento pesado corre de forma asíncrona en una cola.
- Los IDs de evento procesados se guardan en base de datos, para descartar duplicados antes de mutar estado.
- El manejo de reintentos en tu propio código distingue entre errores 4xx, que no se reintentan, y errores 429 o 5xx, que sí.
- El estado
payment.confirmingestá manejado explícitamente, sin dar el pago por válido antes depayment.completed. - Existe un endpoint o proceso de reconciliación que compara periódicamente el estado local contra
GET /v1/payments, por si algún webhook se perdiera. - Las claves de API de producción y de entorno de pruebas están separadas y ninguna está expuesta en el frontend ni en el control de versiones.
Entorno de pruebas
Antes de integrar en producción, cualquier equipo debe poder probar el flujo completo en un entorno sandbox, con una red de pruebas que simula confirmaciones sin mover fondos reales. Esto permite validar el manejo de cada evento de webhook, incluyendo los casos límite como expiración o montos insuficientes, sin arriesgar capital real durante el desarrollo.
Base regulatoria detrás de la infraestructura
Una API de pagos con stablecoin no opera en zona gris si el proveedor detrás está correctamente registrado. En Perú, eso significa estar inscrito como Proveedor de Servicios de Activos Virtuales (PSAV) ante la SBS, bajo el Decreto Supremo N.°006-2023-JUS. Fluyez opera bajo ese registro, con seis años de operación en el mercado peruano y presencia en cinco países de la región, lo que da a cualquier integración técnica una base verificable antes de mover dinero real.
Preguntas frecuentes
¿Qué red conviene usar para pagos frecuentes de monto bajo?
TRON o Polygon, por su fee de red mínimo comparado con Ethereum, donde el costo de gas puede ser desproporcionado frente a transacciones pequeñas.
¿Cómo evito procesar un pago que después se revierte?
Esperando el número de confirmaciones que la red recomienda antes de marcar la transacción como definitiva, y manejando el estado intermedio payment.confirming en tu backend en lugar de dar por válido el pago apenas se detecta en la red.
¿Qué errores debo reintentar automáticamente?
Solo 429 (rate limit) y errores 5xx, siempre con backoff exponencial. Los errores 4xx indican un problema en el request que reintentar sin corregir no resuelve, y puede generar operaciones duplicadas.
¿Cómo evito procesar el mismo webhook dos veces?
Guardando el ID de cada evento procesado en base de datos y descartando cualquier evento con un ID ya registrado antes de ejecutar cambios de estado, independientemente de cuántas veces llegue.
¿Puedo probar la integración sin usar fondos reales?
Sí, un entorno sandbox con red de pruebas permite validar todo el flujo, incluida la firma de webhooks y los casos de expiración, antes de pasar a producción.
¿La API sirve también para enviar pagos, no solo para recibir?
Sí, mediante /v1/payouts y /v1/payouts/batch para dispersión de pagos, nómina internacional o pagos a proveedores en lote.
Conclusión
La diferencia entre una integración de pagos con stablecoin que funciona bien en producción y una que genera incidentes no está en qué tan rápido se implementa, está en tres decisiones concretas: verificar la firma de cada webhook antes de confiar en él, tratar cada evento como potencialmente duplicado, y reintentar solo los errores que tiene sentido reintentar. La arquitectura descrita aquí, creación del cobro, confirmación en cadena y notificación asíncrona con reintentos controlados, es el patrón que sostiene la mayoría de integraciones de pago confiables en producción hoy, no solo en cripto.









