π Certificate Generation System
A multi-tenant backend system for issuing, managing, and publicly verifying digital certificates β built with Spring Boot 3.4, Spring Security (JWT), PostgreSQL, and OpenPDF.
Organizations register, define courses with eligibility rules, enroll recipients, assign signatories, and generate branded PDF certificates β all through a secured REST API.
System Architecture

Request Lifecycle
Every API call flows through a consistent layered pipeline:
sequenceDiagram
participant C as Client
participant F as JwtAuthFilter
participant CT as Controller
participant S as Service
participant R as Repository
participant DB as PostgreSQL
participant EH as GlobalExceptionHandler
C->>F: HTTP Request + Bearer Token
F->>F: Extract & validate JWT
F->>F: Load OrganizationPrincipal
F->>CT: Authenticated request
CT->>S: Delegate business logic
S->>R: Data access
R->>DB: Query / Persist
DB-->>R: Result
R-->>S: Entity
S-->>CT: Response DTO
CT-->>C: HTTP 2xx + JSON
Note over S,EH: On failure
S--xEH: Throws domain exception
EH-->>C: Structured error responseDomain Model

Security Architecture

Key security decisions:
| Aspect | Implementation |
|---|---|
| Identity source | JWT principal (no X-Org-Id header spoofing) |
| Token type | Access + Refresh token pair |
| Session control | currentAuthSessionId invalidates old tokens |
| Password storage | BCrypt hashing |
| Tenant isolation | All queries scoped by orgId from JWT |
Package Structure
src/main/java/com/niranjan/certificates/
βββ CertificatesGenerationApplication.java # Spring Boot entry point
βββ controller/
β βββ AuthController.java # Login, register, refresh
β βββ OrganizationController.java # Org profile & logo
β βββ CourseController.java # Course CRUD
β βββ RecipientController.java # Recipient CRUD
β βββ SignatoryController.java # Signatory CRUD + signature upload
β βββ CertificateController.java # Generate, list, download, verify
βββ dto/
β βββ request/ # Validated input payloads
β β βββ LoginRequest, RegisterRequest
β β βββ CourseRequest, RecipientRequest
β β βββ CertificateRequest, SignatoryRequest
β β βββ UpdateOrgRequest, ListQuery
β β βββ RefreshTokenRequest
β βββ response/ # Output payloads
β βββ AuthResponse, OrganizationResponse
β βββ CourseResponse, RecipientResponse
β βββ CertificateResponse, SignatoryResponse
β βββ VerifyResponse, PageResponse
β βββ ...
βββ entity/
β βββ Organization.java # Tenant root entity
β βββ Course.java # Course with minScore eligibility
β βββ Recipient.java # Enrolled learner
β βββ Signatory.java # Certificate signer
β βββ Certificate.java # Issued certificate
β βββ CertificateStatus.java # ISSUED / REVOKED enum
βββ exception/
β βββ GlobalExceptionHandler.java # Centralized error responses
β βββ ResourceNotFoundException.java
β βββ DuplicateResourceException.java
β βββ IneligibleRecipientException.java
βββ repository/
β βββ OrganizationRepository.java
β βββ CourseRepository.java
β βββ RecipientRepository.java
β βββ SignatoryRepository.java
β βββ CertificateRepository.java
βββ security/
β βββ SecurityConfig.java # Filter chain & endpoint rules
β βββ JwtAuthenticationFilter.java # Per-request token validation
β βββ JwtService.java # Token creation & parsing
β βββ AuthSessionService.java # Refresh token rotation
β βββ OrganizationPrincipal.java # Authenticated identity
β βββ OrganizationUserDetailsService.java # UserDetailsService impl
βββ service/
βββ CertificateService.java # Interface
βββ CourseService.java
βββ RecipientService.java
βββ SignatoryService.java
βββ OrganizationService.java
βββ PdfService.java
βββ ImageService.java
βββ FileStorageService.java
βββ impl/
βββ CertificateServiceImpl.java # Core cert logic + PDF orchestration
βββ CourseServiceImpl.java # Soft delete, minScore defaults
βββ RecipientServiceImpl.java # Post-issuance guards
βββ SignatoryServiceImpl.java # Default signatory management
βββ OrganizationServiceImpl.java # Profile & logo management
βββ PdfServiceImpl.java # OpenPDF rendering engine
βββ ImageServiceImpl.java # PDF-to-image conversion
βββ FileStorageServiceImpl.java # Local disk I/OTech Stack
| Layer | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.4.5 |
| Web | Spring Web (REST) |
| Security | Spring Security + JWT (jjwt 0.12.6) |
| Persistence | Spring Data JPA / Hibernate |
| Database | PostgreSQL |
| Validation | Jakarta Bean Validation |
| PDF Generation | OpenPDF 1.3.30 |
| PDF Imaging | Apache PDFBox 3.0.3 |
| Boilerplate | Lombok |
API Overview
Public Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /api/org/register | Register a new organization |
POST | /api/auth/login | Authenticate and receive tokens |
POST | /api/auth/refresh | Rotate refresh token |
GET | /api/verify/{uniqueCode} | Publicly verify a certificate |
Protected Endpoints (Bearer token required)
| Resource | Endpoints | Operations |
|---|---|---|
| Organization | /api/org/* | Get profile, update, upload logo |
| Courses | /api/courses/* | CRUD + soft delete |
| Recipients | /api/recipients/* | CRUD with post-issuance guards |
| Signatories | /api/signatories/* | CRUD + set default + upload signature |
| Certificates | /api/certificates/* | Generate, list, get, download PDF |
Business Rules
Certificate Issuance Guards
- Recipient and signatory must belong to the authenticated organization
- Recipient's course must be active
- Recipient's score must meet or exceed the course
minScore - Only one certificate per organizationβrecipient pair (enforced in code + DB constraint)
- Failed DB writes trigger best-effort PDF cleanup
Recipient Protection
- Course cannot be changed after a certificate has been issued
- Recipient cannot be deleted after a certificate has been issued
- Course must be active at the time of creation or update
Course Lifecycle
- Courses are soft-deleted (marked inactive) to preserve historical certificates
minScoredefaults to0when omitted; validated in range[0, 100]
Error Handling
All errors are normalized through GlobalExceptionHandler into a consistent response shape:
{
"status": 404,
"message": "Resource not found",
"timestamp": "2026-04-26T00:30:00"
}| Status | Meaning |
|---|---|
400 | Validation error or illegal operation |
401 | Missing / expired / invalid token |
403 | Not authorized to access resource |
404 | Resource not found |
409 | Duplicate certificate |
422 | Recipient score below course minimum |
500 | Unexpected server error |
Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Multi-tenancy | Org ID from JWT, query-scoped | Prevents cross-tenant data leaks |
| Primary keys | UUID | Non-sequential, safe for public URLs |
| Service pattern | Interface + Impl | Thin controllers, testable business logic |
| PDF engine | OpenPDF | Lightweight, no external service dependency |
| File storage | Local disk /uploads | Simple for MVP, swappable to S3 later |
| Course deletion | Soft delete | Preserves certificate history |
| Certificate uniqueness | One per orgβrecipient | Prevents accidental duplicate issuance |
| DI style | Constructor injection (Lombok) | Immutable, testable dependencies |