Skip to content

Avinash-sdbegin/ledger-vault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

ledger-vault (Transactions + Ledger)

Node.js + Express + MongoDB based ledger service that supports:

  • User register/login/logout (JWT stored in cookie, token also returned in response)
  • Creating accounts for logged-in users
  • Getting account balance derived from immutable ledger entries
  • Creating transfers with idempotency protection
  • (Optional) “system user” endpoint for initial-funds credit

Note: This repo contains only the backend.


Tech Stack

  • Node.js (LTS recommended)
  • Express
  • MongoDB + Mongoose
  • JWT auth (cookie + Authorization: Bearer <token> supported)
  • Nodemailer (Gmail OAuth2)

Folder Structure

Backend/
	server.js
	package.json
	src/
		app.js
		config/db.js
		controllers/
		middleware/
		models/
		routes/
		services/

Setup (Local)

1) Install dependencies

From project root:

cd Backend
npm install

2) Create .env

Create a .env file inside the Backend/ folder (same level as server.js).

Minimum required:

MONGO_URI=mongodb://127.0.0.1:27017/backend-ledger
JWT_SECRET=your-super-secret

Email (optional but recommended; the code attempts to connect on startup):

EMAIL_USER=your-email@gmail.com
CLIENT_ID=google-oauth-client-id
CLIENT_SECRET=google-oauth-client-secret
REFRESH_TOKEN=google-oauth-refresh-token

If you don’t want email during local testing, you can still run the server, but you may see email connection errors in logs.

3) Run the server

# dev (nodemon)
npm run dev

# production
npm start

Server runs on:

  • http://localhost:3000

Health check:

  • GET /Ledger Service is up and running

Authentication

On successful register/login:

  • A JWT is set as cookie token
  • The same token is also returned in JSON

Protected routes accept token via:

  • Cookie: token=<jwt>
  • Header: Authorization: Bearer <jwt>

Logout (POST /api/auth/logout) blacklists the token in MongoDB for ~3 days (TTL index).


API

Base URL: http://localhost:3000

Auth

Register

  • POST /api/auth/register

Body:

{ "email": "user@example.com", "password": "secret123", "name": "User" }

Login

  • POST /api/auth/login

Body:

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

Logout

  • POST /api/auth/logout

Accounts (Protected)

Create account

  • POST /api/accounts/

Response returns the created account document.

List my accounts

  • GET /api/accounts/

Get account balance

  • GET /api/accounts/balance/:accountId

Balance is computed from ledger aggregation: balance = totalCredit - totalDebit.


Transactions (Protected)

Create transfer

  • POST /api/transactions/

Body:

{
	"fromAccount": "<accountId>",
	"toAccount": "<accountId>",
	"amount": 100,
	"idempotencyKey": "unique-string-per-request"
}

Notes:

  • idempotencyKey must be unique. If the same key is reused:
    • returns the existing COMPLETED transaction
    • or returns “still processing” for PENDING
  • Implementation intentionally includes a ~15s delay to simulate processing.

Create initial funds (System user only)

  • POST /api/transactions/system/initial-funds

Body:

{
	"toAccount": "<accountId>",
	"amount": 500,
	"idempotencyKey": "unique-string-per-request"
}

This route requires the authenticated user to have systemUser: true.


Quick cURL Examples

Register

curl -i -X POST http://localhost:3000/api/auth/register \
	-H "Content-Type: application/json" \
	-d "{\"email\":\"user@example.com\",\"password\":\"secret123\",\"name\":\"User\"}"

Login (save cookies)

curl -i -c cookies.txt -X POST http://localhost:3000/api/auth/login \
	-H "Content-Type: application/json" \
	-d "{\"email\":\"user@example.com\",\"password\":\"secret123\"}"

Create account (using cookie)

curl -i -b cookies.txt -X POST http://localhost:3000/api/accounts/

List accounts

curl -i -b cookies.txt http://localhost:3000/api/accounts/

Create transaction

curl -i -b cookies.txt -X POST http://localhost:3000/api/transactions/ \
	-H "Content-Type: application/json" \
	-d "{\"fromAccount\":\"FROM_ID\",\"toAccount\":\"TO_ID\",\"amount\":100,\"idempotencyKey\":\"txn-001\"}"

Notes / Known Behaviors

  • Port is currently hard-coded to 3000 in Backend/server.js.
  • Ledger entries are designed to be immutable (update/delete hooks throw errors).
  • Token blacklist uses a TTL index of ~3 days.

Troubleshooting

  • MongoDB connection fails: verify MONGO_URI and that MongoDB is running.
  • 401 Unauthorized on protected routes: pass cookie token or Authorization: Bearer <token>.
  • Email server errors: verify Gmail OAuth2 env vars, or ignore during local testing.

About

Ledger-based bank transaction with JWT auth, immutable ledger entries, idempotent transfers, and balance calculation on MongoDB.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors