Skip to content

junaidify/Learn-vault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

42 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

β–ˆβ–ˆβ•—     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—    β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β•šβ•β•β–ˆβ–ˆβ•”β•β•β•
β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘   
β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘    β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘   
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘     β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   
β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•β•β•      β•šβ•β•β•β•  β•šβ•β•  β•šβ•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•   

My Spring Boot learning sandbox β€” where patterns are forged before production.

Java Spring Boot Spring Security PostgreSQL Gradle

Commits Branches License


⚑ What is Learn Vault?

LearnVault is a full-featured e-learning backend β€” and my personal lab for mastering production-grade Java development before building Junaidify v2.

Every pattern implemented here β€” auth flows, payment integration, JPA design, exception handling β€” gets battle-tested in this repo first, then shipped to production. No tutorial hell. Real architecture decisions, real tradeoffs.


πŸ—οΈ Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        CLIENT (React / Postman)              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚ HTTP Requests
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Spring Security Filter Chain               β”‚
β”‚         JwtAuthFilter β†’ SecurityFilterChain β†’ RBAC          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚                β”‚                β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
   β”‚  AuthControllerβ”‚  β”‚CourseControllerβ”‚  β”‚PaymentControllerβ”‚
   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
          β”‚                β”‚                β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
   β”‚               Service Layer                     β”‚
   β”‚  AuthService β”‚ CourseService β”‚ PaymentService   β”‚
   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
          β”‚                                  β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
   β”‚  JPA / ORM  β”‚                    β”‚  Razorpay   β”‚
   β”‚ (Hibernate) β”‚                    β”‚     API     β”‚
   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
   β”‚ PostgreSQL  β”‚
   β”‚  Database   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”§ Tech Stack

Layer Technology Purpose
Runtime Java 17 + Spring Boot 3.3.6 Core application framework
Security Spring Security 6 + JWT (JJWT 0.12.6) Stateless auth + RBAC
OAuth2 Google OAuth2 + Custom Oauth2SuccessHandler Social login flow
Database PostgreSQL + Spring Data JPA Relational persistence
ORM Hibernate + @OneToMany / @ManyToOne Entity relationships
Payments Razorpay SDK Order creation + payment verification
Validation Jakarta Bean Validation (@NotBlank, @Email) Request validation
Build Gradle 8.x Dependency management
Utilities Lombok (DTOs only), MapStruct Boilerplate reduction

πŸ” Authentication & Security

Complete stateless auth implementation with dual login support:

POST /api/auth/register      β†’ Register user (BCrypt password)
POST /api/auth/login         β†’ Login β†’ returns JWT access token
GET  /oauth2/authorization/google β†’ Initiate Google OAuth2
GET  /oauth2/callback/google      β†’ Oauth2SuccessHandler β†’ JWT issued

Security Stack:

  • SecurityFilterChain with stateless session management
  • JwtFilter β†’ validates token on every request before controller
  • CustomUserDetailsService β†’ loads user from DB by email
  • DaoAuthenticationProvider (Spring Security 6 constructor)
  • @PreAuthorize("hasRole('ADMIN')") for role-based endpoint protection
  • GlobalExceptionHandler via @RestControllerAdvice β†’ standardized error responses

πŸ’³ Payment Integration

Razorpay backend integration β€” fully functional order lifecycle:

POST /api/payments/create-order     β†’ Creates Razorpay order, returns orderId
POST /api/payments/verify           β†’ Verifies HMAC-SHA256 signature
POST /api/payments/webhook          β†’ Handles async payment events

Flow:

Client β†’ POST /create-order β†’ RazorpayClient.orders().create()
       ← { orderId, amount, currency, key }

Client pays via Razorpay checkout ↓

Client β†’ POST /verify β†’ HMAC(orderId + paymentId, secret)
       β†’ EnrollmentService.enroll(userId, courseId)
       ← { success: true }

πŸ—„οΈ Database Schema

Core JPA entities with proper relationship modeling:

// UserEntity ─── @OneToMany ──► EnrollmentEntity
// CourseEntity ── @OneToMany ──► EnrollmentEntity
// EnrollmentEntity has @ManyToOne to both User and Course

@Entity
public class EnrollmentEntity {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private UserEntity user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "course_id")
    private CourseEntity course;

    @Enumerated(EnumType.STRING)
    private PaymentStatus paymentStatus;
}

Tables: users Β· courses Β· enrollments Β· payments


πŸ“ Project Structure

Learn-vault/
β”œβ”€β”€ src/
β”‚   └── main/
β”‚       β”œβ”€β”€ java/com/learnvault/
β”‚       β”‚   β”œβ”€β”€ config/
β”‚       β”‚   β”‚   β”œβ”€β”€ SecurityConfig.java          # SecurityFilterChain
β”‚       β”‚   β”‚   └── RazorpayConfig.java          # RazorpayClient bean
β”‚       β”‚   β”œβ”€β”€ controller/
β”‚       β”‚   β”‚   β”œβ”€β”€ AuthController.java
β”‚       β”‚   β”‚   β”œβ”€β”€ CourseController.java
β”‚       β”‚   β”‚   └── PaymentController.java
β”‚       β”‚   β”œβ”€β”€ service/
β”‚       β”‚   β”‚   β”œβ”€β”€ AuthService.java
β”‚       β”‚   β”‚   β”œβ”€β”€ CourseService.java
β”‚       β”‚   β”‚   └── PaymentService.java
β”‚       β”‚   β”œβ”€β”€ entity/
β”‚       β”‚   β”‚   β”œβ”€β”€ UserEntity.java
β”‚       β”‚   β”‚   β”œβ”€β”€ CourseEntity.java
β”‚       β”‚   β”‚   β”œβ”€β”€ EnrollmentEntity.java
β”‚       β”‚   β”‚   └── PaymentEntity.java
β”‚       β”‚   β”œβ”€β”€ dto/
β”‚       β”‚   β”‚   β”œβ”€β”€ request/                     # Incoming DTOs
β”‚       β”‚   β”‚   └── response/                    # Outgoing DTOs
β”‚       β”‚   β”œβ”€β”€ security/
β”‚       β”‚   β”‚   β”œβ”€β”€ JwtFilter.java
β”‚       β”‚   β”‚   β”œβ”€β”€ JwtUtils.java                # JJWT 0.12.6
β”‚       β”‚   β”‚   β”œβ”€β”€ CustomUserDetailsService.java
β”‚       β”‚   β”‚   └── Oauth2SuccessHandler.java
β”‚       β”‚   β”œβ”€β”€ exception/
β”‚       β”‚   β”‚   └── GlobalExceptionHandler.java  # @RestControllerAdvice
β”‚       β”‚   └── repository/
β”‚       β”‚       β”œβ”€β”€ UserRepository.java
β”‚       β”‚       β”œβ”€β”€ CourseRepository.java
β”‚       β”‚       └── EnrollmentRepository.java
β”‚       └── resources/
β”‚           └── application.yml
β”œβ”€β”€ build.gradle
└── README.md

πŸš€ Getting Started

Prerequisites

  • Java 17+
  • PostgreSQL running locally
  • Razorpay test API keys (optional for payment testing)

Setup

# Clone the repo
git clone https://github.com/junaidify/Learn-vault.git
cd Learn-vault

# Configure environment
cp src/main/resources/application.yml.example src/main/resources/application.yml
# Edit application.yml β†’ set DB credentials, JWT secret, Razorpay keys

# Run
./gradlew bootRun

application.yml

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/learnvault
    username: your_db_user
    password: your_db_password
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

app:
  jwt:
    secret: your_jwt_secret_here
    expiration: 86400000  # 24h in ms

razorpay:
  key-id: rzp_test_xxxx
  key-secret: your_razorpay_secret

πŸ§ͺ API Reference

Auth Endpoints

Method Endpoint Auth Description
POST /api/auth/register ❌ Register new user
POST /api/auth/login ❌ Login β†’ JWT
GET /api/auth/me βœ… JWT Get current user

Course Endpoints

Method Endpoint Auth Description
GET /api/courses ❌ List all courses
GET /api/courses/{id} ❌ Get course details
POST /api/courses βœ… ADMIN Create course
PUT /api/courses/{id} βœ… ADMIN Update course
DELETE /api/courses/{id} βœ… ADMIN Delete course

Payment Endpoints

Method Endpoint Auth Description
POST /api/payments/create-order βœ… JWT Create Razorpay order
POST /api/payments/verify βœ… JWT Verify payment + enroll

πŸ—ΊοΈ What I Learned Building This

Concept Key Insight
Spring Security 6 DaoAuthenticationProvider constructor changed β€” must call setUserDetailsService() explicitly
JJWT 0.12.6 Jwts.parser().verifyWith(key) replaces old .setSigningKey() API
JPA Relationships Never use raw FK fields β€” use @ManyToOne with @JoinColumn, lazy fetch by default
OAuth2 + JWT Oauth2SuccessHandler must generate JWT after OAuth2 success to stay stateless
Exception Handling @RestControllerAdvice + typed @ExceptionHandler keeps error responses consistent
DTO Pattern Service layer returns DTOs, never entities β€” decouples persistence from API contract

πŸ”­ What's Next

This repo is the testing ground for Junaidify v2 β€” a production paid course platform.

Patterns proven here β†’ shipped to:

  • pgvector + OpenAI β†’ RAG "Ask the Course" AI assistant
  • Redis β†’ session cache + rate limiting
  • AWS S3 β†’ video upload pipeline
  • Docker + GitHub Actions β†’ CI/CD
  • WhatsApp API β†’ enrollment notifications

πŸ‘€ Author

Junaid β€” Java Full-Stack Developer

GitHub LinkedIn

"Built as a sandbox. Designed like production."


About

LearnVault: My journey into advanced Java backend development. A full-featured e-learning platform featuring secure payment integration, Spring Security 6, and complex relational database design. Built solo with Spring Boot 3.3.6.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages