Payment App is a full-stack MERN application that implements user authentication, account balance lookup, and peer-to-peer money transfer APIs. The frontend is built with React and communicates with an Express API backed by MongoDB. Authentication is based on JWT, and protected account operations require a valid bearer token. This repository is suitable as a backend-focused portfolio project demonstrating API design, auth middleware usage, and transactional transfer handling.
- Frontend: React (Vite), React Router, Axios, Tailwind CSS
- Backend: Node.js, Express, MongoDB, Mongoose, JWT, bcrypt, zod
- The React frontend calls backend endpoints under
/api/v1using Axios. - User signup/signin returns a JWT token, which the frontend stores in
localStorageastoken. - Protected backend routes require
Authorization: Bearer <jwt_token>. - Account features (
getbalance,transfer) are served by the backend after JWT verification and DB access.
routesdefine HTTP endpoints and map request handlers.middleware/auth.jsvalidates JWT for protected routes and attaches user identity.- Route handlers execute business logic (user auth, profile update, balance lookup, transfer).
models(user.model.js,account.model.js) interact with MongoDB via Mongoose.- Responses are returned to the frontend as JSON.
backend/index.js: Express app bootstrap and route mounting.backend/config/connectdb.js: MongoDB connection setup.backend/routes/userRoutes.js: signup/signin/update/filter user endpoints.backend/routes/accontRoutes.js: balance and transfer endpoints.backend/middleware/auth.js: JWT-protected route gate.
- User submits credentials to
POST /api/v1/users/signuporPOST /api/v1/users/signin. - Backend validates request input and credentials.
- Passwords are verified/handled with
bcrypt. - Backend signs a JWT and returns it to the client.
- Frontend stores token in
localStorageundertoken. - Frontend sends token in
Authorizationheader for protected endpoints. authmiddleware verifies JWT before allowing account operations.
The transfer operation should execute as an atomic flow to prevent inconsistent balances:
- Authenticate sender via JWT middleware.
- Validate request body (
amount, recipient id). - Confirm sender account exists and has sufficient balance.
- Confirm recipient account exists.
- Debit sender account and credit recipient account in one DB transaction/session.
- Commit transaction on success; abort on any error.
- Return success response to frontend.
Note: The project already exposes POST /api/v1/accounts/transfer; atomicity depends on backend transaction implementation details in code.
The app uses separate collections for User and Account:
Userstores identity and authentication-related fields (name/email/password hash).Accountstores financial state (balance, account ownership mapping).
Why this separation is useful:
- Keeps authentication and financial data concerns isolated.
- Simplifies account-specific queries/updates.
- Makes financial operations easier to reason about and secure independently.
Current behavior is primarily basic JSON/error propagation. Production-oriented handling should follow:
- Validate request payloads early (zod) and return
400for invalid input. - Return
401/403for auth failures on protected routes. - Return
404for missing entities (for example, recipient account). - Return
409or400for business-rule failures (for example, insufficient balance). - Return
500for unexpected server/database errors. - Use consistent response envelopes to simplify frontend handling.
Implemented:
- Password hashing with
bcrypt. - JWT-based authentication for protected routes.
- Request validation with
zod. - Bearer token authorization for account routes.
Best-practice improvements (not currently claimed as implemented):
- Move token storage from
localStorageto secure HTTP-only cookies (mitigates XSS token theft risk). - Add token expiry handling and refresh-token rotation.
- Add rate limiting and abuse protection on auth endpoints.
- Add centralized audit/security logging for sensitive operations.
Create backend/.env:
PORT=3000
MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secretPrerequisites:
- Node.js 18+
- MongoDB running locally or remotely
/api/v1
| Method | Endpoint | Protected | Description |
|---|---|---|---|
| POST | /api/v1/users/signup |
No | Create user account and return JWT |
| POST | /api/v1/users/signin |
No | Authenticate user and return JWT |
| PUT | /api/v1/users/ |
Yes | Update user profile fields |
| GET | /api/v1/users/bulk?filter=<name> |
No | Search users by first/last name |
| Method | Endpoint | Protected | Description |
|---|---|---|---|
| GET | /api/v1/accounts/getbalance |
Yes | Fetch authenticated user balance |
| POST | /api/v1/accounts/transfer |
Yes | Transfer funds to another user |
POST /api/v1/users/signup
{
"email": "user@example.com",
"password": "secret123",
"firstName": "John",
"lastName": "Doe"
}POST /api/v1/users/signin
{
"email": "user@example.com",
"password": "secret123"
}PUT /api/v1/users/ (protected)
{
"firstName": "Jane",
"lastName": "Smith"
}POST /api/v1/accounts/transfer (protected)
{
"amount": 500,
"to": "<receiver_user_id>"
}GET /api/v1/accounts/getbalance current response shape:
{
"msg": "your account balance is 1234"
}Suggested production response shape (recommended for API consistency):
{
"balance": 1234,
"currency": "INR"
}Authorization: Bearer <jwt_token>- User opens
/(SignUp) or/signin(SignIn). - On successful auth, frontend stores
tokeninlocalStorage. - User navigates to
/dashboard. Balancecomponent callsGET /api/v1/accounts/getbalancewith bearer token.Userscomponent fetches searchable users via/api/v1/users/bulk.- User initiates transfer from
SendMoney, which posts to/api/v1/accounts/transferwith token.
backend/
config/
connectdb.js # Database connection bootstrap
middleware/
auth.js # JWT verification middleware
models/
user.model.js # User schema/model
account.model.js # Account schema/model
routes/
userRoutes.js # User/auth/profile/search endpoints
accontRoutes.js # Account balance/transfer endpoints
index.js # Express entrypoint
frontend/
src/
components/
Appbar.jsx
Balance.jsx
SendMoney.jsx
Users.jsx
...
pages/
SignUp.jsx
SignIn.jsx
DashBoard.jsx
Transfer.jsx
App.jsx # Client-side route configuration
cd backend
npm install
npm run devBackend runs at http://localhost:3000.
cd frontend
npm install
npm run devThe project is currently configured for local development (localhost) based on frontend API calls. If deployed, update frontend API base URL and backend environment variables for production infrastructure.
- Automated tests are not configured yet.
SendMoneyandUsersintegration is partially wired and can be hardened further.- Balance response currently returns string-based
msgrather than a typed numeric balance field. - Route file is named
accontRoutes.js(typo in filename), which may reduce maintainability.
- Add refresh tokens and token rotation strategy.
- Add rate limiting and brute-force protection for auth endpoints.
- Add unit/integration tests (route, middleware, DB transaction coverage).
- Add centralized logging and request tracing.
- Add Docker support for consistent local/runtime environments.
- Add CI/CD pipeline for linting, test, and deployment automation.
- Improve API response standardization (consistent success/error envelopes).
Add UI/API screenshots here:
- Signup page
- Signin page
- Dashboard (balance + users)
- Transfer flow (request + success state)