Skip to content

Repository files navigation

πŸš– GoOn Backend

A production-grade Ride Hailing REST API built with PHP 6.4 Β· Symfony 8.4 Β· Symfony Security Β· JWT Β· Redis

PHP Symfony MySQL Redis Symfony Mercure Swagger


Overview

GoOn Backend is a fully functional backend system for an Uber-like ride-hailing system. It handles the complete ride lifecycle β€” from rider requesting a ride to driver accepting, starting, completing, and rating β€” backed by real geospatial queries, JWT-based authentication, strategy-pattern business logic, and a wallet payment system.

The project is designed following clean architecture principles: thin controllers, interface-driven service layer, repository abstraction, and separate strategy classes for pluggable business rules.


✨ Core Features

Authentication & Security

  • JWT Access Token (24h expiry) + Refresh Token (6 months, stored in HttpOnly Cookie)
  • Role-based access control β€” ROLE_ADMIN, ROLE_DRIVER, ROLE_RIDER
  • Spring Security filter chain with a custom JwtAuthFilter
  • Secure password hashing via Spring Security's PasswordEncoder
  • Token refresh endpoint reads refresh token from cookie (never exposed in response body)

Geospatial Driver Matching (PostGIS)

  • Driver locations stored as Geometry(Point, 4326) using Hibernate Spatial
  • Two native SQL strategies using PostGIS functions:
    • Nearest Driver β€” ST_DWithin (10 km radius) + ST_Distance ORDER BY
    • Top-Rated Driver β€” ST_DWithin (15 km radius) + ORDER BY rating DESC
  • Distance calculation via OSRM (Open Source Routing Machine) API for real road distances

Fare Calculation (Strategy Pattern)

  • RideFareCalculationStrategy interface with two implementations:
    • Default Fare β€” base rate Γ— road distance
    • Surge Pricing β€” base rate Γ— road distance Γ— 2x surge multiplier
  • RideStrategyManager selects the correct strategy at runtime based on demand

Payment System (Strategy Pattern)

  • PaymentStrategy interface with two implementations:
    • Cash Payment β€” marks payment confirmed, adds fare to driver wallet
    • Wallet Payment β€” debits rider wallet, credits driver wallet, logs transactions
  • PaymentStrategyManager resolves strategy from PaymentMethod enum at runtime

πŸš— Ride Lifecycle Management

  • Full state machine: PENDING β†’ CONFIRMED β†’ ONGOING β†’ ENDED / CANCELLED
  • OTP-verified ride start (driver submits OTP, rider receives it on booking)
  • Atomic @Transactional operations across ride, payment, and driver availability
  • Paginated ride history for both rider and driver (sorted by createdTime DESC)

⭐ Ratings System

  • Rider rates driver; driver rates rider β€” both stored per ride
  • Average rating auto-computed and persisted on Driver and Rider entities

Wallet & Transactions

  • Each user has a Wallet with balance tracking
  • WalletTransaction records every debit/credit with TransactionType and TransactionMethod
  • Wallet created automatically on user signup

Email Notifications

  • Spring Mail (SMTP/Gmail) integration via EmailSenderService
  • Sends transactional emails (ride confirmations, onboarding, etc.)

Global API Standards

  • GlobalResponseHandler wraps every successful response in ApiResponse<T>
  • GlobalExceptionHandler maps exceptions to structured ApiError responses:
    • ResourceNotFoundException β†’ 404
    • RunTimeConflictException β†’ 409
    • JwtException β†’ 401
    • AccessDeniedException β†’ 403
    • MethodArgumentNotValidException β†’ 400 with field-level sub-errors
  • Swagger / OpenAPI UI auto-generated at /swagger-ui/index.html

Tech Stack

