Skip to content

SujalGadhave/Stayfinder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🏨 Stayfinder



A production-ready REST API for a full-featured hotel booking platform — built with Spring Boot, secured with JWT, powered by Stripe payments, and documented with Swagger/OpenAPI.


📋 Table of Contents


✨ Features

Feature Description
🔐 JWT Authentication Stateless auth with access + HTTP-only refresh tokens
🏨 Hotel Management Full CRUD — name, city, amenities, photos, contact info
🛏️ Room Management Multi-type rooms with capacity, photos, and base pricing
📦 Inventory Control Date-range availability with open/close management
💰 Smart Pricing Engine Strategy pattern: Base, Surge, Occupancy, Holiday & Urgency pricing
👥 Guest Management Add, update, delete guests linked to user accounts
📅 Multi-step Booking Init → Add Guests → Payment → Confirm flow
💳 Stripe Integration Payment sessions + webhook event processing
👮 Role-Based Access Separate USER and ADMIN protected routes
📄 Swagger / OpenAPI Auto-generated interactive API docs
🧱 Global Exception Handling Centralized error responses via @ControllerAdvice
📊 Hotel Reporting Date-range booking reports per hotel

🏗️ Architecture & Design

This project follows a clean layered architecture and applies several well-known design patterns:

Client Request
      │
      ▼
  Controller Layer        ← REST endpoints, request/response DTOs
      │
      ▼
  Service Layer           ← Business logic, transaction management
      │
      ▼
  Repository Layer        ← Spring Data JPA, database access
      │
      ▼
  Database (MySQL)

🎯 Design Patterns Used

  • Strategy Pattern — Pluggable pricing strategies (BasePricingStrategy, SurgePricingStrategy, OccupancyPricingStrategy, HolidayPricingStrategy, UrgencyPricingStrategy) composed via PricingService
  • DTO Pattern — Decoupled request/response objects prevent entity leakage
  • Repository Pattern — Spring Data JPA repositories for all entities
  • Advice Pattern@ControllerAdvice for unified error handling
  • Filter Chain — JWT authentication via Spring Security filter

🛠️ Tech Stack

Layer Technology Version
Language Java 17
Framework Spring Boot 3.4.2
Security Spring Security + JJWT 0.12.6
Database MySQL 8.0
ORM Spring Data JPA / Hibernate
Payments Stripe Java SDK 28.2.0
API Docs SpringDoc OpenAPI (Swagger UI) 2.8.3
Mapping ModelMapper 3.2.2
Build Tool Apache Maven
Utilities Lombok

📁 Project Structure

Airbnb-Backend-API/
├── pom.xml
└── src/
    └── main/
        ├── java/com/sujal/airbnb/
        │   ├── AirBnbApplication.java          # Spring Boot entry point
        │   ├── Advice/                         # Global exception handling (@ControllerAdvice)
        │   ├── Config/                         # Spring Security, CORS, Bean configs
        │   ├── Controller/
        │   │   ├── AuthController.java         # Signup, Login, Refresh Token
        │   │   ├── UserController.java         # Profile, Guests, My Bookings
        │   │   ├── HotelBrowseController.java  # Public hotel search & info
        │   │   ├── HotelController.java        # Admin hotel CRUD + reports
        │   │   ├── RoomAdminController.java    # Admin room CRUD
        │   │   ├── InventoryController.java    # Inventory management
        │   │   ├── HotelBookingController.java # Booking lifecycle
        │   │   └── WebHookController.java      # Stripe webhook handler
        │   ├── Dto/                            # Request & Response DTOs
        │   ├── Entities/
        │   │   ├── UserEntity.java
        │   │   ├── HotelEntity.java
        │   │   ├── HotelContactInfo.java
        │   │   ├── HotelMiniPriceEntity.java   # Cached min-price per hotel
        │   │   ├── RoomEntity.java
        │   │   ├── InventoryEntity.java        # Per-room, per-date inventory
        │   │   ├── BookingEntity.java
        │   │   └── GuestEntity.java
        │   ├── Enums/                          # BookingStatus, Gender, Role, etc.
        │   ├── Exception/                      # Custom exception classes
        │   ├── Repository/                     # Spring Data JPA repositories
        │   ├── Security/                       # JWT filter, UserDetailsService
        │   ├── Service/                        # Business logic interfaces + impls
        │   ├── Strategy/
        │   │   ├── PricingStrategy.java        # Interface
        │   │   ├── PricingService.java         # Orchestrator
        │   │   ├── BasePricingStrategy.java
        │   │   ├── SurgePricingStrategy.java
        │   │   ├── OccupancyPricingStrategy.java
        │   │   ├── HolidayPricingStrategy.java
        │   │   └── UrgencyPricingStrategy.java
        │   └── Utils/                          # Helper utilities
        └── resources/
            └── application.properties

🚀 Getting Started

