Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🏦 Ledger Banking API

A production-grade banking transaction system backend built with Node.js, Express, and MongoDB — designed to mirror real-world banking architectures with a focus on data consistency, auditability, and secure financial operations.

🎯 Built using a Ledger-based architecture — balances are never hardcoded, they are always derived from actual transaction history, guaranteeing full data integrity.

🌐 Live API: https://backend-ledger-f0dy.onrender.com

Project Overview


📌 Table of Contents


🏗 Architecture Overview

Client Request
      │
      ▼
  Express Router
      │
      ▼
Auth Middleware (JWT Verify)
      │
      ▼
  Controller
      │
      ▼
  Service Layer
      │
      ▼
  MongoDB (Atlas)
  ┌──────────────────────────────────────┐
  │  Users │ Accounts │ Transactions     │
  │                   │ Ledger Entries   │
  └──────────────────────────────────────┘
      │
      ▼
  Nodemailer (Gmail OAuth)
  [Email Alerts on Account Events]

The project follows a modular MVC-style architecture with clear separation between routes, controllers, services, and models.


🗺 System Architecture Diagram

System Architecture Diagram


✨ Features

  • 🔐 JWT Authentication with bcrypt password hashing
  • 📧 Email Notifications via Nodemailer + Google Gmail OAuth
  • 🏦 Multi-Account Support per user
  • 📒 Ledger-Based Balance Calculation — no hardcoded balances, ever
  • 🔁 Idempotency Keys — prevents duplicate transactions on retry
  • 📊 MongoDB Aggregation Pipeline — dynamic balance computation
  • 🛡️ Auth Middleware on all protected routes
  • 🔑 System-level access control for fund injection
  • ☁️ Deployed on Render with secured environment variables

🛠 Tech Stack

Layer Technology
Runtime Node.js
Framework Express.js
Database MongoDB Atlas + Mongoose
Authentication JWT + bcrypt
Email Service Nodemailer + Google Gmail API (OAuth2)
Deployment Render

📁 Project Structure

ledger-api/
├── controllers/
│   ├── authController.js
│   ├── accountController.js
│   └── transactionController.js
├── models/
│   ├── User.js
│   ├── Account.js
│   ├── Transaction.js
│   └── Ledger.js
├── routes/
│   ├── authRoutes.js
│   ├── accountRoutes.js
│   └── transactionRoutes.js
├── middleware/
│   ├── authMiddleware.js
│   └── authSystemUserMiddleware.js
├── services/
│   └── emailService.js
├── .env
├── app.js
└── server.js

🚀 Getting Started

Prerequisites

  • Node.js v18+
  • MongoDB Atlas account
  • Google Cloud project with Gmail API enabled (for email notifications)

Installation

# Clone the repository
git clone https://github.com/your-username/ledger-api.git
cd ledger-api

# Install dependencies
npm install

# Setup environment variables
cp .env.example .env
# Fill in your values in .env

# Start development server
npm run dev

🔑 Environment Variables

Create a .env file in the root directory:

PORT=3000

# MongoDB
MONGO_URI=mongodb+srv://<username>:<password>@cluster.mongodb.net/ledger

# JWT
JWT_SECRET=your_super_secret_jwt_key

# System User (for initial funds injection)
SYSTEM_SECRET=your_system_level_secret

# Google Gmail OAuth2 (Nodemailer)
GMAIL_CLIENT_ID=your_google_client_id
GMAIL_CLIENT_SECRET=your_google_client_secret
GMAIL_REFRESH_TOKEN=your_refresh_token
GMAIL_USER=your_email@gmail.com

⚠️ Never commit your .env file. Add it to .gitignore.


📡 API Reference

Protected routes require the Authorization: Bearer <token> header.


🔐 Auth Routes

Base: /api/v1/auth

Method Endpoint Auth Description
POST /register Register a new user
POST /login Login and receive JWT
POST /logout Logout user

Register

