Bhupesh Kumar - Student Developer

Bhupesh

Student • Dev • Ailurophile

Current page: Unknown
← Back to Blog
2025-09-018 min read

Building a Serverless Trading Platform Backend: SEBI Hackathon Project

A complete trading platform backend built with Hono.js and Cloudflare Workers featuring authentication, KYC compliance, and trading capabilities

BackendTradingFinTechServerlessSecurity

Introduction

For the SEBI hackathon, our team built a complete trading platform backend using modern serverless technologies. The project demonstrates how to create a secure, scalable financial application with proper authentication, KYC compliance, balance management, and trading capabilities.

This blog post walks through the technical implementation of our trading platform backend built with Hono.js and deployed on Cloudflare Workers.

Technology Stack

  • Backend Framework: Hono.js with TypeScript
  • Database: Cloudflare D1 (SQLite)
  • Deployment: Cloudflare Workers
  • Authentication: JWT with SHA-256 password hashing

Project Architecture

The application follows a clean REST API architecture with these core modules:

src/
├── routes/
│   ├── auth.ts          # User authentication
│   ├── kyc.ts           # KYC management  
│   ├── balance.ts       # Balance operations
│   └── trading.ts       # Stock trading
└── index.ts             # Main application

Main Application Setup

import { Hono } from 'hono'
import auth from './routes/auth'
import kyc from './routes/kyc'
import balance from './routes/balance'
import trading from './routes/trading'

type Bindings = {
  sebi_trading_db: D1Database
}

const app = new Hono<{ Bindings: Bindings }>()

app.route('/auth', auth)
app.route('/kyc', kyc)  
app.route('/balance', balance)
app.route('/trading', trading)

Database Design

We implemented a relational database schema with four main tables:

Users Table

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT UNIQUE NOT NULL,
    email TEXT UNIQUE NOT NULL,
    phone TEXT NOT NULL,
    password_hash TEXT NOT NULL,
    name TEXT NOT NULL,
    kyc_status TEXT DEFAULT 'not_registered',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

KYC Table

CREATE TABLE kyc (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    pan TEXT NOT NULL,
    status TEXT DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    validated_at DATETIME,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

Balance Management

CREATE TABLE balance (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    amount DECIMAL(15,2) DEFAULT 0.00,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE balance_transactions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    type TEXT NOT NULL, -- 'credit' or 'debit'
    amount DECIMAL(15,2) NOT NULL,
    description TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

Core Features Implementation

1. Authentication System

We implemented secure user authentication with password hashing and JWT tokens:

// Password hashing using SHA-256
async function hashPassword(password: string): Promise<string> {
  const encoder = new TextEncoder()
  const data = encoder.encode(password)
  const hashBuffer = await crypto.subtle.digest('SHA-256', data)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
}

// JWT token generation
const payload = {
  sub: result.id.toString(),
  username: result.username,
  exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, // 24 hours
  iat: Math.floor(Date.now() / 1000)
}

const secret = 'hackathon-secret-key-2024'
const token = await sign(payload, secret)

Endpoints:

  • POST /auth/signup - User registration
  • POST /auth/login - User authentication

2. KYC Management System

The KYC system handles user verification with PAN card validation:

// PAN format validation
const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/
if (!panRegex.test(pan.toUpperCase())) {
  return c.json({
    success: false,
    error: 'Invalid PAN format. PAN should be 10 characters (5 letters + 4 digits + 1 letter)'
  }, 400)
}

// Check for existing KYC
const existingKyc = await checkKycExists(c.env.sebi_trading_db, userId)
if (existingKyc) {
  return c.json({
    success: false,
    error: 'KYC already registered for this user',
    existingKyc: {
      id: existingKyc.id,
      pan: existingKyc.pan,
      status: existingKyc.status
    }
  }, 400)
}

Endpoints:

  • POST /kyc/register - Register KYC with PAN
  • POST /kyc/validate - Validate KYC status
  • GET /kyc/status - Check current KYC status

3. Balance Management

The balance system includes minimum balance enforcement and comprehensive transaction tracking:

const MINIMUM_BALANCE = 1000

// Balance addition with validation
const currentBalance = await getOrCreateBalance(c.env.sebi_trading_db, userId)
const currentAmount = safeParseAmount(currentBalance.amount)
const newBalance = currentAmount + addBalance

// Update balance and record transaction
await c.env.sebi_trading_db.prepare(`
  UPDATE balance SET amount = ?, updated_at = ? WHERE user_id = ?
`).bind(newBalance, new Date().toISOString(), userId).run()

await c.env.sebi_trading_db.prepare(`
  INSERT INTO balance_transactions (user_id, type, amount, description)
  VALUES (?, ?, ?, ?)
`).bind(userId, 'credit', addBalance, 'Balance added').run()

Low Balance Alert System:

const isLowBalance = currentAmount < MINIMUM_BALANCE
const shortfall = isLowBalance ? MINIMUM_BALANCE - currentAmount : 0

return c.json({
  success: true,
  alert: {
    isLowBalance: isLowBalance,
    shortfall: shortfall,
    message: isLowBalance ? 
      `Your balance is ₹${currentAmount}. Minimum required is ₹${MINIMUM_BALANCE}. Please recharge ₹${shortfall}.` :
      'Your balance is sufficient for trading.'
  }
})

Endpoints:

  • POST /balance/add - Add money to account
  • GET /balance/check - Check current balance
  • GET /balance/check-low-balance - Check if balance is low
  • GET /balance/alert - Get balance alerts for frontend
  • GET /balance/transactions - Get transaction history with pagination

4. Trading System

The trading system handles stock purchases and sales with proper validation and portfolio management:

Stock Purchase

// Validate purchase requirements
if (currentBalance < totalCost) {
  return c.json({
    success: false,
    error: 'Insufficient balance for this purchase',
    details: {
      requiredAmount: totalCost,
      currentBalance: currentBalance,
      shortfall: totalCost - currentBalance
    }
  }, 400)
}

// Check minimum balance violation
const remainingBalance = currentBalance - totalCost
if (remainingBalance < MINIMUM_BALANCE) {
  return c.json({
    success: false,
    error: 'Purchase would leave insufficient balance'
  }, 400)
}

Portfolio Management

// Update or create portfolio entry
const existingPortfolio = await c.env.sebi_trading_db.prepare(`
  SELECT * FROM portfolio WHERE user_id = ? AND stock_name = ?
`).bind(userId, stockName.toUpperCase()).first()

if (existingPortfolio) {
  // Update existing holding
  const newQuantity = Number(existingPortfolio.quantity) + quantity
  const newTotalInvestment = safeParseAmount(existingPortfolio.total_investment) + totalCost
  const newAveragePrice = newTotalInvestment / newQuantity
  
  await c.env.sebi_trading_db.prepare(`
    UPDATE portfolio SET quantity = ?, average_price = ?, total_investment = ?
    WHERE user_id = ? AND stock_name = ?
  `).bind(newQuantity, newAveragePrice, newTotalInvestment, userId, stockName).run()
} else {
  // Create new portfolio entry
  await c.env.sebi_trading_db.prepare(`
    INSERT INTO portfolio (user_id, stock_name, quantity, average_price, total_investment)
    VALUES (?, ?, ?, ?, ?)
  `).bind(userId, stockName, quantity, price, totalCost).run()
}

Stock Sale with Profit/Loss Calculation

// Calculate profit/loss
const saleProceeds = price * quantity
const averagePrice = safeParseAmount(portfolioEntry.average_price)
const profitLoss = (price - averagePrice) * quantity
const profitLossPercentage = ((price - averagePrice) / averagePrice) * 100

// Update portfolio or remove if fully sold
const remainingQuantity = ownedQuantity - quantity
if (remainingQuantity === 0) {
  await c.env.sebi_trading_db.prepare(`
    DELETE FROM portfolio WHERE user_id = ? AND stock_name = ?
  `).bind(userId, stockName).run()
}

Endpoints:

  • POST /trading/buy - Purchase stocks
  • POST /trading/sell - Sell stocks
  • GET /trading/portfolio - View current portfolio

Security Implementation

Token Verification Middleware

async function verifyToken(c: any) {
  const authHeader = c.req.header('Authorization')
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return c.json({
      success: false,
      error: 'Authorization header required'
    }, 401)
  }

  const token = authHeader.substring(7)
  try {
    const secret = 'hackathon-secret-key-2024'
    const payload = await verify(token, secret)
    return payload
  } catch (error) {
    return c.json({
      success: false,
      error: 'Invalid or expired token'
    }, 401)
  }
}

Input Validation

All endpoints include comprehensive input validation:

  • Data type checking
  • Range validation (positive numbers for amounts)
  • Format verification (PAN format validation)
  • Business logic validation (sufficient balance, stock ownership)

Database Security

  • Parameterized queries to prevent SQL injection
  • Foreign key constraints for data integrity
  • Proper indexing for performance

API Response Design

All endpoints follow a consistent response structure:

Success Response

{
  "success": true,
  "message": "Operation completed successfully",
  "data": {...},
  "metadata": {...}
}

Error Response

{
  "success": false,
  "error": "Detailed error message",
  "details": {
    "additionalInfo": "..."
  }
}

Performance Optimizations

Database Indexing

CREATE INDEX idx_kyc_user_id ON kyc(user_id);
CREATE INDEX idx_balance_user_id ON balance(user_id);
CREATE INDEX idx_balance_transactions_user_id ON balance_transactions(user_id);

Safe Amount Parsing

function safeParseAmount(amount: any): number {
  if (amount === null || amount === undefined) return 0
  const parsed = parseFloat(amount)
  return isNaN(parsed) ? 0 : parsed
}

Efficient Balance Management

async function getOrCreateBalance(db: D1Database, userId: string) {
  let balanceRecord = await db.prepare(`
    SELECT * FROM balance WHERE user_id = ?
  `).bind(userId).first()
  
  if (!balanceRecord) {
    await db.prepare(`
      INSERT INTO balance (user_id, amount) VALUES (?, 0.00)
    `).bind(userId).run()
    balanceRecord = await db.prepare(`
      SELECT * FROM balance WHERE user_id = ?
    `).bind(userId).first()
  }
  
  return balanceRecord
}

Testing

The project includes comprehensive test cases covering all endpoints:

  • User registration and authentication flow
  • KYC registration, validation, and status checking
  • Balance operations and transaction history
  • Stock trading operations (buy/sell)
  • Portfolio management
  • Error handling scenarios

All test cases are documented with curl commands and expected responses.

Deployment

The application is deployed on Cloudflare Workers with:

Configuration (wrangler.toml)

name = "sebi-hackathon"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "sebi_trading_db"
database_name = "sebi-trading-db"

Deployment Commands

npm run dev      # Development server
npm run deploy   # Production deployment

Key Features Implemented

Secure Authentication
KYC Management
Balance Management
Trading Operations
Transaction History
Portfolio Tracking
Smart Alerts
Error Handling

Conclusion

This SEBI hackathon project demonstrates how modern serverless technologies can be used to build a comprehensive trading platform backend. The combination of Hono.js and Cloudflare Workers provides excellent performance, scalability, and developer experience.

The implementation covers all essential features of a trading platform including secure authentication, regulatory compliance through KYC, proper balance management, and complete trading operations with portfolio tracking.

The serverless architecture ensures global low latency, automatic scaling, and cost-effective operations, making it suitable for both development and production environments.

Project Links