Prerequisites

  • ☕ Java 17+
  • 🐬 MySQL 8.0+
  • 🔧 Maven 3.8+
  • 💳 Stripe account (for payments)

1. Clone the Repository

git clone https://github.com/SujalGadhave/Airbnb-Backend-API.git
cd Airbnb-Backend-API

2. Create the MySQL Database

CREATE DATABASE airbnb_db;

3. Configure Environment Variables

Set the following environment variables (or update application.properties):

Variable Description
JWT_SECRET A strong secret key for signing JWTs
FRONTEND_URL Your frontend origin URL (for CORS)
STRIPE_SECRET_KEY Your Stripe secret API key
STRIPE_WEBHOOK_SECRET Your Stripe webhook signing secret

4. Build & Run

# Build
mvn clean install

# Run
mvn spring-boot:run

The server starts at: http://localhost:8080


⚙️ Configuration

# Database
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/airbnb_db
spring.datasource.username=root
spring.datasource.password=YOUR_DB_PASSWORD
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect

# Server
server.servlet.context-path=/api/v1
server.port=8080

# JWT
jwt.secretKey=${JWT_SECRET}

# Frontend (CORS)
frontend.url=${FRONTEND_URL}

# Stripe
stripe.secret.key=${STRIPE_SECRET_KEY}
stripe.webhook.secret=${STRIPE_WEBHOOK_SECRET}

# Swagger
springdoc.swagger-ui.operationsSorter=method
springdoc.swagger-ui.tagsSorter=alpha

🌐 Base URL & Swagger

Resource URL
Base URL http://localhost:8080/api/v1
Swagger UI http://localhost:8080/api/v1/swagger-ui.html
OpenAPI JSON http://localhost:8080/api/v1/v3/api-docs

🔐 Authentication Flow

1. POST /auth/signup      → Register new user
2. POST /auth/login       → Get access token (JWT) + refresh token (HttpOnly cookie)
3. POST /auth/refresh     → Exchange refresh token for new access token

All protected routes require: Authorization: Bearer <access_token>

Token Details

Token Storage Expiry
Access Token Response body Short-lived
Refresh Token HTTP-only cookie Long-lived

💡 Smart Pricing Engine

The pricing engine uses the Strategy design pattern to compose multiple pricing strategies dynamically:

Base Price
    │
    ├─▶ SurgePricingStrategy     → Applies admin-set surge multiplier
    ├─▶ OccupancyPricingStrategy → Increases price as occupancy rises
    ├─▶ HolidayPricingStrategy   → Adds premium on public holidays
    └─▶ UrgencyPricingStrategy   → Raises price for last-minute bookings

                    ▼
            Final Price (via PricingService)

This makes adding new pricing rules as simple as implementing PricingStrategy — zero changes to existing code.


📡 API Reference

🔐 Authentication — /auth

Method Endpoint Auth Description
POST /auth/signup None Register a new user
POST /auth/login None Login and get JWT token
POST /auth/refresh Cookie Refresh access token
POST /auth/signup — Request Body
{
    "email": "user@example.com",
    "password": "yourPassword123",
    "name": "John Doe"
}
POST /auth/login — Request Body
{
    "email": "user@example.com",
    "password": "yourPassword123"
}

👤 User Profile — /users

🔒 All endpoints require Bearer Token

Method Endpoint Description
GET /users/profile Get current user profile
PATCH /users/profile Update profile (name, DOB, gender)
GET /users/myBookings List all bookings for current user
GET /users/guests List all saved guests
POST /users/guests Add a new guest
PUT /users/guests/{guestId} Update a guest
DELETE /users/guests/{guestId} Remove a guest
PATCH /users/profile — Request Body
{
    "name": "John Updated",
    "dateOfBirth": "1990-05-15",
    "gender": "MALE"
}

Gender options: MALE, FEMALE

POST /users/guests — Request Body
{
    "name": "Jane Doe",
    "gender": "FEMALE",
    "age": 28
}

🏨 Hotel Browse — /hotels (Public)

Method Endpoint Description
GET /hotels/search Search available hotels with filters
GET /hotels/{hotelId}/info Get detailed hotel information
GET /hotels/search — Request Body
{
    "city": "New York",
    "startDate": "2025-06-01",
    "endDate": "2025-06-05",
    "roomsCount": 2,
    "page": 0,
    "size": 10
}

🏢 Hotel Admin — /admin/hotels

🔒 Requires Admin Bearer Token