Layer Technology
Language PHP
Framework Symfony 8.4
Security Symfony Security + JJWT 0.12.6
ORM Symfony + Hibernate 6
Geospatial Hibernate Spatial + PostGIS
Database MySQL
Distance API OSRM (Open Source Routing Machine)
Mapping ModelMapper 3.2.0
API Docs SpringDoc OpenAPI (Swagger UI) 2.6.0
Monitoring ()
Email Symfony mail (Gmail SMTP)
Build build tool
Utilities ()

πŸ“ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Client / Postman                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚ HTTP
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              JwtAuthFilter (Security Layer)         β”‚
β”‚         Validates JWT β†’ sets SecurityContext        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               REST Controllers (3)                  β”‚
β”‚         AuthController / DriverController           β”‚
β”‚                  RiderController                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                          β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Service Layer     β”‚  β”‚    Strategy Layer          β”‚
β”‚  AuthService        β”‚  β”‚  DriverMatchingStrategy    β”‚
β”‚  RideService        β”‚  β”‚  RideFareCalculationStrat. β”‚
β”‚  DriverService      β”‚  β”‚  PaymentStrategy           β”‚
β”‚  RiderService       β”‚  β”‚  (Strategy Pattern)        β”‚
β”‚  PaymentService     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚  WalletService      β”‚
β”‚  RatingService      β”‚
β”‚  EmailService       β”‚
β”‚  DistanceService    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Repository Layer (JPA)                 β”‚
β”‚  DriverRepo / RiderRepo / RideRepo / UserRepo       β”‚
β”‚  WalletRepo / PaymentRepo / RatingRepo              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           MySQL Database + Redis             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‚ Project Structure