POST /api/v1/auth/register
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "secret123"
}

Response:

{
  "success": true,
  "message": "User registered successfully",
  "token": "<jwt_token>"
}

Login

POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "john@example.com",
  "password": "secret123"
}

Response:

{
  "success": true,
  "token": "<jwt_token>"
}

Register — 201 Created Register

Login — 200 OK Login

📧 Email Notification on Registration Email Notification


🏦 Account Routes

Base: /api/v1/account

Method Endpoint Auth Description
POST / ✅ User Create a new bank account
GET / ✅ User Get all accounts of logged-in user
GET /balance/:accountId ✅ User Get real-time balance via ledger aggregation

💡 Balance is computed dynamically using MongoDB Aggregation Pipeline by summing all credit and debit ledger entries — not fetched from a static field.

Create Account

POST /api/v1/account
Authorization: Bearer <token>
Content-Type: application/json

{
  "accountName": "Savings Account"
}

Get Balance

GET /api/v1/account/balance/:accountId
Authorization: Bearer <token>

Response:

{
  "success": true,
  "accountId": "abc123",
  "balance": 15000
}

Create Account — 201 Created Create Account

Get Balance — 200 OK Get Balance


💸 Transaction Routes

Base: /api/v1/transaction

Method Endpoint Auth Description
POST /transfer ✅ User Transfer funds between accounts
POST /initial-funds ✅ System Inject initial funds into an account

Transfer Funds

POST /api/v1/transaction/transfer
Authorization: Bearer <token>
Content-Type: application/json

{
  "fromAccountId": "abc123",
  "toAccountId": "xyz789",
  "amount": 500,
  "idempotencyKey": "unique-key-12345"
}

Response:

{
  "success": true,
  "message": "Transfer successful",
  "transactionId": "txn_98765"
}

Initial Funds Injection (System Only)

POST /api/v1/transaction/initial-funds
Authorization: Bearer <system_token>
Content-Type: application/json

{
  "accountId": "abc123",
  "amount": 10000
}

Initial Funds — 201 Created Initial Funds

Transfer Funds — 201 Created Transfer

📧 Email Alert on Transfer Transaction Email


📒 How the Ledger System Works

Instead of storing a balance field directly on an account, every financial event is stored as a Ledger Entry:

Account Created
      │
      ▼
Initial Funds Added → Ledger Entry { type: "credit", amount: 10000 }
      │
      ▼
Transfer of ₹500  → Ledger Entry { type: "debit",  amount: 500,  fromAccount: A }
                  → Ledger Entry { type: "credit", amount: 500,  toAccount:   B }
      │
      ▼
Balance Query
  = SUM(all credits) - SUM(all debits)
  = Calculated via MongoDB Aggregation Pipeline

Why this matters:

  • ✅ Full audit trail of every rupee
  • ✅ Balance is always derivable from history
  • ✅ No risk of balance going out of sync
  • ✅ Mirrors how real banks operate

🔁 Idempotency Keys

To prevent duplicate transactions (e.g., if a client retries a failed request), every transfer request accepts an idempotencyKey.

Client sends Transfer Request with idempotencyKey: "order-pay-001"
      │
      ▼
Server checks: Has this key been processed before?
      │
      ├── YES → Return original response (no duplicate transaction)
      │
      └── NO  → Process transaction, store key, return response

This is a critical requirement in real-world payment systems to ensure that network retries don't result in double charges.


☁️ Deployment

This API is deployed on Render.

Key deployment practices followed:

  • All secrets stored as environment variables on Render dashboard
  • node_modules/ and .env excluded via .gitignore
  • MongoDB Atlas connection string whitelisted for Render's IP range

📄 License

MIT © Priyanshu Varshney


Built with ❤️ following production-grade banking architecture principles

About

Backend Ledger System — A production-style banking backend built with Node.js, Express, and MongoDB, featuring JWT authentication, ledger-based transaction management, and secure money transfer system with auditability and idempotency support.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages