A complete, production-ready railway ticket booking application built with React + Spring Boot
- Project Overview
- Architecture
- Repository Layout
- Feature List
- Tech Stack Summary
- Quick Start
- Full Booking Flow
- Seeded Data
- Environment Setup Checklist
- API Overview
- Known Limitations
- Bug Fixes History
- Future Roadmap
RailBook is a full-stack railway ticket booking system modelled after Indian Railways. Users can search trains across 20 city pairs, select seats visually, fill passenger details, and complete payment through Razorpay — all within a polished, responsive UI that supports both dark and light themes.
┌─────────────────────────────────────────────────────┐
│ Browser │
│ React 18 + Vite · Tailwind CSS · React Router │
│ http://localhost:5173 │
└────────────────────┬────────────────────────────────┘
│ REST API (JSON)
│ Axios + JWT Bearer token
│
┌────────────────────▼────────────────────────────────┐
│ Spring Boot 3.2 │
│ JWT Auth · Spring Security · Spring Data JPA │
│ http://localhost:8080 │
└────────────────────┬────────────────────────────────┘
│ JDBC / Hibernate ORM
│
┌────────────────────▼────────────────────────────────┐
│ MySQL 8 (railbook_db) │
│ 10 tables · Auto-created · Auto-seeded │
└─────────────────────────────────────────────────────┘
│
┌──────────┴──────────┐
│ │
┌─────▼──────┐ ┌──────▼──────┐
│ Razorpay │ │ Gmail SMTP │
│ Payments │ │ Emails │
└────────────┘ └─────────────┘
railbook/
├── README.md ← This file (full project overview)
│
├── railbook-frontend/ ← React Frontend
│ ├── README.md ← Frontend-specific README
│ ├── public/train-icon.svg ← Custom train SVG favicon
│ ├── .env ← API URL + Razorpay Key ID
│ └── src/
│ ├── api/ ← Axios wrappers (7 files)
│ ├── components/ ← Reusable UI (7 components)
│ ├── context/ ← Auth + Booking + Theme state
│ ├── hooks/ ← useAuth, useTheme
│ ├── pages/ ← 19 pages / routes
│ └── utils/ ← helpers.js
│
└── railbook-backend/ ← Spring Boot Backend
├── README.md ← Backend-specific README
├── pom.xml
└── src/main/
├── resources/
│ └── application.yml ← All credentials & config
└── java/com/railbook/
├── config/ ← Security, CORS, DataSeeder
├── controller/ ← 6 REST controllers
├── dto/ ← 9 request + 11 response DTOs
├── entity/ ← 10 JPA entities
├── enums/ ← 5 enums
├── exception/ ← 8 exception classes
├── repository/ ← 9 repositories
├── security/ ← JWT filter + entry point
├── service/ ← 6 interfaces + 7 implementations
└── util/ ← 4 mappers + PnrGenerator
- Register with name, email, password, phone
- Login with JWT access token (24h) + refresh token (7 days)
- Auto token refresh on expiry
- User profile dropdown with sign out
- Search trains by From / To / Date / Passengers / Class
- Smart search — matches on city name OR station name (e.g. "Kolkata" finds "Kolkata Howrah")
- Browse all trains with live text search + type filter + class filter + sort
- 48 pre-seeded trains across 20 bidirectional Indian city pairs
- Search — enter route + date
- Select Train — filter by class, sort by price/rating/departure
- Select Seats — choose 1–6 passengers, pick seats on visual coach map
- Passenger Details — name, age, gender, berth pref, ID proof per passenger
- Payment — Razorpay checkout (Card / UPI / Netbanking / Wallet)
- Confirmation — E-ticket with PNR, passenger table, fare breakdown, print/share
- My Bookings — full history with status badges
- PNR Status — live lookup by PNR number
- Cancel Ticket — lookup by PNR with confirmation prompt
- Email confirmation on booking (Gmail SMTP)
- Dark / Light theme toggle — persists to localStorage, respects system preference
- Fully responsive — mobile, tablet, desktop
- 6-step booking progress stepper
- Sticky booking summary sidebar with live fare calculation
- Debounced station autocomplete (300ms)
- Loading spinners on all async operations
- Form validation with per-field error messages
Home · All Trains · Trains (search results) · Seats · Passengers · Payment · Confirmation · Login · Register · My Bookings · PNR Status · Help (FAQ) · Cancel Ticket · Contact · Refund Policy · Terms · Privacy · 404
| Framework | React 18.2 + Vite 5.1 |
| Styling | Tailwind CSS 3.4 (dark mode: class) |
| Routing | React Router DOM 6.22 |
| HTTP | Axios 1.6 with JWT interceptor |
| Icons | Lucide React 0.383 |
| State | React Context API (Auth + Booking + Theme) |
| Payment | Razorpay JS checkout widget |
| Framework | Spring Boot 3.2.3 |
| Language | Java 21 |
| Security | Spring Security 6.2 + JJWT 0.12.5 |
| Database | MySQL 8 via Spring Data JPA + Hibernate 6.4 |
| Payment | Razorpay Java SDK 1.4.5 |
| Spring Mail (Gmail SMTP) | |
| Build | Maven 3.9 |
# 1. Set your credentials in application.yml
# - spring.datasource.password
# - app.razorpay.key-id + key-secret
# - spring.mail.username + password
cd railbook-backend
mvn clean install
mvn spring-boot:run
# → http://localhost:8080
# → DB auto-created, 48 trains auto-seeded# 1. Set .env
# VITE_API_BASE_URL=http://localhost:8080/api
# VITE_RAZORPAY_KEY_ID=rzp_test_XXXXXXXXXXXXXXXX
cd railbook-frontend
npm install
npm run dev
# → http://localhost:5173Navigate to http://localhost:5173, register an account, and book a train.
[Home] Search: Delhi → Kolkata, 2 passengers
↓
[Train List] Pick Rajdhani Express, 3A class → click Book
↓
[Seats] Set passengers = 2, pick 2 seats from coach map
↓
[Passengers] Fill details for Passenger 1 & 2 + contact info
→ POST /api/bookings → creates PENDING booking, returns PNR
↓
[Payment] Select UPI → click Pay ₹4,XXX
→ POST /api/payments/create-order → Razorpay order created
→ Razorpay checkout opens
→ POST /api/payments/verify → HMAC verified → CONFIRMED
→ Confirmation email sent
↓
[Confirmation] E-ticket shown with PNR, passenger list, fare breakdown
New Delhi (NDLS), Kolkata Howrah (HWH), Mumbai Central (MMCT), Chennai Central (MAS), Bangalore City (SBC), Hyderabad (HYB), Pune (PUNE), Chandigarh (CDG), Bhopal (BPL), Jaipur (JP), Prayagraj (ALD), Lucknow (LKO), Patna (PNBE), Bhubaneswar (BBS), Amritsar (ASR)
| Code | Name |
|---|---|
| SL | Sleeper |
| 3A | AC 3-Tier |
| 2A | AC 2-Tier |
| 1A | First Class AC |
| CC | Chair Car |
| EC | Executive Chair Car |
-
spring.datasource.password— your MySQL password -
app.jwt.secret— change to a secure 64-char secret in production -
app.razorpay.key-id— from Razorpay Dashboard → Settings → API Keys -
app.razorpay.key-secret— same location (never expose this) -
spring.mail.username— your Gmail address -
spring.mail.password— Gmail App Password (not your login password)
-
VITE_API_BASE_URL=http://localhost:8080/api -
VITE_RAZORPAY_KEY_ID=rzp_test_XXXXXX— Key ID only (not secret)
- dashboard.razorpay.com → Sign up
- Switch to Test Mode (top right toggle)
- Settings → API Keys → Generate Test Key
- Copy Key ID → frontend
.envAND backendapplication.yml - Copy Key Secret → backend
application.ymlONLY
- Google Account → Security → 2-Step Verification (enable)
- Security → App Passwords → Generate
- Use the 16-char password in
spring.mail.password
Base URL: http://localhost:8080
All protected endpoints require: Authorization: Bearer <accessToken>
| Domain | Public Endpoints | Protected Endpoints |
|---|---|---|
| Auth | POST /register, /login, /refresh | POST /logout |
| Users | — | GET/PUT /users/me |
| Trains | POST /trains/search, GET /trains/all, /trains/{id}, /trains/{id}/seats | — |
| Stations | GET /stations, /stations/search | — |
| Bookings | GET /bookings/pnr/{pnr} | POST /bookings, GET /bookings, PATCH /{pnr}/cancel |
| Payments | — | POST /payments/create-order, /payments/verify |
| Item | Details |
|---|---|
| Contact form | UI only — no email service integrated yet |
| Seat locking | Seats marked BOOKED on POST /bookings — no real-time lock between concurrent users |
| Payment | Test mode only — use Razorpay test card 4111 1111 1111 1111 |
| Requires Gmail App Password and 2FA enabled | |
| No admin panel | Admin role exists in DB but no admin UI built yet |
| # | Bug | Fix |
|---|---|---|
| 1 | Login fails after 1–2 days with "Duplicate entry for refresh_tokens" | @Modifying @Query deleteByUserId() ensures DELETE flushes before INSERT |
| 2 | All Trains page returns 400 Bad Request | Removed @NotBlank/@NotNull from TrainSearchRequest |
| 3 | No trains found for Kolkata → Delhi search | DataSeeder now seeds 48 bidirectional trains; search also matches city name |
| 4 | Seat page always shows "Choose 1 seat" even for multi-passenger | Added passenger count selector (1–6) directly on SeatsPage |
| 5 | Register page: white text on white input in dark mode | Added text-slate-800 bg-white to all inputs in RegisterPage.jsx |
| 6 | OAuth2 removed | All Google/GitHub OAuth2 code removed from both frontend and backend |
- Contact Us — integrate EmailJS or Spring Boot
/api/contactendpoint - Admin panel — manage trains, view all bookings, update seat counts
- Real-time seat locking — Redis-based seat hold with 10-minute expiry
- Waitlist / RAC support
- Ticket PDF download
- SMS notification via Twilio
- Multi-language support (Hindi)
- PWA — offline mode and home screen install