uberApp-Backend/
β”œβ”€β”€ src/main/java/com/appdefine/uber/uberApp/
β”‚   β”œβ”€β”€ advices/
β”‚   β”‚   β”œβ”€β”€ ApiError.java                   # Error response model
β”‚   β”‚   β”œβ”€β”€ ApiResponse.java                # Unified response wrapper
β”‚   β”‚   β”œβ”€β”€ GlobalExceptionHandler.java     # @RestControllerAdvice
β”‚   β”‚   └── GlobalResponseHandler.java      # Auto-wraps all responses
β”‚   β”‚
β”‚   β”œβ”€β”€ configs/
β”‚   β”‚   β”œβ”€β”€ MapperConfig.java               # ModelMapper bean
β”‚   β”‚   β”œβ”€β”€ SecurityConfig.java             # JWT filter, CORS, CSRF
β”‚   β”‚   └── WebSecurityConfig.java          # HTTP security rules
β”‚   β”‚
β”‚   β”œβ”€β”€ controllers/
β”‚   β”‚   β”œβ”€β”€ AuthController.java             # /auth/*
β”‚   β”‚   β”œβ”€β”€ DriverController.java           # /drivers/* (ROLE_DRIVER)
β”‚   β”‚   └── RiderController.java            # /riders/* (ROLE_RIDER)
β”‚   β”‚
β”‚   β”œβ”€β”€ dto/                                # Request/Response DTOs
β”‚   β”œβ”€β”€ entities/                           # JPA Entities
β”‚   β”‚   β”œβ”€β”€ enums/                          # RideStatus, PaymentMethod, Role ...
β”‚   β”‚   β”œβ”€β”€ Driver.java
β”‚   β”‚   β”œβ”€β”€ Ride.java                       # Geometry(Point,4326) fields
β”‚   β”‚   β”œβ”€β”€ RideRequest.java
β”‚   β”‚   β”œβ”€β”€ Rider.java
β”‚   β”‚   β”œβ”€β”€ User.java
β”‚   β”‚   β”œβ”€β”€ Wallet.java
β”‚   β”‚   β”œβ”€β”€ WalletTransaction.java
β”‚   β”‚   β”œβ”€β”€ Payment.java
β”‚   β”‚   └── Rating.java
β”‚   β”‚
β”‚   β”œβ”€β”€ exceptions/
β”‚   β”‚   β”œβ”€β”€ ResourceNotFoundException.java
β”‚   β”‚   └── RunTimeConflictException.java
β”‚   β”‚
β”‚   β”œβ”€β”€ repositories/                       # Symfony Data JPA Repositories
β”‚   β”‚   └── DriverRepository.java           # Native PostGIS spatial queries
β”‚   β”‚
β”‚   β”œβ”€β”€ security/
β”‚   β”‚   β”œβ”€β”€ JWTService.java                 # Token generation & validation
β”‚   β”‚   └── JwtAuthFilter.java              # OncePerRequestFilter
β”‚   β”‚
β”‚   β”œβ”€β”€ services/                           # Service interfaces
β”‚   β”‚   └── impl/                           # Service implementations
β”‚   β”‚       β”œβ”€β”€ DistanceServiceOSRMImpl.java # OSRM road distance API
β”‚   β”‚       β”œβ”€β”€ DriverServiceImpl.java
β”‚   β”‚       β”œβ”€β”€ RiderServiceImpl.java
β”‚   β”‚       β”œβ”€β”€ RideServiceImpl.java
β”‚   β”‚       β”œβ”€β”€ PaymentServiceImpl.java
β”‚   β”‚       β”œβ”€β”€ WalletServiceImpl.java
β”‚   β”‚       β”œβ”€β”€ RatingServiceImpl.java
β”‚   β”‚       └── EmailSenderServiceImpl.java
β”‚   β”‚
β”‚   β”œβ”€β”€ strategies/                         # Strategy pattern interfaces
β”‚   β”‚   └── impl/
β”‚   β”‚       β”œβ”€β”€ DriverMatchingNearestDriverStrategy.java
β”‚   β”‚       β”œβ”€β”€ DriverMatchingHighestRatedDriverStrategy.java
β”‚   β”‚       β”œβ”€β”€ RideFareDefaultFareCalculationStrategy.java
β”‚   β”‚       β”œβ”€β”€ RideFareSurgePricingFareCalculationStrategy.java
β”‚   β”‚       β”œβ”€β”€ CashPaymentStrategy.java
β”‚   β”‚       └── WalletPaymentStrategy.java
β”‚   β”‚
β”‚   └── utils/
β”‚       └── GeometryUtil.java               # WKT ↔ Point conversion helpers
β”‚
β”œβ”€β”€ src/main/resources/
β”‚   β”œβ”€β”€ application.properties
β”‚   └── data.sql                            # Seed data
β”‚
β”œβ”€β”€ uberApp.postman_collection.json         # Ready-to-import Postman collection
β”œβ”€β”€ pom.xml

API Reference

All protected endpoints require Authorization: Bearer <access_token> header.

Auth β€” /auth

Method Endpoint Role Description
POST /Oauth/signup Public Register a new user
POST /Oauth/login Public Login, returns access token; sets refresh token as HttpOnly cookie
GET /Oauth/refresh-token Public Issue new access token using refresh token cookie
POST /Oauth/onBoardNewDriver/{userId} ADMIN Promote user to driver with vehicle ID

Driver β€” /drivers (requires ROLE_DRIVER)

Method Endpoint Description
POST /drivers/acceptRide/{rideRequestId} Accept a pending ride request
POST /drivers/startRide/{rideRequestId} Start ride after OTP verification
POST /drivers/endRide/{rideId} Complete the ride, trigger payment
POST /drivers/cancelRide/{rideId} Cancel a confirmed ride
POST /drivers/rateRider Rate the rider after ride completion
GET /drivers/getMyProfile Get authenticated driver's profile
GET /drivers/getMyRides?pageOffset=0&pageSize=10 Paginated ride history

Rider β€” /riders (requires ROLE_USER)

Method Endpoint Description
POST /riders/requestRide Request a new ride with pickup & drop-off location
POST /riders/cancelRide/{rideId} Cancel a ride
POST /riders/rateDriver Rate the driver after ride
GET /riders/getMyProfile Get authenticated rider's profile
GET /riders/getMyRides?pageOffset=0&pageSize=10 Paginated ride history

