Integra TronDealer en 6 pasos: API de pagos USDT, webhooks HMAC y sweep automático

Guía paso a paso para conectar tu backend a TronDealer. Registra tu negocio vía API, asigna wallets multi-chain, verifica firmas HMAC X-Signature-256 y entiende el lifecycle de depósitos on-chain con USDT y USDC.

12 minActualizada: 20 de abril de 2026Read in English

Esta guía documenta la integración completa de TronDealer, la pasarela de pagos con stablecoins operada por QvaPay. Al terminar tendrás un backend capaz de aceptar USDT y USDC en BSC, Ethereum y Polygon, con detección automática de depósitos, notificaciones firmadas por webhook y sweep de fondos al wallet principal.

Si vienes de otra pasarela (Coinbase Commerce, BitPay, NOWPayments), la API de TronDealer cubre el mismo flujo —registro, checkout, webhook, reconciliación— pero sin volatilidad de precio (los clientes pagan directamente en stablecoins) y con soporte nativo para mercados hispanos vía QvaPay.

Prerrequisitos
  • Un backend que pueda hacer peticiones HTTPS y procesar JSON.
  • Una URL pública con HTTPS para recibir webhooks (puedes usar ngrok, cloudflared o un túnel en dev).
  • Conocimientos básicos de HMAC-SHA256 para validar firmas.

Visión general del flujo#

El ciclo de vida de un pago tiene cuatro estados. Todo lo que integres debe entender esta progresión:

detectedconfirmednotifiedswept
  1. detected — TronDealer ve el depósito en la red correspondiente (BSC, ETH o Polygon).
  2. confirmed — La transacción acumula suficientes confirmaciones on-chain (por defecto 15 en BSC).
  3. notified — Se dispara el webhook POST a tu URL configurada, firmado con HMAC si definiste webhook_secret.
  4. swept — Los fondos se barren automáticamente al wallet principal o cuenta QvaPay que hayas configurado.

Los 6 pasos#

  1. 1

    1. Registra tu negocio vía API

    El registro es público y no requiere aprobación manual. Necesitas un token Cloudflare Turnstile (CAPTCHA) obtenido desde el widget en el frontend, pero si estás haciendo pruebas desde backend puedes registrarte a través del formulario web en /onboard y luego autenticarte.

    POSThttps://trondealer.com/api/v2/clients/register-public
    Authnone (Turnstile token)
    curl -X POST https://trondealer.com/api/v2/clients/register-public \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Mi Tienda",
        "webhook_url": "https://mi-backend.com/webhooks/trondealer",
        "webhook_secret": "un-secreto-largo-y-aleatorio",
        "min_confirmations": 15,
        "sweep_wallet": "0xABC...123",
        "payout_method": "wallet",
        "turnstile_token": "..."
      }'

    La respuesta incluye client.api_key con prefijo td_ seguido de 64 caracteres hex. Guárdalo en tu vault de secretos: esta cabecera es la credencial que autentica todas las llamadas posteriores y no podrás recuperarla.

    Elige bien el payout_method

    Al registrar defines cómo recibes los fondos después del sweep: wallet (tu dirección EVM personal), qvapay (cuenta QvaPay por usuario o email) o zelle (email o teléfono). Cambiar después requiere editar la configuración desde el dashboard.

  2. 2

    2. Asigna una wallet multi-chain

    Cada cliente genera wallets EVM bajo demanda. La misma dirección es válida en BSC, Ethereum y Polygon (las tres redes V2 soportadas actualmente).

    POSThttps://trondealer.com/api/v2/wallets/assign
    Authx-api-key: td_...
    curl -X POST https://trondealer.com/api/v2/wallets/assign \
      -H "x-api-key: td_..." \
      -H "Content-Type: application/json" \
      -d '{ "label": "Pedido #A-1024" }'

    Si pasas el mismo label dos veces, TronDealer reutiliza la wallet existente (200 OK). Esto te permite tratar el label como idempotencia del lado de tu sistema: el ID de pedido, el user ID, etc.

    La clave privada de la wallet se almacena cifrada en nuestra base de datos y nunca se expone por API. TronDealer orquesta todas las operaciones on-chain (sweep, gas funding) por ti.

  3. 3

    3. Consulta el balance on-chain

    Antes de marcar un pedido como pagado, puedes verificar el balance en vivo de una wallet. El endpoint consulta directamente los contratos de USDT y USDC en las tres redes y devuelve saldos decodificados.

    POSThttps://trondealer.com/api/v2/wallets/balance
    Authx-api-key: td_...
    balance.sh
    curl -X POST https://trondealer.com/api/v2/wallets/balance \
      -H "x-api-key: td_..." \
      -H "Content-Type: application/json" \
      -d '{ "address": "0xABC...123" }'

    Respuesta típica:

    balance-response.json
    {
      "success": true,
      "balances": {
        "bsc":  { "native": "0.012", "usdt": "50.0", "usdc": "0.0" },
        "eth":  { "native": "0.0", "usdt": "0.0", "usdc": "25.5" },
        "pol":  { "native": "0.0", "usdt": "0.0", "usdc": "0.0" }
      }
    }

    En la mayoría de los casos no necesitas pollearlo: el webhook de confirmación (paso 5) llega automáticamente cuando hay un depósito. Úsalo solo para reconciliación manual o dashboards internos.

  4. 4

    4. Lista transacciones con filtros y paginación

    Para mostrar el historial de un cliente o reconciliar depósitos contra tu ERP, lista las transacciones asociadas a sus wallets.

    POSThttps://trondealer.com/api/v2/wallets/transactions
    Authx-api-key: td_...
    list-transactions.js
    const res = await fetch('https://trondealer.com/api/v2/wallets/transactions', {
      method: 'POST',
      headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
      body: JSON.stringify({
        address: '0xABC...123',
        status: 'confirmed', // detected | confirmed | notified | swept
        limit: 20,
        offset: 0,
      }),
    })
    const { transactions, total } = await res.json()

    Cada transacción incluye: tx_hash, block_number, from_address, to_address, asset (USDT/USDC), amount decodificado, confirmations actuales, status, network, webhook_sent, y timestamps.

  5. 5

    5. Configura y verifica el webhook HMAC

    Esta es la parte crítica de cualquier pasarela: garantizar que los webhooks que recibes son auténticos y no fueron inyectados por un tercero con acceso a tu URL.

    Si registraste un webhook_secret, cada POST incluye un header X-Signature-256 con la firma HMAC-SHA256 del body completo. Debes:

    1. Recibir el body raw (antes de parsearlo como JSON).
    2. Calcular HMAC-SHA256(secret, raw_body) en hex.
    3. Comparar con el valor del header usando comparación constante (timing-safe).
    import crypto from 'node:crypto'
    import express from 'express'
     
    const app = express()
    const SECRET = process.env.WEBHOOK_SECRET
     
    // IMPORTANTE: body raw, no json()
    app.post(
      '/webhooks/trondealer',
      express.raw({ type: 'application/json' }),
      (req, res) => {
        const sig = req.headers['x-signature-256']
        const expected = crypto
          .createHmac('sha256', SECRET)
          .update(req.body)
          .digest('hex')
     
        if (!sig || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
          return res.status(401).send('invalid signature')
        }
     
        const event = JSON.parse(req.body.toString())
        // event.tx_hash, event.amount, event.asset, event.network, event.status
        console.log('Pago confirmado:', event)
        res.status(200).send('ok')
      }
    )
    Nunca uses comparación con `==`

    Una comparación string igualdad clásica es vulnerable a timing attacks. Usa crypto.timingSafeEqual en Node, hmac.compare_digest en Python o hash_equals en PHP. Es un detalle pequeño pero crítico.

    Reintentos

    Si tu webhook devuelve un código ≠ 2xx, TronDealer reintenta automáticamente con backoff exponencial. Responde 200 lo antes posible y procesa el evento de forma asíncrona (cola) si tu lógica es pesada.

  6. 6

    6. Entiende el lifecycle end-to-end

    Cada depósito atraviesa estos estados. Diseña tu lógica de negocio alrededor de confirmed (o notified, que implica confirmado), nunca de detected.

    EstadoDisparadorAcción recomendada
    detectedEl scanner ve el log de TransferNo marcar pedido como pagado aún
    confirmed≥ min_confirmations bloquesCandidato a liberar producto/servicio
    notifiedWebhook entregado con 2xxTu sistema ya confirmó recepción
    sweptFondos movidos al wallet principalCobro consolidado, listo para contabilidad

    Las confirmaciones por defecto son 15 (BSC, rápido). Puedes subirlo a 20–30 si manejas montos altos, o bajarlo a 6–10 si priorizas UX. El valor se configura por cliente al registrarte o desde /dashboard/settings.

Reconciliación y buenas prácticas#

Un pedido, una wallet

Usa un label único por pedido (por ejemplo el ID de orden) al hacer POST /wallets/assign. Esto te da una wallet dedicada por transacción y simplifica la reconciliación: el match es 1:1 entre pedido y dirección de destino.

  • Idempotencia: usa tx_hash + log_index como clave única en tu base de datos. Si un webhook se entrega dos veces (reintento) no procesarás el pago dos veces.
  • Timeouts: no consideres un pedido "fallido" solo porque la wallet está vacía a los 60 segundos. Los depósitos en BSC tardan ~15s, en ETH varios minutos.
  • Logs: guarda el body raw del webhook junto con la firma. Te salvará la vida en un debug.
  • Seguridad: la x-api-key debe vivir solo en tu backend. Nunca la expongas en frontend ni mobile.

Preguntas frecuentes#

Siguiente paso#

Con el flujo ya integrado, lo que queda es endurecer la parte operativa: monitorear entregas de webhooks desde /dashboard/webhooks, configurar alertas de sweeps fallidos en /dashboard/sweeps, y ajustar min_confirmations según tu tolerancia a riesgo.

Si necesitas endpoints no cubiertos por V2 (TRON, Solana, Bitcoin), la documentación Swagger lista los endpoints V1 por cadena.

Mira la explicación en video#

Recorrido completo del sistema TronDealer en video.