Method Endpoint Description
POST /admin/hotels Create a new hotel
GET /admin/hotels List all your hotels
GET /admin/hotels/{hotelId} Get hotel by ID
PUT /admin/hotels/{hotelId} Update hotel details
DELETE /admin/hotels/{hotelId} Delete a hotel
PATCH /admin/hotels/{hotelId}/activate Activate hotel (make it bookable)
GET /admin/hotels/{hotelId}/bookings View all bookings for a hotel
GET /admin/hotels/{hotelId}/reports Get booking report (with date range)
POST /admin/hotels — Request Body
{
    "name": "Grand Hotel",
    "city": "New York",
    "photos": ["https://example.com/photo1.jpg"],
    "amenities": ["WiFi", "Pool", "Gym", "Spa", "Restaurant"],
    "contactInfo": {
        "address": "123 Main Street, New York, NY 10001",
        "phoneNumber": "+1-555-123-4567",
        "email": "info@grandhotel.com",
        "location": "40.7128,-74.0060"
    }
}
GET /admin/hotels/{id}/reports — Query Params
GET /admin/hotels/1/reports?startDate=2025-01-01&endDate=2025-01-31

🛏️ Room Admin — /admin/hotels/{hotelId}/rooms

🔒 Requires Admin Bearer Token

Method Endpoint Description
POST /admin/hotels/{hotelId}/rooms Create a new room
GET /admin/hotels/{hotelId}/rooms List all rooms in a hotel
PUT /admin/hotels/{hotelId}/rooms/{roomId} Update room details
DELETE /admin/hotels/{hotelId}/rooms/{roomId} Delete a room
POST /admin/hotels/{hotelId}/rooms — Request Body
{
    "type": "Deluxe Suite",
    "basePrice": 299.99,
    "photos": ["https://example.com/room1.jpg"],
    "amenities": ["King Bed", "Ocean View", "Mini Bar", "Room Service"],
    "totalCount": 10,
    "capacity": 2
}

📦 Inventory — /admin/inventory

🔒 Requires Admin Bearer Token

Method Endpoint Description
GET /admin/inventory/rooms/{roomId} View inventory for a room
PATCH /admin/inventory/rooms/{roomId} Update pricing / availability
PATCH /admin/inventory/rooms/{roomId} — Request Body
{
    "startDate": "2025-06-01",
    "endDate": "2025-06-30",
    "surgeFactor": 1.5,
    "closed": false
}

📅 Booking — /booking

🔒 Requires Bearer Token

Method Endpoint Description
POST /booking/init Initialize a new booking
POST /booking/{bookingId}/addGuests Add guests to booking
POST /booking/{bookingId}/payments Initiate Stripe payment
POST /booking/{bookingId}/cancel Cancel a booking
GET /booking/{bookingId}/status Get booking status
POST /booking/init — Request Body
{
    "hotelId": 1,
    "roomId": 1,
    "checkInDate": "2025-06-01",
    "checkOutDate": "2025-06-05",
    "roomsCount": 1
}
POST /booking/{bookingId}/addGuests — Request Body
[
    { "name": "John Doe", "gender": "MALE", "age": 30 },
    { "name": "Jane Doe", "gender": "FEMALE", "age": 28 }
]

💳 Webhooks — /webhook

Method Endpoint Description
POST /webhook/payment Stripe payment event handler

⚠️ This endpoint is called by Stripe, not by users. Requires Stripe-Signature header.


🧪 Testing with Postman

Step 1 — Set Up Environment

Create a Postman environment with:

Variable Value Description
base_url http://localhost:8080/api/v1 API base URL
access_token (empty) Set automatically after login

Step 2 — Configure Auth Header

Authorization: Bearer {{access_token}}

Step 3 — Auto-capture Token on Login

Add this to the Tests tab of your Login request:

if (pm.response.code === 200) {
    var jsonData = pm.response.json();
    pm.environment.set("access_token", jsonData.data.accessToken);
}

🔁 Sample Test Flow

 1. POST /auth/signup           → Register your account
 2. POST /auth/login            → Get JWT access token
 3. POST /admin/hotels          → Create a hotel (admin)
 4. POST /admin/hotels/{id}/rooms → Add room types
 5. PATCH /admin/hotels/{id}/activate → Make hotel bookable
 6. GET  /hotels/search         → Search for hotels (as user)
 7. GET  /hotels/{id}/info      → View hotel details
 8. POST /booking/init          → Initialize a booking
 9. POST /booking/{id}/addGuests → Add traveller details
10. POST /booking/{id}/payments → Pay with Stripe
11. GET  /booking/{id}/status   → Confirm booking is active

📊 HTTP Status Codes

Code Status Meaning
200 ✅ OK Request succeeded
201 ✅ Created Resource successfully created
204 ✅ No Content Success with no response body
400 ❌ Bad Request Invalid input / validation error
401 ❌ Unauthorized Missing or invalid JWT
403 ❌ Forbidden Authenticated but insufficient permissions
404 ❌ Not Found Resource does not exist
500 ❌ Internal Server Error Unexpected server-side error

🤝 Contributing

Contributions, issues, and feature requests are welcome!

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

👨‍💻 Author

Sujal Gadhave

GitHub


If you found this project helpful, please give it a star!

About

A production-ready REST API for a full-featured hotel booking platform built with Spring Boot, secured with JWT, powered by Stripe payments, and documented with Swagger/OpenAPI.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages