Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/peLuis123/crypto-shop-backend/llms.txt

Use this file to discover all available pages before exploring further.

This guide explains how to integrate Socket.io to receive real-time notifications when cryptocurrency transactions are confirmed.

Overview

The Crypto Shop Backend uses Socket.io to push real-time notifications to clients when:
  • TRX payment transactions are confirmed on the blockchain
  • Order status changes from “pending” to “completed”
  • Product stock is updated

Socket.io Server Configuration

From src/config/socket.js:5, the server is configured with CORS support:
import { Server } from 'socket.io';

let io;

export const initializeSocket = (server) => {
  io = new Server(server, {
    cors: {
      origin: ['http://localhost:3000', 'http://localhost:5173', process.env.FRONTEND_URL],
      credentials: true,
      methods: ['GET', 'POST']
    }
  });

  io.on('connection', (socket) => {
    console.log(`[Socket] User connected: ${socket.id}`);

    socket.on('join-user', (userId) => {
      socket.join(`user:${userId}`);
      console.log(`[Socket] User ${userId} joined room user:${userId}`);
    });

    socket.on('disconnect', () => {
      console.log(`[Socket] User disconnected: ${socket.id}`);
    });
  });

  return io;
};

Connection Flow

1

User Connects

Client establishes WebSocket connection to the server.
2

Join User Room

Client emits join-user event with their user ID to join a private room.
3

Wait for Events

Client listens for transaction:confirmed events.
4

Receive Notification

When a transaction is confirmed, server emits event to the user’s room.

Client Setup

Installation

Install the Socket.io client library:
npm install socket.io-client

Basic Connection

import { io } from 'socket.io-client';

// Connect to the server
const socket = io('http://localhost:3000', {
  withCredentials: true,
  transports: ['websocket', 'polling']
});

// Wait for connection
socket.on('connect', () => {
  console.log('Connected to server:', socket.id);
  
  // Join your user room
  const userId = '507f1f77bcf86cd799439010'; // Your user ID
  socket.emit('join-user', userId);
  console.log('Joined user room:', userId);
});

// Listen for transaction confirmations
socket.on('transaction:confirmed', (data) => {
  console.log('Transaction confirmed!', data);
  
  // data contains:
  // - orderId: The order that was confirmed
  // - txHash: Transaction hash on blockchain
  // - message: Confirmation message
  // - timestamp: When it was confirmed
});

// Handle disconnection
socket.on('disconnect', () => {
  console.log('Disconnected from server');
});

Transaction Confirmation Event

From src/config/socket.js:37, when a transaction is confirmed:
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 Data Structure

{
  "orderId": "507f1f77bcf86cd799439013",
  "txHash": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "message": "Your purchase has been confirmed. You can now place new orders.",
  "timestamp": "2024-01-20T14:35:22.123Z"
}

Complete Integration Example

import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';

export function useTransactionNotifications(userId) {
  const [socket, setSocket] = useState(null);
  const [notifications, setNotifications] = useState([]);

  useEffect(() => {
    // Create socket connection
    const newSocket = io('http://localhost:3000', {
      withCredentials: true,
      transports: ['websocket', 'polling']
    });

    newSocket.on('connect', () => {
      console.log('Socket connected');
      newSocket.emit('join-user', userId);
    });

    newSocket.on('transaction:confirmed', (data) => {
      console.log('Transaction confirmed:', data);
      
      // Add to notifications
      setNotifications(prev => [...prev, data]);
      
      // Show browser notification
      if ('Notification' in window && Notification.permission === 'granted') {
        new Notification('Payment Confirmed', {
          body: data.message,
          icon: '/icon.png'
        });
      }
      
      // Play sound
      const audio = new Audio('/notification.mp3');
      audio.play();
    });

    newSocket.on('disconnect', () => {
      console.log('Socket disconnected');
    });

    setSocket(newSocket);

    // Cleanup
    return () => {
      newSocket.disconnect();
    };
  }, [userId]);

  return { socket, notifications };
}

// Usage in component
function App() {
  const userId = '507f1f77bcf86cd799439010';
  const { notifications } = useTransactionNotifications(userId);

  return (
    <div>
      <h2>Notifications</h2>
      {notifications.map((notif, index) => (
        <div key={index}>
          Order {notif.orderId} confirmed!
          <br />
          TX: {notif.txHash}
        </div>
      ));
    </div>
  );
}

When Notifications are Sent

From src/services/transactionListener.js:72, notifications are sent when:
  1. A pending transaction receives 21 confirmations on the blockchain
  2. The transaction type is “purchase”
  3. The order status is “pending”
if (status && status.confirmed) {
  // Update transaction status
  transaction.status = 'confirmed';
  transaction.confirmations = CONFIRMATIONS_REQUIRED;
  await transaction.save();

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

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

        // Send notification
        emitTransactionConfirmed(
          transaction.userId.toString(),
          transaction.orderId.toString(),
          transaction.transactionHash
        );
      }
    }
  }
}

Room-based Messaging

The system uses room-based messaging to ensure users only receive their own notifications:
  • Each user joins a private room: user:{userId}
  • Notifications are emitted only to the specific user’s room
  • Multiple clients can connect with the same user ID and all will receive notifications

Error Handling

import { io } from 'socket.io-client';

const socket = io('http://localhost:3000', {
  withCredentials: true,
  transports: ['websocket', 'polling'],
  reconnection: true,
  reconnectionDelay: 1000,
  reconnectionAttempts: 5
});

socket.on('connect', () => {
  console.log('Connected successfully');
});

socket.on('connect_error', (error) => {
  console.error('Connection error:', error.message);
});

socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
  
  if (reason === 'io server disconnect') {
    // Server disconnected, reconnect manually
    socket.connect();
  }
});

socket.on('reconnect', (attemptNumber) => {
  console.log('Reconnected after', attemptNumber, 'attempts');
  
  // Re-join user room
  const userId = '507f1f77bcf86cd799439010';
  socket.emit('join-user', userId);
});

socket.on('reconnect_error', (error) => {
  console.error('Reconnection error:', error);
});

socket.on('reconnect_failed', () => {
  console.error('Reconnection failed after maximum attempts');
});

Browser Notifications

Request permission and show browser notifications:
// Request notification permission
async function requestNotificationPermission() {
  if ('Notification' in window) {
    const permission = await Notification.requestPermission();
    return permission === 'granted';
  }
  return false;
}

// Setup socket with browser notifications
async function setupNotifications(userId) {
  const hasPermission = await requestNotificationPermission();
  
  const socket = io('http://localhost:3000', {
    withCredentials: true
  });

  socket.on('connect', () => {
    socket.emit('join-user', userId);
  });

  socket.on('transaction:confirmed', (data) => {
    console.log('Transaction confirmed:', data);
    
    if (hasPermission) {
      const notification = new Notification('Payment Confirmed', {
        body: `Order ${data.orderId} has been confirmed!`,
        icon: '/icon.png',
        badge: '/badge.png',
        tag: data.orderId,
        requireInteraction: false
      });

      notification.onclick = () => {
        window.focus();
        window.location.href = `/orders/${data.orderId}`;
      };
    }
  });

  return socket;
}

Testing Notifications

To test the notification system:
1

Create an Order

Use the Creating Orders guide to create an order.
2

Connect Socket.io

Connect your client and join your user room.
3

Pay for Order

Use the Payment Processing guide to pay.
4

Wait for Confirmation

The transaction listener checks every 15 seconds. You should receive a notification when the transaction is confirmed (21 confirmations on TRON blockchain).

Socket.io Events Reference

join-userJoin a user-specific room to receive notifications.
socket.emit('join-user', userId);
Parameters:
  • userId (string): The user’s MongoDB ObjectId

Production Considerations

For production deployments:
  • Use secure WebSocket connections (wss://)
  • Set secure: true in CORS configuration
  • Add rate limiting for socket events
  • Implement authentication middleware for socket connections
  • Use Redis adapter for horizontal scaling
  • Monitor socket connections and events

Next Steps

Payment Processing

Understand how payments trigger notifications

Creating Orders

Learn how to create orders that can be paid