Skip to main content
This guide explains how to process cryptocurrency payments using TRON (TRX) and handle transaction confirmations.

Payment Flow Overview

1

Create Order

Create an order with products (see Creating Orders).
2

Initiate Payment

Send TRX from user’s wallet to merchant address using the Pay Order endpoint.
3

Transaction Broadcast

Transaction is broadcast to TRON network and a transaction hash is returned.
4

Pending Confirmation

Order status remains “pending” while waiting for blockchain confirmations.
5

Blockchain Confirmation

Transaction listener monitors the blockchain and confirms after 21 confirmations.
6

Order Completed

Order status updates to “completed” and product stock is reduced. User receives real-time notification.

Pay for an Order

Send TRX payment for a pending order.

Endpoint

POST /api/orders/:id/pay
Requires authentication. The order must belong to the authenticated user and have “pending” status.

URL Parameters

  • id: The MongoDB ObjectId of the order

Request

No request body required. The payment details are retrieved from the order.

Response

{
  "success": true,
  "message": "Payment sent. Waiting for blockchain confirmation.",
  "order": {
    "_id": "507f1f77bcf86cd799439013",
    "orderId": "#TRX-1001",
    "userId": "507f1f77bcf86cd799439010",
    "products": [
      {
        "productId": "507f1f77bcf86cd799439011",
        "name": "Wireless Headphones",
        "price": 29.99,
        "quantity": 2,
        "color": "Black"
      }
    ],
    "subtotal": 59.98,
    "networkFee": -0.01,
    "total": 59.97,
    "status": "pending",
    "transactionHash": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
    "walletAddress": "TYwXGhKhqPTbLvRPFdvZnXqcqN4kVy7ABC",
    "merchantAddress": "TXYZMerchantAddressHere123456789",
    "createdAt": "2024-01-20T14:30:00.000Z",
    "updatedAt": "2024-01-20T14:32:00.000Z"
  },
  "transaction": {
    "hash": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
    "from": "TYwXGhKhqPTbLvRPFdvZnXqcqN4kVy7ABC",
    "to": "TXYZMerchantAddressHere123456789",
    "amount": 59.97,
    "status": "pending"
  }
}

Implementation Example

const orderId = '507f1f77bcf86cd799439013';

const response = await fetch(
  `http://localhost:3000/api/orders/${orderId}/pay`,
  {
    method: 'POST',
    credentials: 'include'
  }
);

const data = await response.json();

if (data.success) {
  console.log('Payment sent!');
  console.log('Transaction hash:', data.transaction.hash);
  console.log('Status:', data.transaction.status);
  
  // Now wait for real-time confirmation via Socket.io
} else {
  console.error('Payment failed:', data.error);
}

Payment Processing Logic

From src/api/orders/payOrder.js:41, the payment process:
// Check user balance
const balance = await getBalance(user.wallet.address);

if (balance < Math.abs(order.total)) {
  return res.status(400).json({ 
    error: 'Insufficient balance',
    code: 'INSUFFICIENT_BALANCE',
    userBalance: balance,
    requiredAmount: Math.abs(order.total)
  });
}

// Send TRX transaction
const tx = await sendTRX(
  user.wallet.privateKey,
  order.merchantAddress,
  Math.abs(order.total)
);

const txHash = tx.txid || tx.txID || tx.transaction?.txID;

// Create transaction record
const transaction = new Transaction({
  userId,
  orderId: order._id,
  type: 'purchase',
  amount: Math.abs(order.total),
  currency: 'TRX',
  network: 'TRC-20',
  transactionHash: txHash,
  fromAddress: order.walletAddress,
  toAddress: order.merchantAddress,
  status: 'pending',
  confirmations: 0
});

await transaction.save();
order.transactionHash = txHash;
await order.save();

Transaction Confirmation Process

The system automatically monitors pending transactions using a background service.

Configuration

From src/services/transactionListener.js:16:
const CONFIRMATIONS_REQUIRED = 21;
const CHECK_INTERVAL = 15000; // 15 seconds

Transaction Listener

The transaction listener runs continuously and checks pending transactions every 15 seconds:
export const syncPendingTransactions = async () => {
  // Find all pending transactions
  const pendingTransactions = await Transaction.find({
    status: 'pending',
    transactionHash: { $ne: null }
  });

  console.log(`[Listener] Found ${pendingTransactions.length} pending transactions`);

  for (const transaction of pendingTransactions) {
    // Check transaction status on blockchain
    const status = await getTransactionStatus(transaction.transactionHash);

    if (status && status.confirmed) {
      console.log(`[Listener] Transaction confirmed: ${transaction.transactionHash}`);
      
      // Update transaction status
      transaction.status = 'confirmed';
      transaction.confirmations = CONFIRMATIONS_REQUIRED;
      transaction.updatedAt = Date.now();
      await transaction.save();

      // Update order status
      if (transaction.orderId) {
        const order = await Order.findById(transaction.orderId);
        if (order && transaction.type === 'purchase' && order.status === 'pending') {
          order.status = 'completed';
          order.updatedAt = Date.now();
          await order.save();

          // Reduce product stock
          for (const item of order.products) {
            await Product.findByIdAndUpdate(
              item.productId,
              { $inc: { stock: -item.quantity } }
            );
          }

          // Emit real-time notification
          emitTransactionConfirmed(
            transaction.userId.toString(),
            transaction.orderId.toString(),
            transaction.transactionHash
          );
        }
      }
    }
  }
};

What Happens on Confirmation

When a transaction is confirmed (from src/services/transactionListener.js:49):
1

Transaction Status Updated

Transaction status changes from pending to confirmed with 21 confirmations.
2

Order Status Updated

Order status changes from pending to completed.
3

Product Stock Reduced

Stock is decreased for each product in the order using $inc operator.
4

Real-time Notification Sent

Socket.io event is emitted to notify the user (see Real-time Notifications).

Checking Transaction Status

From src/services/transactionListener.js:19, you can check a transaction status:
export const getTransactionStatus = async (txHash) => {
  try {
    const tx = await tronWeb.trx.getTransactionInfo(txHash);
    
    if (!tx || !tx.blockNumber) {
      return null;
    }
    
    return {
      confirmed: true,
      confirmations: 21
    };
  } catch (error) {
    return null;
  }
};

Payment Error Handling

class PaymentService {
  async payOrder(orderId) {
    try {
      const response = await fetch(
        `http://localhost:3000/api/orders/${orderId}/pay`,
        {
          method: 'POST',
          credentials: 'include'
        }
      );

      if (!response.ok) {
        const error = await response.json();
        
        if (error.code === 'INSUFFICIENT_BALANCE') {
          throw new Error(
            `Insufficient balance. You have ${error.userBalance} TRX but need ${error.requiredAmount} TRX`
          );
        }
        
        throw new Error(error.error || 'Payment failed');
      }

      const data = await response.json();
      
      console.log('Payment successful!');
      console.log('Transaction hash:', data.transaction.hash);
      console.log('Please wait for blockchain confirmation...');
      
      return data;
    } catch (error) {
      console.error('Payment error:', error.message);
      throw error;
    }
  }
}

Transaction States

Transaction has been broadcast to the blockchain but not yet confirmed.
  • Order status: pending
  • Confirmations: 0
  • Stock: Not yet reduced
  • User action: Wait for confirmation

Payment Security

Important security considerations:
  • User wallet private keys are stored encrypted in the database
  • Transactions are signed server-side using the user’s private key
  • Balance is verified before processing payment
  • Order ownership is verified before payment
  • Only pending orders can be paid
  • Transaction hash is stored for verification and auditing

Monitoring Payments

The transaction listener automatically starts when the server boots:
// Transaction listener checks every 15 seconds
startTransactionListener();
You can monitor the logs to see transaction confirmations:
[Listener] Found 3 pending transactions
[Listener] Transaction confirmed: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0
[Socket] User 507f1f77bcf86cd799439010 notified of confirmation

Next Steps

Real-time Notifications

Set up Socket.io to receive instant payment confirmations

Admin Dashboard

Monitor all transactions and sales from the admin panel

Build docs developers (and LLMs) love