Full-Stack case study
HRC TECH
Secure e-learning platform with payments and protected course content

- Commits
- 59
- Build window
- 18 days
- Lines of code
- 7,239
- Technologies
- 12
in the repository
19 Sept 2026 to 6 Oct 2026
hand-written source
in the stack
Overview
Students browse and buy courses, then learn from video and PDF lessons with quizzes and progress tracking. Admins manage courses, lessons, quizzes and enrollments. Course files are never served as static assets: access is token-gated and every lesson is watermarked.
Goal. Selling video and PDF courses online means paying students must get the content while everyone else (and copy-pasters) must not. The goal was a platform where access is enforced on the server and shared files can be traced back to a student.
My role
Solo developer: backend, frontend, database, payments and deployment setup
What it does
- 01Student and Admin roles with backend-enforced authorization
- 02JWT access tokens plus an httpOnly refresh-token cookie; rate-limited auth, refresh and streaming endpoints
- 03Stripe Checkout (test mode) with signature-verified, idempotent webhooks
- 04Purchase-based access to video and PDF lessons through short-lived, hashed content tokens
- 05Background pipeline that watermarks videos (FFmpeg) and PDFs (PDFsharp) per lesson
- 06Quizzes with scored attempts and per-lesson progress tracking
- 07Admin screens for courses, lessons, quizzes and enrollments
- 08Swagger API docs, Docker Compose setup and an error boundary in the React app
Architecture
Engineering decisions
Four-project backend
Domain, Application, Infrastructure and Api projects keep business rules independent of EF Core, Stripe and ASP.NET.
Hashed content tokens
Each token is tied to a student and a lesson, expires, and only its SHA-256 hash is stored, so a database leak does not expose working links.
No static file serving
Lessons are streamed through a controller that sets Cache-Control: no-store and supports byte ranges so video seeking works.
Idempotent payments
Every Stripe event ID is stored in a WebhookEvents table; a retried webhook is recognised and skipped, and an already-paid payment returns early.
Watermarking off the request path
Uploads are queued in a Channel and processed by a BackgroundService, so admins are not blocked while FFmpeg runs.
In-memory access token
The React app keeps the short-lived JWT in memory and relies on the httpOnly refresh cookie, which avoids localStorage token theft.
Tech stack
- ASP.NET Core 8
- C#
- Entity Framework Core
- PostgreSQL
- React
- Vite
- Tailwind CSS
- Stripe
- FFmpeg
- PDFsharp
- JWT
- Docker
Timeline
19 Sept 2026
Project created
.NET solution started; domain and application layers, authentication, login model, rate limiting and Swagger followed on 19-20 Sep.
20 Sept 2026
Course management
Domain, infrastructure and DbContext, admin CRUD, public catalogue, lesson and PDF upload.
21 Sept 2026
Watermarking
FFmpeg video and document watermark pipeline.
22 Sept 2026
Protected viewing
Content streaming protection, enrollment migration and API checks.
23 Sept 2026
Stripe payments
Entities, gateway, DbContext wiring and webhook handling; fixes on 24 and 26 Sep.
25 Sept 2026
Quizzes and React frontend
Quiz domain, DTOs and controller; React/Vite app with auth pages, routes and a secure player.
27 Sept 2026
UI and progress
Layout, YouTube section, bug fixes, progress tracking.
30 Sept 2026
Backend testing
API and course/admin flow tests; test setup on 2 Oct.
2 Oct 2026
Docker and deployment
Docker backend setup, file storage and deployment configuration.
6 Oct 2026
Polish
Error boundary, thumbnail and PDF viewer fixes.
Scope and next steps
- Payments run in Stripe test mode.
- Hosted on a free tier, so the first request after idle time can take a few seconds.
Related notes
Next project
Shop-Zone