The Crypto Shop Backend emits Socket.io events to notify clients of transaction confirmations in real-time. This guide covers all available events and their payloads.
Event Types
Client → Server Events
Events that clients emit to the server:
Event Description Payload join-userJoin user-specific room for notifications userId: string
Server → Client Events
Events that the server emits to clients:
Event Description Trigger transaction:confirmedTransaction confirmed on blockchain Transaction listener detects confirmation
join-user Event
Clients emit this event to join a user-specific room and receive personalized notifications.
Client Implementation
import { io } from 'socket.io-client' ;
const socket = io ( 'http://localhost:3000' );
// Join user room after authentication
const userId = '507f1f77bcf86cd799439011' ; // From your auth system
socket . emit ( 'join-user' , userId );
Server Handler
~/workspace/source/src/config/socket.js:17-20
socket . on ( 'join-user' , ( userId ) => {
socket . join ( `user: ${ userId } ` );
console . log ( `[Socket] User ${ userId } joined room user: ${ userId } ` );
});
Payload:
userId : string // MongoDB ObjectId as string
Example:
socket . emit ( 'join-user' , '507f1f77bcf86cd799439011' );
Users must join their room before receiving transaction:confirmed events. The room name follows the pattern user:{userId}.
transaction:confirmed Event
Emitted when a blockchain transaction reaches the required number of confirmations (21 blocks).
Server Implementation
~/workspace/source/src/config/socket.js:37-45
export const emitTransactionConfirmed = ( userId , orderId , txHash ) => {
const io = getIO ();
io . to ( `user: ${ userId } ` ). emit ( 'transaction:confirmed' , {
orderId ,
txHash ,
message: 'Your purchase has been confirmed. You can now place new orders.' ,
timestamp: new Date ()
});
};
Event Flow
Payload Structure
interface TransactionConfirmedPayload {
orderId : string ; // MongoDB ObjectId of the confirmed order
txHash : string ; // TRON transaction hash
message : string ; // User-friendly message
timestamp : Date ; // When the confirmation occurred
}
Example Payload
{
"orderId" : "65f8a9b2c3d4e5f6a7b8c9d0" ,
"txHash" : "7f3e8d9c2b1a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e" ,
"message" : "Your purchase has been confirmed. You can now place new orders." ,
"timestamp" : "2026-03-04T15:30:45.123Z"
}
Client Event Listeners
React Example
import { useEffect , useState } from 'react' ;
import { io } from 'socket.io-client' ;
import { toast } from 'react-toastify' ;
function OrderPage () {
const [ socket , setSocket ] = useState ( null );
const userId = '507f1f77bcf86cd799439011' ; // From auth context
useEffect (() => {
// Connect to Socket.io
const newSocket = io ( 'http://localhost:3000' );
setSocket ( newSocket );
// Join user room
newSocket . emit ( 'join-user' , userId );
// Listen for transaction confirmations
newSocket . on ( 'transaction:confirmed' , ( data ) => {
console . log ( 'Transaction confirmed:' , data );
// Show success notification
toast . success ( data . message );
// Update UI
updateOrderStatus ( data . orderId , 'completed' );
// Optionally redirect or refresh
// window.location.href = `/orders/${data.orderId}`;
});
// Cleanup
return () => {
newSocket . off ( 'transaction:confirmed' );
newSocket . disconnect ();
};
}, [ userId ]);
return (
< div >
{ /* Your order page UI */ }
</ div >
);
}
Vue.js Example
< script setup >
import { onMounted , onUnmounted , inject } from 'vue';
import { useToast } from 'vue-toastification';
const socket = inject('socket');
const toast = useToast();
const userId = '507f1f77bcf86cd799439011'; // From auth store
onMounted(() => {
// Join user room
socket . emit ( 'join-user' , userId );
// Listen for confirmations
socket . on ( 'transaction:confirmed' , ( data ) => {
toast . success ( data . message );
console . log ( 'Order confirmed:' , data . orderId );
});
} );
onUnmounted(() => {
socket . off ( 'transaction:confirmed' );
} );
</ script >
Vanilla JavaScript
const socket = io ( 'http://localhost:3000' );
const userId = '507f1f77bcf86cd799439011' ;
// Join user room
socket . emit ( 'join-user' , userId );
// Listen for transaction confirmations
socket . on ( 'transaction:confirmed' , ( data ) => {
// Show notification
showNotification ({
title: 'Transaction Confirmed!' ,
message: data . message ,
type: 'success'
});
// Update order card in the DOM
const orderCard = document . querySelector ( `[data-order-id=" ${ data . orderId } "]` );
if ( orderCard ) {
orderCard . classList . add ( 'confirmed' );
orderCard . querySelector ( '.status' ). textContent = 'Completed' ;
}
// Log transaction details
console . log ( 'Transaction Hash:' , data . txHash );
console . log ( 'Confirmed at:' , data . timestamp );
});
Event Timing
Understanding when events are triggered:
Transaction Lifecycle
Key Timings:
Block time : ~3 seconds per block
Required confirmations : 21 blocks
Minimum time to confirmation : ~63 seconds
Listener check interval : 15 seconds (configured in /workspace/source/src/services/transactionListener.js:17)
Use Cases
1. Show Success Notification
socket . on ( 'transaction:confirmed' , ( data ) => {
// Show toast notification
toast . success ( data . message , {
duration: 5000 ,
icon: '✓'
});
});
2. Update Order Status in UI
socket . on ( 'transaction:confirmed' , ( data ) => {
// Update order in state management (Redux/Vuex/etc.)
dispatch ({
type: 'UPDATE_ORDER_STATUS' ,
payload: {
orderId: data . orderId ,
status: 'completed' ,
confirmedAt: data . timestamp
}
});
});
3. Redirect to Order Details
socket . on ( 'transaction:confirmed' , ( data ) => {
// Show notification and redirect
toast . success ( 'Payment confirmed! Redirecting...' );
setTimeout (() => {
window . location . href = `/orders/ ${ data . orderId } ` ;
}, 2000 );
});
4. Enable New Purchases
socket . on ( 'transaction:confirmed' , ( data ) => {
// Re-enable purchase buttons
const purchaseButtons = document . querySelectorAll ( '.purchase-btn' );
purchaseButtons . forEach ( btn => {
btn . disabled = false ;
btn . textContent = 'Buy Now' ;
});
// Update user's pending order status
setHasPendingOrder ( false );
});
5. Track Analytics
socket . on ( 'transaction:confirmed' , ( data ) => {
// Send event to analytics
analytics . track ( 'Purchase Completed' , {
orderId: data . orderId ,
transactionHash: data . txHash ,
confirmedAt: data . timestamp
});
// Send to monitoring
console . log ( '[Analytics] Purchase completed:' , data . orderId );
});
Error Handling
Handle potential issues with event listeners:
socket . on ( 'transaction:confirmed' , ( data ) => {
try {
// Validate payload
if ( ! data . orderId || ! data . txHash ) {
console . error ( 'Invalid transaction:confirmed payload:' , data );
return ;
}
// Update UI
updateOrderStatus ( data . orderId , 'completed' );
// Show notification
toast . success ( data . message );
} catch ( error ) {
console . error ( 'Error handling transaction:confirmed:' , error );
toast . error ( 'Failed to process confirmation' );
}
});
// Handle connection errors
socket . on ( 'connect_error' , ( error ) => {
console . error ( 'Socket connection error:' , error );
toast . error ( 'Real-time updates temporarily unavailable' );
});
// Handle disconnections
socket . on ( 'disconnect' , ( reason ) => {
console . warn ( 'Socket disconnected:' , reason );
if ( reason === 'io server disconnect' ) {
// Server forcefully disconnected, reconnect manually
socket . connect ();
}
});
Testing Events
Manual Testing
Connect to server :
const socket = io ( 'http://localhost:3000' );
socket . on ( 'connect' , () => console . log ( 'Connected' ));
Join user room :
const userId = '507f1f77bcf86cd799439011' ;
socket . emit ( 'join-user' , userId );
Listen for events :
socket . on ( 'transaction:confirmed' , ( data ) => {
console . log ( 'Received event:' , data );
});
Trigger a transaction (make a purchase) and wait ~1 minute for confirmation
Simulating Events (Development)
For testing without waiting for blockchain confirmation:
import { getIO } from './src/config/socket.js' ;
// Emit test event
const io = getIO ();
io . to ( 'user:507f1f77bcf86cd799439011' ). emit ( 'transaction:confirmed' , {
orderId: '65f8a9b2c3d4e5f6a7b8c9d0' ,
txHash: 'test_transaction_hash_12345' ,
message: 'TEST: Your purchase has been confirmed.' ,
timestamp: new Date ()
});
Best Practices
Always Clean Up Event Listeners
Prevent memory leaks by removing listeners when components unmount: useEffect (() => {
const handler = ( data ) => handleConfirmation ( data );
socket . on ( 'transaction:confirmed' , handler );
return () => {
socket . off ( 'transaction:confirmed' , handler );
};
}, []);
Always validate incoming data before using it: socket . on ( 'transaction:confirmed' , ( data ) => {
if ( ! data ?. orderId || ! data ?. txHash ) {
console . error ( 'Invalid payload' );
return ;
}
// Process valid data
});
Re-join user rooms after reconnection: socket . on ( 'connect' , () => {
if ( userId ) {
socket . emit ( 'join-user' , userId );
}
});
If Socket.io fails, poll the API as a fallback: const pollOrderStatus = async ( orderId ) => {
const response = await fetch ( `/api/orders/ ${ orderId } ` );
const order = await response . json ();
return order . status ;
};
socket . on ( 'connect_error' , () => {
// Fall back to polling every 15 seconds
const interval = setInterval (() => {
pollOrderStatus ( currentOrderId );
}, 15000 );
});
Debugging
Enable Socket.io debugging for troubleshooting:
import { io } from 'socket.io-client' ;
const socket = io ( 'http://localhost:3000' , {
// Enable debug logs
debug: true
});
// Or use localStorage in browser
localStorage . debug = 'socket.io-client:*' ;
Server-side debugging:
DEBUG = socket.io:* npm run dev
Next Steps
Socket.io Setup Configure Socket.io client connection
Transaction Listener Understand transaction monitoring