βš™οΈ Getting Started

Prerequisites

  • PHP 8+
  • MySQL 14+ with Redis extension enabled

1. Clone the Repository

git clone https://github.com/j-deku/goon-backend-php.git
cd goon-Backend-php

2. Setup Database

CREATE DATABASE GoOn;
\c goon-backend-php
CREATE EXTENSION postgis;

3. Configure application.properties

# Database

# JWT
jwt.secretKey=your_strong_secret_key_min_32_chars

# SMTP (Gmail example)
symfony.mail.host=smtp.gmail.com
symfony.mail.port=587
symfony.mail.username=your_email@gmail.com
symfony.mail.password=your_app_password
symfony.mail.properties.mail.smtp.auth=true
symfony.mail.properties.mail.smtp.starttls.enable=true

4. Build & Run

# Build
symfony doctrine:validate

# Run
symfony server:start

The server starts at http://localhost:8080

5. Explore APIs

Tool URL
Swagger UI http://localhost:8080/swagger-ui/index.html
Actuator Health http://localhost:8080/actuator/health
Postman Collection Import goOn.postman_collection.json

Authentication Flow

POST /Oauth/signup         β†’  Create account
POST /Oauth/login          β†’  Get access token (+ refresh token in HttpOnly cookie)
                             Add header: Authorization: Bearer <access_token>
GET  /Oauth/refresh        β†’  Renew access token using cookie (no body needed)

Design Patterns Used

Pattern Where Applied
Strategy Driver matching (Nearest / Highest Rated)
Strategy Fare calculation (Default / Surge Pricing)
Strategy Payment processing (Cash / Wallet)
Factory / Manager RideStrategyManager, PaymentStrategyManager resolve strategies at runtime
Repository Symfony Data with custom native MySQL queries
DTO All controller I/O uses DTOs, never entities directly
Global Advice @RestControllerAdvice for centralized error handling and response wrapping

Testing

./mvnw test

A Postman collection with pre-configured requests for all endpoints is included:

GoOn.postman_collection.json

Import it into Postman and set the base_url variable to http://localhost:8080.


Security Highlights

  • Passwords never stored in plain text β€” BCrypt hashing via Spring Security
  • JWT signed with HMAC-SHA256; validated on every request via JwtAuthFilter
  • Refresh token stored in HttpOnly cookie β€” inaccessible to JavaScript (XSS protection)
  • Role-based endpoint guards using @Secured annotations
  • Input validation with @Valid + field-level error messages in response

Geospatial Queries (Redis)

Driver locations use the WGS-84 coordinate system (SRID 4326). Example native query:

-- Find 10 nearest available drivers within 10 km
SELECT d.*, ST_Distance(d.current_location, :pickUpLocation) AS distance
FROM driver d
WHERE d.available = true
  AND ST_DWithin(d.current_location, :pickUpLocation, 10000)
ORDER BY distance
LIMIT 10;

Key Maven Dependencies

symfony server:start
symfony doctrine:migration
symfony doctrine:validation
modelmapper:3.2.0
sql (runtime)

Planned Enhancements

  • Google Maps / HERE Maps integration for live routing
  • WebSocket β€” real-time ride status updates
  • OTP delivery via SMS (Twilio)
  • Push notifications (Firebase FCM)
  • Admin dashboard APIs
  • Ride fare estimation before booking
  • Docker + Docker Compose setup
  • CI/CD pipeline (GitHub Actions)

Contributing

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

Made with ❀️ using ExpressJs · Symfony 8.4 · Redis · Messenger

About

πŸš— GoOn Backend with Symfony & Symfony Security. This repository contains the backend implementation for an Uber-like ride-hailing application. Built using Symfony, the project leverages Symfony Security for robust authentication and authorization, ensuring a secure and scalable architecture.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages