DEV Community

IronixPay
IronixPay

Posted on

Accept USDT Payments in 5 Minutes — Node.js Tutorial

You want to accept USDT on your website, SaaS app, or Telegram bot. You don't want to deal with blockchain nodes, wallet management, or building a checkout page from scratch.

This tutorial gets you from zero to a working USDT checkout in under 5 minutes, using IronixPay — a payment gateway for USDT on TRON.

What you'll build: A payment flow where your customer clicks "Pay", sees a checkout page with a TRON address + QR code, sends USDT, and your server gets a webhook when the payment confirms on-chain.

What You Need

That's it. No blockchain node. No wallet. No smart contract.


Step 1: Get Your API Key

  1. Log in to the IronixPay Dashboard
  2. Go to API Keys in the sidebar
  3. Click Create Key → copy it (starts with sk_test_)

💡 Use sk_test_ keys for development (TRON Nile testnet, free test USDT). Switch to sk_live_ for real payments.


Step 2: Create a Checkout Session (Backend)

Your backend creates a session, which generates a unique payment address and checkout URL.

// server.js
const express = require('express');
const app = express();
app.use(express.json());

const IRONIXPAY_SECRET = process.env.IRONIXPAY_SECRET_KEY; // sk_test_...

app.post('/create-checkout', async (req, res) => {
  const response = await fetch(
    'https://sandbox.ironixpay.com/v1/checkout/sessions',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${IRONIXPAY_SECRET}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        amount: 10000000, // 10.00 USDT (micro-units: 1 USDT = 1,000,000)
        currency: 'USDT',
        network: 'TRON',
        success_url: 'https://yoursite.com/success',
        cancel_url: 'https://yoursite.com/cancel',
        client_reference_id: req.body.orderId,
      }),
    }
  );

  const session = await response.json();
  res.json({ id: session.id, url: session.url });
});

app.listen(3000, () => console.log('Server running on :3000'));
Enter fullscreen mode Exit fullscreen mode

The response gives you:

  • url — hosted checkout page with QR code and payment address
  • id — session ID (e.g. cs_abc123...) for tracking

⚠️ Never expose sk_test_ or sk_live_ keys in frontend code. Always create sessions from your backend.


Step 3: Send Your Customer to Checkout

document.getElementById('pay-btn').addEventListener('click', async () => {
  const res = await fetch('/create-checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ orderId: 'order_001' }),
  });
  const { url } = await res.json();
  window.location.href = url; // → IronixPay checkout page
});
Enter fullscreen mode Exit fullscreen mode
<button id="pay-btn">Pay 10 USDT</button>
Enter fullscreen mode Exit fullscreen mode

💡 Want to embed the checkout inside your own page? Use the @ironix-pay/sdk — see the Embed Mode guide.


Step 4: Receive the Webhook

When your customer pays, IronixPay sends a session.completed webhook:

const crypto = require('crypto');
const WEBHOOK_SECRET = process.env.IRONIXPAY_WEBHOOK_SECRET; // whsec_...

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-signature'];
  const timestamp = req.headers['x-timestamp'];
  const payload = req.body.toString();

  // 1. Verify signature
  const message = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(message)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  // 2. Check timestamp (5-min window)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return res.status(401).send('Timestamp expired');
  }

  // 3. Process the event
  const event = JSON.parse(payload);
  if (event.event_type === 'session.completed') {
    const { session_id, amount_received } = event.data;
    console.log(`✅ Payment received! Session: ${session_id}, Amount: ${amount_received / 1_000_000} USDT`);
    // TODO: Mark order as paid in your database
  }

  res.status(200).send('OK');
});
Enter fullscreen mode Exit fullscreen mode

💡 Go to Dashboard → Developer → Webhooks to set your endpoint URL and copy the signing secret.


Complete Working Example

Copy-paste this server.js:

require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const app = express();

const SECRET_KEY = process.env.IRONIXPAY_SECRET_KEY;
const WEBHOOK_SECRET = process.env.IRONIXPAY_WEBHOOK_SECRET;
const API_BASE = 'https://sandbox.ironixpay.com';

app.use('/public', express.static('public'));
app.use(express.json());

app.post('/create-checkout', async (req, res) => {
  const response = await fetch(`${API_BASE}/v1/checkout/sessions`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: req.body.amount || 10000000,
      currency: 'USDT',
      network: 'TRON',
      success_url: `http://localhost:3000/public/success.html`,
      cancel_url: `http://localhost:3000/public/cancel.html`,
      client_reference_id: req.body.orderId,
    }),
  });
  const session = await response.json();
  res.json({ id: session.id, url: session.url });
});

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-signature'];
  const ts = req.headers['x-timestamp'];
  const body = req.body.toString();

  const expected = crypto.createHmac('sha256', WEBHOOK_SECRET)
    .update(`${ts}.${body}`).digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).send('Bad signature');
  }

  const event = JSON.parse(body);
  console.log(`[Webhook] ${event.event_type}:`, JSON.stringify(event.data, null, 2));

  if (event.event_type === 'session.completed') {
    console.log(`💰 Order paid: ${event.data.amount_received / 1e6} USDT`);
  }

  res.sendStatus(200);
});

app.listen(3000, () => console.log('🚀 http://localhost:3000'));
Enter fullscreen mode Exit fullscreen mode
# .env
IRONIXPAY_SECRET_KEY=sk_test_your_key_here
IRONIXPAY_WEBHOOK_SECRET=whsec_your_secret_here
Enter fullscreen mode Exit fullscreen mode
npm install express dotenv
node server.js
Enter fullscreen mode Exit fullscreen mode

Telegram Bot? Same API.

bot.onText(/\/pay/, async (msg) => {
  const res = await fetch(`${API_BASE}/v1/checkout/sessions`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: 5000000,
      currency: 'USDT',
      network: 'TRON',
      success_url: 'https://t.me/your_bot',
      cancel_url: 'https://t.me/your_bot',
    }),
  });
  const session = await res.json();

  bot.sendMessage(msg.chat.id, `💳 Pay 5 USDT to complete your order:`, {
    reply_markup: {
      inline_keyboard: [[{ text: '🔗 Pay Now', url: session.url }]],
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

What Happens Behind the Scenes

Your app creates session → IronixPay generates unique TRON address
                                    ↓
        Customer sends USDT → on-chain transaction detected
                                    ↓
              IronixPay confirms → webhook fired to your server
                                    ↓
           USDT auto-swept to your treasury wallet 💰
Enter fullscreen mode Exit fullscreen mode

You never touch a private key. You never run a blockchain node. IronixPay handles HD wallet derivation, transaction monitoring, and fund sweeping.


Learn More


Built with ❤️ by IronixPay — USDT payment gateway for developers.

Top comments (0)