A demonstration project combining Udemy-style course marketplace with Uber-style instructor matching, built with .NET 8 Web API and React 18.
CourseMarket is a hybrid learning platform that offers:
- Udemy Flow: Browse courses, purchase with Stripe, access purchased content
- Uber Logic: Request personalized live lessons with automatic instructor matching
- Three user roles (User, Instructor, Admin) with JWT authentication
- Course marketplace with search and pagination
- Secure payment processing with Stripe
- Intelligent instructor matching algorithm
- Live lesson request system
- Responsive React UI with Tailwind CSS
- Clean Architecture backend with CQRS pattern
- .NET 8 Web API
- Entity Framework Core 8 + SQLite
- MediatR (CQRS pattern)
- JWT Bearer Authentication
- Stripe.net SDK
- BCrypt for password hashing
- Swagger/OpenAPI documentation
- React 18 + TypeScript
- Vite (build tool)
- Tailwind CSS
- React Query (@tanstack/react-query)
- Zustand (state management)
- React Router v6
- Axios
- .NET 8 SDK
- Node.js 18+
- Stripe CLI (optional, for webhook testing)
cd /path/to/your/projects
# Project is already in: uber_and_udemy_project/cd backend/src/CourseMarket.API
dotnet restore
dotnet ef database updateThe appsettings.json is already configured with:
- JWT Secret:
YourSuperSecretKeyThatIsAtLeast32CharactersLongForHS256Algorithm - Stripe Test Keys: (Replace with your own from Stripe Dashboard)
{
"Stripe": {
"SecretKey": "sk_test_...",
"PublishableKey": "pk_test_...",
"WebhookSecret": "whsec_..."
}
}dotnet runThe API will be available at:
- HTTP: http://localhost:5032
- Swagger: http://localhost:5032/swagger
cd frontend
npm installThe .env file is already configured:
VITE_API_URL=http://localhost:5032/api
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_your_key_hereReplace the Stripe publishable key with your own.
npm run devThe app will be available at:
- Frontend: http://localhost:5173
The database is pre-seeded with test accounts:
Email: admin@coursemarket.com
Password: Admin123!
Email: john.instructor@coursemarket.com
Password: Instructor123!
Role: Instructor (Web Development)
Email: jane.instructor@coursemarket.com
Password: Instructor123!
Role: Instructor (Data Science)
Email: mike.instructor@coursemarket.com
Password: Instructor123!
Role: Instructor (Design)
Email: alice@coursemarket.com
Password: User123!
Role: User
Email: bob@coursemarket.com
Password: User123!
Role: User
... (4 more users: charlie, diana, eve, fiona)
Use these test cards for payment testing:
| Card Number | Scenario | Expected Result |
|---|---|---|
4242 4242 4242 4242 |
Success | Payment succeeds |
4000 0000 0000 0002 |
Decline | Card declined |
4000 0025 0000 3155 |
Auth Required | Requires 3D Secure |
Expiry Date: Any future date (e.g., 12/34) CVC: Any 3 digits (e.g., 123) ZIP: Any 5 digits (e.g., 12345)
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/register |
Create new account | No |
| POST | /api/auth/login |
Authenticate user | No |
| GET | /api/auth/me |
Get current user | Yes |
| Method | Endpoint | Description | Auth Required | Role |
|---|---|---|---|---|
| GET | /api/courses |
List all courses (paginated) | No | - |
| GET | /api/courses/{id} |
Get course details | No | - |
| GET | /api/courses/my-courses |
Get purchased courses | Yes | Any |
| POST | /api/courses |
Create new course | Yes | Instructor/Admin |
| PUT | /api/courses/{id} |
Update course | Yes | Instructor/Admin |
Query Parameters for GET /api/courses:
page(default: 1)pageSize(default: 10)search(optional)
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/purchases/create-payment-intent |
Start purchase flow | Yes |
| POST | /api/purchases/confirm |
Confirm and complete purchase | Yes |
| GET | /api/purchases/my-purchases |
Get purchase history | Yes |
| Method | Endpoint | Description | Auth Required | Role |
|---|---|---|---|---|
| POST | /api/live-lessons/requests |
Create lesson request | Yes | User |
| GET | /api/live-lessons/my-requests |
Get user's requests | Yes | User |
| GET | /api/live-lessons/assigned |
Get assigned lessons | Yes | Instructor |
| PATCH | /api/live-lessons/requests/{id}/status |
Update request status | Yes | Instructor/Admin |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /api/notifications |
Get user's notifications | Yes |
| PATCH | /api/notifications/{id}/read |
Mark notification as read | Yes |
| PATCH | /api/notifications/mark-all-read |
Mark all as read | Yes |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/webhooks/stripe |
Stripe webhook handler | No (signature validated) |
uber_and_udemy_project/
├── backend/
│ ├── CourseMarket.sln
│ └── src/
│ ├── CourseMarket.Domain/ # Entities, Enums
│ ├── CourseMarket.Application/ # CQRS Commands/Queries
│ ├── CourseMarket.Infrastructure/ # DbContext, Services
│ └── CourseMarket.API/ # Controllers, Program.cs
└── frontend/
├── src/
│ ├── api/ # Axios client + endpoints
│ ├── components/ # Reusable UI components
│ ├── pages/ # Route-level pages
│ ├── store/ # Zustand stores
│ ├── types/ # TypeScript interfaces
│ └── App.tsx # Router configuration
├── package.json
└── vite.config.ts
- User browses courses at
/courses - Clicks on course to view details at
/courses/{id} - Clicks "Purchase Course" button
- Backend creates Stripe PaymentIntent and returns
clientSecret - Frontend shows Stripe checkout form (in demo: auto-confirms)
- Payment succeeds → Backend verifies and creates Purchase record
- Course is added to user's "My Courses"
- Stripe webhook (async) validates and logs transaction
- User navigates to
/live-lessonsand clicks "Request Live Lesson" - Fills out form: topic, description, preferred date, duration
- Submits request → Backend triggers matching algorithm
- Matching Algorithm scores all instructors:
- Topic Relevance: +10 points if instructor teaches related courses
- Availability: -2 points per active assignment
- Random Factor: +0-5 for variety
- Best instructor is automatically assigned
InstructorAssignmentrecord created with match score- Notification sent to instructor
- User sees assigned instructor on
/live-lessons - Instructor sees assignment on
/instructor/dashboard
- Register new account or login with
alice@coursemarket.com / User123! - Navigate to "Browse Courses"
- Click on any course (e.g., "Complete React Masterclass")
- Click "Purchase Course"
- In demo mode: automatically confirms purchase
- Navigate to "My Courses" to see purchased course
- Login as a user (e.g.,
bob@coursemarket.com / User123!) - Navigate to "Live Lessons"
- Click "Request Live Lesson"
- Fill in:
- Topic: "Advanced TypeScript"
- Description: "Need help understanding generics"
- Preferred Date: Any future date/time
- Duration: 60 minutes
- Submit → System automatically matches best instructor
- View assignment with instructor details and match score
- Login as instructor (
john.instructor@coursemarket.com / Instructor123!) - Navigate to "Instructor Dashboard"
- View assigned live lesson requests
- See match scores and student information
To test Stripe webhooks locally:
- Install Stripe CLI
- Login:
stripe login - Forward webhooks:
stripe listen --forward-to localhost:5032/api/webhooks/stripe
- Copy the webhook signing secret (starts with
whsec_) - Update
appsettings.jsonwith the newWebhookSecret - Trigger test events:
stripe trigger payment_intent.succeeded
# Install SQLite browser (optional)
# macOS: brew install --cask db-browser-for-sqlite
# Windows: Download from https://sqlitebrowser.org/
# Database file location
backend/src/CourseMarket.API/coursemarket.dbcd backend/src/CourseMarket.API
rm -f coursemarket.db*
dotnet ef database updateThis will:
- Delete existing database
- Re-run migrations
- Re-seed test data
This project follows Clean Architecture principles. For detailed information about:
- Technology choices and rationale
- Payment flow diagrams
- Instructor matching algorithm
- Scalability strategy
See: ARCHITECTURE.md
- Clean Architecture: Domain → Application → Infrastructure → API
- CQRS: Commands (writes) and Queries (reads) separated via MediatR
- Repository Pattern: EF Core DbContext as Unit of Work
- Dependency Injection: Built-in .NET DI container
- Container/Presentational: Smart components with hooks vs pure UI
- Custom Hooks: Encapsulate React Query logic
- Protected Routes: HOC pattern for authentication/authorization
- Atomic Design: Reusable UI components (Button, Input, Card)
Solution:
# Change port in backend/src/CourseMarket.API/Properties/launchSettings.json
"applicationUrl": "http://localhost:YOUR_PORT"Solution:
cd backend/src/CourseMarket.API
dotnet ef database drop --force
dotnet ef database updateSolution:
- Ensure backend is running on http://localhost:5032
- Check that frontend
.envhas correctVITE_API_URL - Verify CORS policy in
Program.csincludes frontend origin
Solution:
- Use test card:
4242 4242 4242 4242 - Verify Stripe keys in
appsettings.jsonand.env - Check browser console for errors
- Ensure using test mode keys (start with
pk_test_andsk_test_)
- Pagination: All list endpoints support page/pageSize
- Database Indexes: Foreign keys indexed for fast queries
- React Query Caching: 5-minute stale time reduces API calls
- Code Splitting: React Router lazy loading for routes
- Password Hashing: BCrypt with salt rounds
- JWT Tokens: 7-day expiration, role-based claims
- Role-based Authorization:
[Authorize(Roles = "Instructor,Admin")] - Stripe Security: PCI compliance via Stripe.js, webhook signature validation
- SQL Injection Protection: EF Core parameterizes all queries
- XSS Protection: React escapes output by default
- Real-time notifications with SignalR
- Course reviews and ratings
- Video upload and streaming
- Instructor availability calendar
- Email notifications
- Admin dashboard with analytics
- Full Stripe checkout integration (not just PaymentIntent)
- Unit and integration tests
- CI/CD pipeline
- Deployment to Azure/AWS
This is a demonstration project for educational purposes.
For issues or questions:
- Open an issue on the repository
- Review the ARCHITECTURE.md for technical details
Built with:
Happy Coding! 🚀