Spaces:
Sleeping
Quran App β Full System Architecture
1. System Overview
Quran App is a full-stack web platform that connects students with certified Quran teachers (Sheikhs) for live online recitation sessions. It also provides AI-powered solo practice where students record their recitation and receive automated evaluation.
The system has three user roles:
- Student β finds a sheikh, books sessions, practices solo, tracks progress
- Sheikh β manages availability, accepts session requests, conducts live sessions, reviews students
- Admin β approves sheikh applications, manages users, monitors the platform
High-Level System Diagram
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (Client) β
β β
β React 19 + TypeScript + Vite β
β Tailwind CSS + Radix UI (shadcn) β
β React Router v7 Β· i18next (AR/EN) Β· Stripe.js β
β STOMP/SockJS Client Β· WebRTC (RTCPeerConnection) β
βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββ
β HTTPS REST + WebSocket (WS)
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Spring Boot 4 Backend β
β (Java 21) β
β β
β REST API (port 8080) β
β Spring Security + JWT + Google OAuth2 β
β Spring Data JPA Β· WebSocket (STOMP broker) β
β JavaMail Β· Stripe SDK Β· JAVE2 (audio) β
βββββββββββββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL DB β β External Services β
β β β β
β Users, Sessions, β β β AI Service (ngrok) β recitation eval β
β Payments, β β β Stripe β payments & payouts β
β Recitations, β β β Google OAuth2 β social login β
β Reviews, β β β Gmail SMTP β email / OTP β
β Availability, β β β
β Streaks β ββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββ
2. Technology Stack
Frontend
| Concern | Technology |
|---|---|
| Framework | React 19 + TypeScript |
| Build tool | Vite 7 |
| Styling | Tailwind CSS + Radix UI (shadcn components) |
| Routing | React Router v7 |
| Internationalization | i18next + react-i18next (Arabic / English) |
| Real-time | STOMP/SockJS (@stomp/stompjs + sockjs-client) |
| Video calls | WebRTC (RTCPeerConnection, browser native) |
| Payments | @stripe/react-stripe-js + @stripe/stripe-js |
| Notifications | Sonner + react-hot-toast |
| Icons | Lucide React |
| Date picker | react-day-picker |
Backend
| Concern | Technology |
|---|---|
| Framework | Spring Boot 4.0 (Java 21) |
| Security | Spring Security + JWT (jjwt 0.12.5) + OAuth2 Client |
| Database ORM | Spring Data JPA + Hibernate |
| Database | PostgreSQL |
| WebSocket | Spring WebSocket + STOMP (in-memory broker) |
| Spring Mail (Gmail SMTP, STARTTLS) | |
| Payments | Stripe Java SDK 25.3 |
| Audio processing | JAVE2 3.5 (ffmpeg wrapper for Linux) |
| Google Auth | Google API Client 2.6 |
| Monitoring | Spring Boot Actuator |
| Boilerplate | Lombok |
3. User Roles & Capabilities
Student
- Register/login (email or Google)
- Complete profile (Quran level, language preference, accent)
- Browse and search Sheikhs by rating, price, availability
- Book 1-hour sessions with a Sheikh
- Pay for sessions via Stripe (credit card)
- Join live video sessions (WebRTC)
- Record solo recitations and get AI-powered evaluation
- View evaluation reports (word accuracy, tashkeel, mistakes)
- Track daily streak and practice statistics
- Write reviews after completed sessions
Sheikh
- Register/login (email or Google)
- Complete profile (bio, specialization, years of experience, hourly rate)
- Submit to admin approval process (including interview scheduling)
- Set weekly availability (recurring time slots per day)
- Receive and manage session requests (accept / reject)
- Conduct live video sessions
- Write session notes for students
- Receive payouts via Stripe Connect after session completion
- View their students and session history
Admin
- Approve or reject sheikh applications
- Schedule interview sessions with pending sheikhs
- Conduct video interview sessions
- Manage all users (activate / deactivate accounts)
- View full user profiles and session history
- Access admin dashboard with platform statistics
4. Frontend Architecture
Application Entry Point
The app boots from main.tsx β App.tsx. The provider stack wraps the entire app:
BrowserRouter
βββ LanguageProvider (i18next context)
βββ AuthProvider (user state, JWT management)
βββ StudentProvider (student-specific data)
βββ Routes
Route Structure
Routes are split into three groups:
Public (no auth required)
/ Landing page
/signin Email login
/register Registration
/forgot-password OTP request
/verify-otp OTP verification
/reset-password New password
/auth/google Google OAuth callback
/complete-profile Role selection after register
/complete-profile/student Student profile wizard
/complete-profile/sheikh Sheikh profile wizard
/sheikh/interview Interview scheduling (pending sheikh)
Protected β Student
/student/dashboard Dashboard with stats, streak, recent activity
/student/practice AI-powered recitation practice
/student/find-sheikh Sheikh search/browse
/student/sheikh/:id Sheikh profile + booking
/student/sessions Session list
/student/evaluations/:id AI evaluation reports
/waiting-room/:id Pre-session waiting room
/session/:sessionId Live video session
/session-report/:id Post-session report + review
/session-waiting/:id Post-session waiting (before report)
Protected β Sheikh
/sheikh/dashboard Dashboard with stats, pending requests
/sheikh/sessions Session management
/sheikh/students Student list
Protected β Admin
/admin/dashboard Platform statistics
/admin/sheikh-approval Sheikh approval queue
/admin/users All users management
/admin/user/:userId User detail view
/admin/interview/:id Live interview session
Common
/profile Edit own profile
Route Guard Logic
ProtectedRoute checks three conditions in order:
- Loading state β show spinner
- No user β redirect to
/signin - Profile not completed β redirect to
/complete-profile(except admin, who skips this)
State Management
No Redux. State lives in three React contexts:
| Context | Responsibility |
|---|---|
AuthContext |
User identity, JWT token, login/logout/register, session restore on load |
LanguageContext |
Current language (AR/EN), direction (RTL/LTR) |
StudentContext |
Student-specific data (streak, stats, etc.) |
Auth token is stored in localStorage under key authToken. User data is stored under currentUser. On app load, AuthContext re-validates the token by calling GET /api/auth/verify-profile.
Frontend Module Map
src/
βββ App.tsx Route definitions + providers
βββ main.tsx App entry + Google OAuth provider
βββ components/
β βββ auth/ Login, Register, OTP, Profile wizards
β βββ dashboards/ StudentDashboard, SheikhDashboard, AdminDashboard
β βββ sessions/ LiveSession, WaitingRoom, MySessions, SessionReport
β βββ sheikh/ FindSheikh, SheikhProfile, MyStudents, UpdatePricing
β βββ practice/ RecordingPractice, EvaluationReports
β βββ profile/ ProfilePage
β βββ admin/ SheikhApproval, UserManagement, InterviewSession
β βββ ui/ Reusable shadcn/Radix UI components (50+ components)
β βββ Navbar.tsx Top navigation bar
β βββ RoleSwitcher.tsx Dev tool for switching roles
βββ hooks/
β βββ useWebRTC.ts WebRTC + STOMP signaling hook
βββ lib/
β βββ authContext.tsx Auth state and API calls
β βββ languageContext.tsx i18n context
β βββ StudentContext.tsx Student data context
β βββ api/ All REST API modules (one file per domain)
β βββ aiApi.ts AI analysis service calls
β βββ bookingApi.ts Availability, booking, sessions
β βββ paymentApi.ts Stripe payment flow
β βββ studentApi.ts Student data
β βββ sheikhApi.ts Sheikh data
β βββ adminApi.ts Admin operations
β βββ ...
βββ types/
β βββ index.ts Domain types (User, Session, Payment, etc.)
β βββ api.ts API helper types (PaginatedResponse, AppError, etc.)
βββ styles/
βββ globals.css
5. Backend Architecture
Package Structure
The backend is organized by domain slice, not by layer. Each domain has its own controller, service, repository, DTO, and mapper.
com.yourcompany.quranapp.quran_app_backend/
βββ config/
β βββ SecurityConfig.java CORS, JWT filter chain, role rules
β βββ GlobalExceptionHandler.java Centralized error responses
βββ security/
β βββ JwtAuthenticationFilter.java Validates Bearer token on every request
β βββ CustomUserDetails.java Wraps User entity, exposes userId + role
β βββ UserDetailsServiceImpl.java Loads user by email from DB
βββ entity/ JPA entities (data model)
βββ dashboard/
β βββ common/ Auth (login, register, OTP, Google OAuth)
β βββ student/ Student APIs (sessions, recitations, profile)
β βββ sheikh/ Sheikh APIs (sessions, availability, notes)
β βββ admin/ Admin APIs (approvals, user management)
βββ payment/ Stripe payment controller + service
βββ websocket/ WebSocket config + signaling controller
βββ google/ Google token verification
βββ setup/
βββ DataLoader.java Seed data on startup
Security Configuration
Spring Security is configured as stateless (no HTTP session). Every request goes through:
HTTP Request
β JwtAuthenticationFilter
β Extract Bearer token from Authorization header
β Validate token via JwtService
β Load user from DB
β Set SecurityContext
β SecurityFilterChain
β Check role-based access rules
β Controller
Endpoint access rules:
| Path pattern | Access |
|---|---|
/api/auth/** |
Public |
/api/public/** |
Public |
/ws/** |
Public (WebSocket needs its own auth) |
/api/admin/** |
ADMIN only |
/api/sheikhs/** |
STUDENT only (browse sheikhs) |
/api/sheikh/** |
SHEIKH, ADMIN, STUDENT |
/api/student/** |
STUDENT, SHEIKH, ADMIN |
/api/reviews/** |
STUDENT, SHEIKH, ADMIN |
/api/payments/** |
STUDENT, SHEIKH, ADMIN |
| Everything else | Any authenticated user |
Domain Controllers
Common β /api/auth/**
| Endpoint | Description |
|---|---|
POST /register/student |
Register new student |
POST /register/sheikh |
Register new sheikh |
POST /login |
Email/password login β returns JWT |
POST /google |
Google ID token login/register |
POST /forgot-password |
Send OTP email |
POST /verify-otp |
Validate OTP |
POST /reset-password |
Set new password with OTP |
POST /logout |
Invalidate session (stateless β client discards token) |
GET /verify-profile |
Check profile completion status |
POST /complete-google-profile |
Complete profile for Google-registered student |
POST /complete-google-profile/sheikh |
Complete profile for Google-registered sheikh |
Student β /api/student/**
| Endpoint | Description |
|---|---|
GET /sessions |
Get all student sessions (filterable by status) |
GET /sessions/{id} |
Get single session |
POST /sessions |
Book a session with a sheikh |
POST /sessions/{id}/join |
Record student join timestamp |
POST /sessions/{id}/leave |
Record student leave timestamp |
GET /recitations |
Get all recitations |
GET /recitations/recent |
Get top 5 recent recitations |
GET /recitations/stats |
Get recitation statistics |
POST /recitations |
Upload audio + surah number β AI eval + save |
GET /recitations/{id} |
Get single recitation detail |
GET /profile/complete |
(also via auth) Complete student profile |
Sheikh β /api/sheikh/**
| Endpoint | Description |
|---|---|
GET /sessions |
Get sheikh sessions (filterable by status) |
GET /sessions/{id} |
Get single session |
POST /sessions/{id}/accept |
Accept session β status = TOPAY |
POST /sessions/{id}/reject |
Reject/cancel session |
POST /sessions/{id}/start |
Start session β status = ON_GOING |
POST /sessions/{id}/end |
End session + save notes β status = COMPLETED |
POST /sessions/{id}/join |
Record sheikh join timestamp |
POST /sessions/{id}/leave |
Record sheikh leave timestamp |
POST /sessions/{id}/notes |
Update session notes |
GET /availability |
Get sheikh's weekly availability slots |
PUT /availability |
Set sheikh's weekly availability slots |
GET /students |
Get sheikh's student list |
GET /dashboard |
Dashboard statistics |
Student browsing Sheikhs β /api/sheikhs/**
| Endpoint | Description |
|---|---|
GET / |
Browse sheikhs (filters: country, price, rating, language) |
GET /{id} |
Get sheikh public profile |
GET /{id}/available-dates |
Get available dates in next N days |
GET /{id}/available-slots/day |
Get time slots for a specific date |
GET /{id}/available-slots |
Get all slots in a date range |
GET /{id}/Top-Reviews |
Get top reviews for a sheikh |
Admin β /api/admin/**
| Endpoint | Description |
|---|---|
GET /dashboard |
Platform-wide statistics |
GET /sheikh-approvals |
Pending sheikh applications |
POST /sheikh-approvals/{id}/approve |
Approve sheikh |
POST /sheikh-approvals/{id}/reject |
Reject sheikh with reason |
POST /sheikh-approvals/{id}/schedule-interview |
Schedule interview session |
GET /users |
All users with filters |
GET /users/{id} |
Full user profile |
POST /users/{id}/deactivate |
Deactivate user account |
Reviews β /api/reviews/**
| Endpoint | Description |
|---|---|
POST /session/{id} |
Student submits review after session |
GET /student/session/{id}/details |
Student view of session + review |
GET /sheikh/session/{id}/details |
Sheikh view of session + review |
Payments β /api/payments/**
| Endpoint | Description |
|---|---|
POST /initiate/card |
Create Stripe PaymentIntent, return clientSecret |
POST /confirm-payment/{intentId} |
Confirm payment with backend after Stripe.js success |
POST /confirm-session/{sessionId} |
Verify payment is PAID when sheikh accepts |
POST /payout/{sessionId} |
Transfer funds to sheikh via Stripe Connect |
POST /refund/{sessionId} |
Refund student via Stripe Refunds API |
POST /connect/sheikh/{sheikhId} |
Create Stripe Connect account for sheikh |
WebSocket β /ws
| Destination | Description |
|---|---|
/app/signal |
Send WebRTC/chat signaling message |
/topic/session/{id} |
Subscribe to receive signals for a session |
6. Data Model
Entity Relationship Summary
User (1) βββββββββββ (0..1) StudentProfile
User (1) βββββββββββ (0..1) SheikhProfile
User (1) βββββββββββ (0..1) Streak
User (1) βββββββββββ (n) Availability [sheikh side]
User (1) βββββββββββ (n) Session [as student]
User (1) βββββββββββ (n) Session [as sheikh]
User (1) βββββββββββ (n) Recitation [as student]
User (1) βββββββββββ (n) Payment [as student]
User (1) βββββββββββ (n) Payment [as sheikh]
User (1) βββββββββββ (n) Review [as student]
Session (1) βββββββ (0..1) Payment
Session (1) βββββββ (0..1) Review
Entity Details
User
id BIGINT PK
email VARCHAR UNIQUE
passwordHash VARCHAR
fullName VARCHAR
phone VARCHAR
gender VARCHAR
country VARCHAR
birthDateTime TIMESTAMP
role ENUM(STUDENT, SHEIKH, ADMIN)
status ENUM(ALIVE, DEAD) [soft delete]
isActive BOOLEAN
authProvider VARCHAR [LOCAL or GOOGLE]
resetPasswordOtp VARCHAR
resetPasswordOtpExpiresAt TIMESTAMP
createdAt TIMESTAMP
Indexes: role, phone, country, is_active, created_at
StudentProfile
id (= user_id) BIGINT PK FK β users.id
quranLevel VARCHAR [beginner / intermediate / advanced]
preferredLanguage VARCHAR
preferredAccent VARCHAR
profilePhotoUrl VARCHAR
SheikhProfile
id (= user_id) BIGINT PK FK β users.id
bio TEXT
specialization VARCHAR
yearsOfExperience INT
hourlyRate DECIMAL(10,2)
ratingAvg DOUBLE
ratingCount INT
certificateUrl VARCHAR
profilePhotoUrl VARCHAR
status ENUM(PENDING, APPROVED, REJECTED, INTERVIEW_SCHEDULED)
rejectionReason VARCHAR
stripeAccountId VARCHAR [Stripe Connect account ID]
interviewDateTime TIMESTAMP
messageToPendingSheikh VARCHAR
Indexes: status, rating_avg, hourly_rate, status+rating_avg
Session
id BIGINT PK
scheduledStart TIMESTAMP
scheduledEnd TIMESTAMP
actualStart TIMESTAMP
actualEnd TIMESTAMP
studentJoinedAt TIMESTAMP
sheikhJoinedAt TIMESTAMP
studentLeftAt TIMESTAMP
sheikhLeftAt TIMESTAMP
status ENUM(REQUESTED, TOPAY, ON_GOING, COMPLETED, CANCELLED, MISSED)
price DECIMAL(10,2)
sessionDescription VARCHAR(255)
sessionNotes TEXT
student_id FK β users.id
sheikh_id FK β users.id
createdAt TIMESTAMP
updatedAt TIMESTAMP
Indexes: student_id, sheikh_id, status, scheduled_start,
sheikh+status+start, student+status, sheikh+student
Payment
id BIGINT PK
amount DECIMAL(10,2)
currency VARCHAR(10) [Always "EGP" currently, Stripe handles conversion]
paymentMethod ENUM(CREDIT_CARD, VODAFONE_CASH)
paymentStatus ENUM(PENDING, PAID, REFUNDED, FAILED)
transactionId VARCHAR UNIQUE [Stripe PaymentIntent ID]
orderId VARCHAR UNIQUE [Stripe Order ID]
transferId VARCHAR UNIQUE [Stripe Transfer/Refund ID]
platformCommission DECIMAL(10,2) [15% of amount]
sheikhPayout DECIMAL(10,2) [85% of amount]
student_id FK β users.id
sheikh_id FK β users.id
session_id FK β sessions.id UNIQUE
paymentDate TIMESTAMP
capturedAt TIMESTAMP
transferredAt TIMESTAMP
refundedAt TIMESTAMP
Indexes: student_id, sheikh_id, payment_status
Recitation
id BIGINT PK
surahName VARCHAR(25)
ayahStart INT
ayahEnd INT
wordScore DECIMAL(4,2)
tashkeelScore DECIMAL(4,2)
totalScore DECIMAL(4,2) [tajweed score from AI]
durationInMinutes DECIMAL(5,2)
mistakesReportJson JSONB [full AI response stored as JSON]
student_id FK β users.id
createdAt TIMESTAMP
Index: student_id
Review
id BIGINT PK
comment TEXT
rate INT (1-5)
session_id FK β sessions.id UNIQUE
student_id FK β users.id
createdAt TIMESTAMP
Index: student_id
Availability
id BIGINT PK
dayOfWeek ENUM(MONDAY..SUNDAY)
startTime TIME
endTime TIME
sheikh_id FK β users.id
Indexes: sheikh_id, day_of_week
Streak
id (= user_id) BIGINT PK FK β users.id
currentStreak INT
longestStreak INT
lastActivityDate DATE
7. Authentication & Authorization Flow
Email/Password Registration
Frontend Backend
βββββββββ βββββββ
POST /api/auth/register/student
{ fullName, email, password,
phone, gender, country,
birthDate }
ββββΊ Validate fields
Hash password (BCrypt)
Save User (role=STUDENT)
Generate JWT
ββββ { token, user }
POST /api/auth/verify-profile
Authorization: Bearer <token>
ββββΊ Decode token β userId
Check StudentProfile exists
ββββ { profileCompleted, profileStatus }
β if profileCompleted = false β redirect to /complete-profile/student
POST /api/student/profile/complete
{ quranLevel, language, accent }
ββββΊ Save StudentProfile
ββββ 200 OK
β redirect to /student/dashboard
Google OAuth Flow
Frontend Backend Google
βββββββββ βββββββ ββββββ
User clicks "Sign in with Google"
β Google Identity Services
returns idToken
ββββββββΊ Verify idToken
POST /api/auth/google
{ idToken, role: "STUDENT" }
ββββΊ Verify idToken with
Google API Client
Find/create User
Generate JWT
ββββ { token, user }
(same profile completion flow as above)
JWT Token Lifecycle
- Token expiry: 24 hours (86400000 ms, configurable via
app.jwt.expiration-ms) - Token storage:
localStoragekeyauthToken - Token format: Standard JWT signed with HMAC-SHA key (
app.jwt.secret-key) - Every protected API request sends:
Authorization: Bearer <token> JwtAuthenticationFiltervalidates token on every request before it reaches the controller- On app load,
AuthContextrestores session by re-callingGET /api/auth/verify-profile
Password Reset (OTP flow)
POST /api/auth/forgot-password { email }
β Generate 6-digit OTP
β Store OTP + expiry in users table
β Send OTP via Gmail SMTP
POST /api/auth/verify-otp { email, otp }
β Validate OTP matches + not expired
β Return success
POST /api/auth/reset-password { email, otp, newPassword }
β Re-validate OTP
β BCrypt hash new password
β Clear OTP fields
8. Session Lifecycle
Complete Flow: Booking to Completion
1. REQUESTED
Student books a session β POST /api/student/sessions
Session created with status = REQUESTED
2. TOPAY (payment pending)
Sheikh accepts β POST /api/sheikh/sessions/{id}/accept
Status changes to TOPAY
3. Payment
Student sees payment prompt
Frontend creates PaymentIntent β POST /api/payments/initiate/card
Backend returns { clientSecret, publishableKey }
Stripe.js handles card entry on frontend
On Stripe success β POST /api/payments/confirm-payment/{intentId}
Status changes to SCHEDULED (payment confirmed)
4. ON_GOING
At session time, both parties join waiting room
Sheikh starts β POST /api/sheikh/sessions/{id}/start
Status = ON_GOING
Both parties join live WebRTC session
5. COMPLETED
Sheikh ends session + adds notes β POST /api/sheikh/sessions/{id}/end
Status = COMPLETED
Payout triggered β POST /api/payments/payout/{sessionId}
Platform keeps 15%, sheikh receives 85% via Stripe Connect
6. Review
Student writes review β POST /api/reviews/session/{id}
Sheikh rating updated (rolling average)
Exception paths:
β Sheikh rejects β CANCELLED
β Session time passes without start β MISSED β student refunded
Session Statuses
REQUESTED β TOPAY β (SCHEDULED) β ON_GOING β COMPLETED
ββββΊ CANCELLED (sheikh rejects)
ββββββββββββββββΊ MISSED (no-show, auto-refund)
9. Real-Time Communication (WebRTC + WebSocket)
Architecture
The backend acts as a signaling server only. It does not relay audio/video. Actual media flows peer-to-peer between browser clients via WebRTC.
Student Browser Backend (Signaling) Sheikh Browser
βββββββββββββββ βββββββββββββββββ ββββββββββββββ
Connect SockJS /ws βββββββΊ STOMP broker Connect SockJS /ws
Subscribe /topic/ /topic/session/{id} Subscribe /topic/
session/{id} session/{id}
Send join signal βββββββΊ Broadcast to topic βββββββββΊ Receives join
/topic/session/{id}
Creates RTCPeerConnection Creates RTCPeerConnection
Creates Offer βββββββΊ Broadcast βββββββββΊ Receives Offer
Creates Answer
Receives Answer βββββββ Broadcast βββββββββ Sends Answer
ICE exchange ββββββββΊ Broadcast ββββββββββΊ ICE exchange
Direct P2P stream βββββββββββββββββββββββββββββββββββββββββΊ Direct P2P stream
(audio + video, no server relay)
Signaling Message Types
| Type | Direction | Purpose |
|---|---|---|
join |
Any β Topic | Notify that a user entered the session room |
offer |
Caller β Topic | WebRTC SDP offer |
answer |
Callee β Topic | WebRTC SDP answer |
ice-candidate |
Any β Topic | ICE connectivity candidates |
chat |
Any β Topic | In-session text chat message |
ICE Servers (STUN)
The frontend uses Google's public STUN servers for NAT traversal:
stun:stun.l.google.com:19302stun:stun1.l.google.com:19302stun:stun2.l.google.com:19302
No TURN server is currently configured. This means sessions behind symmetric NAT may fail to connect.
WebSocket Config (Backend)
- Endpoint:
/wswith SockJS fallback - Application destination prefix:
/app(send to controller) - Broker topics:
/topic(broadcast),/queue(point-to-point) - User destination prefix:
/user
10. Payment Flow (Stripe)
Architecture
The system uses Stripe Payment Intents for collecting student payments and Stripe Connect (Express accounts) for paying out sheikhs.
Student Payment Flow
Frontend Backend Stripe
βββββββββ βββββββ ββββββ
POST /api/payments/initiate/card
{ sessionId, studentId,
sheikhId, amount }
ββββΊ Create PaymentIntent
(amount in smallest unit)
βββββ { clientSecret,
publishableKey,
paymentIntentId }
Stripe.js loads card form
User enters card details
stripe.confirmCardPayment(clientSecret)
βββββββΊ Charge card
βββββββ Payment confirmed
POST /api/payments/confirm-payment/{intentId}
ββββΊ Retrieve PaymentIntent from Stripe
Verify status = succeeded
Save Payment record (PAID)
Update Session status β SCHEDULED
βββββ { success: true }
Sheikh Payout Flow (after session completion)
Backend Stripe
βββββββ ββββββ
POST /api/payments/payout/{sessionId}
Verify session is COMPLETED
Calculate: sheikhPayout = amount Γ 0.85
commission = amount Γ 0.15
Create Stripe Transfer to sheikh's
connected account (stripeAccountId) βββββββΊ Transfer funds
Update Payment: transferredAt, transferId βββββββ Transfer confirmed
Refund Flow (missed/cancelled sessions)
Backend Stripe
βββββββ ββββββ
POST /api/payments/refund/{sessionId}
Verify payment is PAID
Create Stripe Refund for full amount βββββββΊ Refund issued
Update Payment: refundedAt, paymentStatus=REFUNDED
Platform Commission
Commission percentage is configurable: platform.commission.percentage = 15
11. AI Recitation Evaluation Flow
Flow
Frontend Backend AI Service
βββββββββ βββββββ ββββββββββ
User records audio in browser
(MediaRecorder API)
POST /api/student/recitations
multipart/form-data
{ surahNumber, audio file }
ββββΊ Receive audio file
Convert to WAV (JAVE2)
Forward to AI service:
POST {AI_BASE_URL}/analyze
{ audio, surah_number }
βββββββΊ Speech recognition
Text alignment with
Quran reference
Error detection:
- Extra words
- Missed words
- Tashkeel errors
βββββββ { surah_info,
overall_stats,
ayahs[] }
Parse AI response
Calculate scores:
- wordScore
- tashkeelScore
- totalScore
Store Recitation
(mistakesReportJson as JSONB)
βββββ RecitationDetailsDto
Display scores + mistake report
AI Response Structure
The AI service returns per-ayah analysis:
words_stage: extra words and missed words per ayahtashkeel_stage: per-word tashkeel comparison with reasonsoverall_stats: word accuracy % and tashkeel accuracy %
Scores are stored as JSONB in mistakesReportJson for full replay capability on the frontend.
Direct AI Call (frontend also calls AI directly)
The frontend aiApi.ts can also call the AI service directly (bypassing the backend) for immediate feedback before saving. This is used in the RecordingPractice component.
12. Sheikh Approval Flow
Steps
1. Register
Sheikh registers β role = SHEIKH
Status = PENDING (no SheikhProfile yet)
2. Complete Profile
POST /api/sheikh/profile/complete
{ bio, specialization, yearsOfExperience,
hourlyRate, certificate }
SheikhProfile created β status = PENDING
3. Admin Review
Admin sees sheikh in approval queue
GET /api/admin/sheikh-approvals
4. Interview Scheduling
Admin schedules interview:
POST /api/admin/sheikh-approvals/{id}/schedule-interview
{ dateTime, message }
Status β INTERVIEW_SCHEDULED
Sheikh notified (email or in-app)
5. Interview Session
Admin and sheikh join live video call
(same WebRTC infrastructure as student sessions)
Route: /admin/interview/:sheikhId
Route: /sheikh/interview-session/:sheikhId
6. Decision
Approve: status β APPROVED, sheikh can accept students
Reject: status β REJECTED, rejectionReason saved
7. Post-Approval
Sheikh sets hourly rate: PUT /api/sheikh/update-pricing
Sheikh sets availability: PUT /api/sheikh/availability
Sheikh appears in student search: GET /api/sheikhs/
13. Internationalization (i18n)
The frontend supports Arabic and English using i18next.
- Language detection: browser preference (
i18next-browser-languagedetector) - Translations:
src/lib/translations.ts - Language context:
src/lib/languageContext.tsx - Direction: Automatically switches between LTR (English) and RTL (Arabic)
- Language switcher:
src/components/LanguageSwitcher.tsx
14. Database Optimization
JDBC Batching (Hibernate)
Enabled globally for bulk insert/update performance:
hibernate:
jdbc:
batch_size: 20
order_inserts: true
order_updates: true
Indexes
Key composite and single-column indexes defined at entity level:
| Table | Indexes |
|---|---|
| users | role, phone, country, is_active, created_at |
| sessions | student_id, sheikh_id, status, scheduled_start, sheikh+status+start, student+status, sheikh+student |
| sheikh_profile | status, rating_avg, hourly_rate, status+rating_avg |
| payment | student_id, sheikh_id, payment_status |
| recitations | student_id |
| review | student_id |
| availability | sheikh_id, day_of_week |
15. Configuration & Environment
Backend Environment Variables
| Variable | Purpose |
|---|---|
DB_URL |
PostgreSQL JDBC URL |
PGUSER |
Database username |
PGPASSWORD |
Database password |
JWT_SECRET |
JWT HMAC signing key |
MAIL_USERNAME |
Gmail address for OTP emails |
MAIL_PASSWORD |
Gmail app password |
STRIPE_SECRET_KEY |
Stripe secret key |
STRIPE_PUBLISHABLE_KEY |
Stripe publishable key |
GOOGLE_CLIENT_ID |
Google OAuth client ID |
Frontend Environment Variables
| Variable | Purpose |
|---|---|
VITE_API_BASE_URL |
Backend base URL (defaults to http://localhost:8080) |
AI Service
External Python service exposed via ngrok:
- Base URL: configured in
app.ai.base-url - Connect timeout: 5s
- Read timeout: 600s (10 min, for long audio files)
- Endpoint:
POST /analyzewith{ audio, surah_number }
16. Cross-Cutting Concerns
Error Handling
- Backend:
GlobalExceptionHandlercatches all exceptions and returns consistent JSON error responses - Frontend:
AppErrorclass standardizes error handling across all API modules withstatus+pathcontext - Frontend:
ErrorBoundarycomponent wraps dashboards to prevent full-page crashes
CORS
Backend allows requests from http://localhost:* with credentials. For production this needs to be restricted to the actual frontend domain.
Monitoring
Spring Boot Actuator is enabled with all endpoints exposed (management.endpoints.web.exposure.include: "*"). For production this should be restricted to health + metrics only.
Logging
org.hibernate.SQL: DEBUG # All SQL statements
org.hibernate.stat: DEBUG # Query counts per session
Hibernate statistics are enabled (generate_statistics: true) for N+1 query detection during development.
Schema Management
JPA ddl-auto: update β Hibernate auto-updates the schema on startup. This is acceptable for development but should be replaced with Flyway or Liquibase for production.
Audio Processing
JAVE2 wraps ffmpeg. The Linux 64-bit native binary is included as a Maven dependency (jave-nativebin-linux64). Audio files uploaded by students are converted to WAV before being sent to the AI service.