The transaction listener is a background service that monitors pending TRON transactions and updates order statuses when transactions are confirmed on the blockchain.
How It Works
The listener runs on a polling interval, checking the status of all pending transactions in the database:
Configuration
The listener is configured in /workspace/source/src/services/transactionListener.js:16-17:
const CONFIRMATIONS_REQUIRED = 21 ;
const CHECK_INTERVAL = 15000 ; // 15 seconds
Constant Value Description CONFIRMATIONS_REQUIRED21 Number of block confirmations needed CHECK_INTERVAL15000ms Polling interval (15 seconds)
TRON produces blocks every ~3 seconds. With 21 confirmations required, transactions are finalized in approximately 1 minute .
Starting the Listener
The listener is started when the server boots:
import { startTransactionListener } from './services/transactionListener.js' ;
// Start the listener
startTransactionListener ();
Implementation in /workspace/source/src/services/transactionListener.js:102-107:
export const startTransactionListener = () => {
syncPendingTransactions (); // Run immediately
setInterval (() => {
syncPendingTransactions ();
}, CHECK_INTERVAL ); // Then every 15 seconds
};
Transaction Status Check
The listener queries the TRON blockchain to check transaction status:
~/workspace/source/src/services/transactionListener.js:19-34
export const getTransactionStatus = async ( txHash ) => {
try {
const tx = await tronWeb . trx . getTransactionInfo ( txHash );
if ( ! tx || ! tx . blockNumber ) {
return null ; // Transaction not found or not yet mined
}
return {
confirmed: true ,
confirmations: 21
};
} catch ( error ) {
return null ;
}
};
Processing Pending Transactions
The syncPendingTransactions function processes all pending transactions:
Step 1: Query Pending Transactions
~/workspace/source/src/services/transactionListener.js:36-41
export const syncPendingTransactions = async () => {
try {
const pendingTransactions = await Transaction . find ({
status: 'pending' ,
transactionHash: { $ne: null } // Must have a transaction hash
});
console . log ( `[Listener] Found ${ pendingTransactions . length } pending transactions` );
// ...
}
};
Step 2: Check Blockchain Status
~/workspace/source/src/services/transactionListener.js:45-54
for ( const transaction of pendingTransactions ) {
try {
const status = await getTransactionStatus ( transaction . transactionHash );
if ( status && status . confirmed ) {
console . log ( `[Listener] Transaction confirmed: ${ transaction . transactionHash } ` );
transaction . status = 'confirmed' ;
transaction . confirmations = CONFIRMATIONS_REQUIRED ;
transaction . updatedAt = Date . now ();
await transaction . save ();
// ...
}
}
}
Step 3: Update Order Status
When a transaction is confirmed, the listener updates the associated order:
Purchase Transaction
Refund Transaction
// ~/workspace/source/src/services/transactionListener.js:56-77
if ( transaction . orderId ) {
const order = await Order . findById ( transaction . orderId );
if ( order ) {
if ( transaction . type === 'purchase' && order . status === 'pending' ) {
order . status = 'completed' ;
order . updatedAt = Date . now ();
await order . save ();
// Deduct stock for sold products
for ( const item of order . products ) {
await Product . findByIdAndUpdate (
item . productId ,
{ $inc: { stock: - item . quantity } }
);
}
// Emit Socket.io event to notify user
emitTransactionConfirmed (
transaction . userId . toString (),
transaction . orderId . toString (),
transaction . transactionHash
);
}
}
}
Transaction Lifecycle
Real-time Notifications
When a transaction is confirmed, users receive real-time notifications via Socket.io:
~/workspace/source/src/services/transactionListener.js:72-76
emitTransactionConfirmed (
transaction . userId . toString (),
transaction . orderId . toString (),
transaction . transactionHash
);
See Socket.io Events for payload details.
Stock Management
The listener automatically manages product inventory:
Purchase Confirmation
When a purchase is confirmed, stock is decremented:
for ( const item of order . products ) {
await Product . findByIdAndUpdate (
item . productId ,
{ $inc: { stock: - item . quantity } } // Decrease stock
);
}
Refund Processing
When a refund is confirmed, stock is restored:
for ( const item of order . products ) {
await Product . findByIdAndUpdate (
item . productId ,
{ $inc: { stock: item . quantity } } // Increase stock
);
}
Error Handling
The listener uses try-catch blocks to handle errors gracefully:
for ( const transaction of pendingTransactions ) {
try {
const status = await getTransactionStatus ( transaction . transactionHash );
// Process transaction...
} catch ( error ) {
// Silent error handling - continues to next transaction
}
}
Errors are caught silently to prevent one failed transaction from stopping the entire listener. Monitor your application logs for transaction processing issues.
Monitoring
The listener logs important events:
[Listener] Found 3 pending transactions
[Listener] Transaction confirmed: 7f3e8d9c2b1a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e
[Socket] User 507f1f77bcf86cd799439011 joined room user:507f1f77bcf86cd799439011
Polling Interval : The 15-second interval balances responsiveness with API rate limits. Adjust CHECK_INTERVAL based on your needs:
Faster : 10 seconds (more responsive, higher API usage)
Slower : 30 seconds (lower API usage, less responsive)
Next Steps
Socket.io Events Learn about real-time event payloads
TRON Setup Configure TRON network connection