A complete, production-ready event ticketing platform built with FastAPI, featuring Paystack payment integration, QR code generation, SendGrid email notifications, and comprehensive security features.
- Features
- Tech Stack
- Prerequisites
- Installation & Setup
- Configuration
- Running the Application
- API Documentation
- Testing
- Deployment
- Project Structure
- Security Features
- Troubleshooting
- User Management: Complete authentication system with JWT tokens
- Event Management: Create, list, and manage events with capacity tracking
- Payment Processing: Secure Paystack integration with circuit breaker pattern
- Ticket Generation: Automatic QR code generation for tickets
- Email Notifications: Automated ticket delivery via SendGrid
- Rate Limiting: Built-in API rate limiting protection
- Security Headers: Comprehensive security middleware
- RESTful API design
- PostgreSQL database with SQLAlchemy ORM
- Bcrypt password hashing (12 rounds)
- JWT token-based authentication
- Circuit breaker pattern for payment gateway resilience
- Comprehensive test coverage with pytest
- CORS middleware for frontend integration
- Request logging and monitoring
- Framework: FastAPI 0.116.1
- Database: PostgreSQL with SQLAlchemy 2.0.43
- Authentication: JWT (python-jose 3.5.0) + Bcrypt (4.3.0)
- Payment Gateway: Paystack API
- QR Codes: qrcode 8.2 + Pillow 11.3.0
- Email: SendGrid API
- Testing: pytest 8.4.2 + pytest-asyncio
- Server: Uvicorn 0.35.0
- Production: Gunicorn 23.0.0 with Uvicorn workers
Before you begin, ensure you have the following installed:
- Python 3.11+ (tested with 3.13.7)
- PostgreSQL 12+ (or access to a PostgreSQL database)
- pip (Python package manager)
- Git (for cloning the repository)
-
PostgreSQL Database
- Local installation OR cloud service (Render, Heroku, AWS RDS, etc.)
-
Paystack Account
- Sign up at paystack.com
- Get your test/live API keys from the dashboard
-
SendGrid Account (for email notifications)
- Sign up at sendgrid.com
- Get your API key from the dashboard
- Verify your sender email address
git clone <your-repo-url>
cd EventTicketingWindows:
python -m venv venv
venv\Scripts\activatemacOS/Linux:
python3 -m venv venv
source venv/bin/activatepip install -r requirements.txtNote: If you encounter issues with psycopg2, try:
pip install psycopg2-binaryOption A: Local PostgreSQL
-
Install PostgreSQL from postgresql.org
-
Create a database:
CREATE DATABASE event_ticketing;
CREATE USER event_user WITH PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE event_ticketing TO event_user;- Your DATABASE_URL will be:
postgresql://event_user:your_secure_password@localhost:5432/event_ticketing
Option B: Render PostgreSQL (Free)
- Go to render.com
- Create a new PostgreSQL database
- Copy the "External Database URL"
Option C: Other Cloud Providers
- Heroku Postgres
- AWS RDS
- Supabase
- ElephantSQL
- Copy the example environment file:
cp .env.example .env- Edit
.envwith your actual values:
# Database - REQUIRED
DATABASE_URL=postgresql://user:password@host:5432/database
# JWT Secret - REQUIRED (generate a secure random string)
JWT_SECRET_KEY=your-super-secret-key-minimum-32-characters-long
# Paystack Keys - REQUIRED
PAYSTACK_SECRET_KEY=sk_test_your_secret_key_here
PAYSTACK_PUBLIC_KEY=pk_test_your_public_key_here
# SendGrid Configuration - REQUIRED
SENDGRID_API_KEY=SG.your_sendgrid_api_key_here
SENDGRID_USER=your-verified-sender@example.com
# Frontend URL - OPTIONAL (defaults to localhost:3000)
FRONTEND_URL=http://localhost:3000-
JWT_SECRET_KEY: Generate using Python:
import secrets print(secrets.token_urlsafe(32))
-
SENDGRID_API_KEY: Get from SendGrid Dashboard β Settings β API Keys
-
SENDGRID_USER: Must be a verified sender email in SendGrid
-
Never commit
.envto version control (already in.gitignore)
- Log in to SendGrid
- Go to Settings β Sender Authentication
- Verify your sender email address
- Create an API Key with "Mail Send" permissions
- Use the verified email as
SENDGRID_USER
uvicorn app.main:app --reloadThe API will be available at: http://localhost:8000
gunicorn app.main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000-
Health Check:
curl http://localhost:8000/health
Expected:
{"status":"healthy"} -
API Root:
curl http://localhost:8000/
Expected: JSON with API info
Once the server is running, access the auto-generated API documentation:
-
Swagger UI: http://localhost:8000/docs
- Interactive API explorer
- Test endpoints directly in browser
- View request/response schemas
- Try it out feature for all endpoints
-
ReDoc: http://localhost:8000/redoc
- Alternative documentation format
- Better for reading/reference
- Clean, professional layout
-
POST /auth/register- Register new user- Body:
{ "email": "user@example.com", "name": "User Name", "password": "password123" } - Response: User object with ID and timestamps
- Body:
-
POST /auth/login- Login and get JWT token- Body:
{ "email": "user@example.com", "password": "password123" } - Response:
{ "access_token": "...", "token_type": "bearer", "user": {...} }
- Body:
-
GET /auth/me- Get current user info (requires auth)- Headers:
Authorization: Bearer <token> - Response: Current user object
- Headers:
-
POST /events/- Create event (requires auth)- Headers:
Authorization: Bearer <token> - Body: Event details (title, description, date, location, price, capacity)
- Response: Created event object
- Headers:
-
GET /events/- List all events (public)- Query:
?skip=0&limit=100(pagination) - Response: Array of event objects
- Query:
-
GET /events/{event_id}- Get event details (public)- Response: Single event object
-
POST /payments/initialize- Initialize payment (requires auth)- Headers:
Authorization: Bearer <token> - Body:
{ "event_id": 1, "email": "user@example.com" } - Response:
{ "authorization_url": "...", "reference": "...", "access_code": "..." }
- Headers:
-
GET /payments/verify/{reference}- Verify payment (requires auth)- Headers:
Authorization: Bearer <token> - Response:
{ "message": "...", "ticket_code": "...", "qr_code_path": "...", "email_sent": true }
- Headers:
-
GET /tickets/my-tickets- Get user's tickets (requires auth)- Headers:
Authorization: Bearer <token> - Response: Array of ticket objects
- Headers:
-
GET /tickets/{ticket_code}- Get ticket by code (public)- Response: Single ticket object with QR code path
- Register a user or login to get a token
- Include token in subsequent requests:
Authorization: Bearer <your_jwt_token> - Token expires after 30 minutes (configurable)
- Initialize Payment: Get Paystack checkout URL
- User Completes Payment: Redirect to Paystack (external)
- Verify Payment: Confirm payment and generate ticket
- Receive Email: SendGrid sends ticket with QR code
pytestpytest --cov=app --cov-report=htmlView coverage report: Open htmlcov/index.html in your browser
pytest tests/test_auth.py
pytest tests/test_events.py
pytest tests/test_payments.py
pytest tests/test_tickets.py
pytest tests/test_services.pytests/test_auth.py- Authentication endpoints (register, login, JWT)tests/test_events.py- Event management (create, list, retrieve)tests/test_payments.py- Payment processing (initialize, verify)tests/test_tickets.py- Ticket operations (list, retrieve)tests/test_services.py- Service layer (QR generation, email, circuit breaker)
Current test coverage includes:
- β User registration and authentication
- β Event CRUD operations
- β Payment initialization and verification (mocked)
- β Ticket generation and retrieval
- β QR code generation
- β Email service (mocked)
- β Circuit breaker pattern
- β Error handling and validation
-
Push to GitHub:
git add . git commit -m "Ready for deployment" git push origin main
-
Create PostgreSQL Database on Render:
- Go to render.com Dashboard
- Click "New" β "PostgreSQL"
- Copy the "External Database URL"
-
Create Web Service:
- Click "New" β "Web Service"
- Connect your GitHub repository
- Render auto-detects settings from
Procfile
-
Set Environment Variables in Render dashboard:
DATABASE_URL=<from Render PostgreSQL> JWT_SECRET_KEY=<your-generated-secret> PAYSTACK_SECRET_KEY=<from Paystack> PAYSTACK_PUBLIC_KEY=<from Paystack> SENDGRID_API_KEY=<from SendGrid> SENDGRID_USER=<your-verified-email> FRONTEND_URL=<your-frontend-url> -
Deploy: Click "Manual Deploy" or wait for auto-deploy
# Login to Heroku
heroku login
# Create app
heroku create your-app-name
# Add PostgreSQL
heroku addons:create heroku-postgresql:mini
# Set environment variables
heroku config:set JWT_SECRET_KEY=your-secret-key
heroku config:set PAYSTACK_SECRET_KEY=your-paystack-key
heroku config:set SENDGRID_API_KEY=your-sendgrid-key
heroku config:set SENDGRID_USER=your-email@example.com
# Deploy
git push heroku main-
Install Railway CLI:
npm i -g @railway/cli
-
Deploy:
railway login railway init railway up
-
Add PostgreSQL and set environment variables in Railway dashboard
EventTicketing/
βββ app/
β βββ __init__.py
β βββ main.py # FastAPI application entry point
β βββ config.py # Configuration settings
β βββ database.py # Database connection & session
β βββ models.py # SQLAlchemy database models
β βββ schemas.py # Pydantic request/response schemas
β βββ security.py # Password hashing & JWT functions
β βββ auth.py # Authentication dependencies
β βββ middleware.py # Custom middleware (security, CORS, rate limiting)
β βββ cruds/ # Database CRUD operations
β β βββ users.py
β β βββ events.py
β β βββ payments.py
β β βββ tickets.py
β βββ routers/ # API route handlers
β β βββ auth.py
β β βββ events.py
β β βββ payments.py
β β βββ tickets.py
β βββ services/ # External service integrations
β βββ paystack.py # Paystack payment gateway (with circuit breaker)
β βββ qr_service.py # QR code generation
β βββ email_service.py # SendGrid email notifications
βββ tests/ # Test suite
β βββ conftest.py # Test configuration & fixtures
β βββ test_auth.py
β βββ test_events.py
β βββ test_payments.py
β βββ test_tickets.py
β βββ test_services.py
βββ qr_codes/ # Generated QR codes (created at runtime)
βββ .env.example # Environment variables template
βββ .gitignore
βββ requirements.txt # Python dependencies
βββ Procfile # Deployment configuration (Heroku/Render)
βββ runtime.txt # Python version specification
βββ README.md
- JWT Tokens: Stateless authentication with 30-minute expiration
- Bcrypt Hashing: Secure password storage with 12 rounds
- Token Validation: Middleware validates tokens on protected routes
- Bearer Token Scheme: HTTP Authorization header
- Rate Limiting: 100 requests per 60 seconds per IP
- CORS Protection: Configurable allowed origins
- Security Headers:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockStrict-Transport-Security: max-age=31536000
- Circuit Breaker: Prevents cascade failures (5 failures β 60s timeout)
- Reference Validation: Unique transaction references (cryptographically secure)
- Status Tracking: Payment state management (pending β success/failed)
- Idempotency: Payment verification is idempotent
- SQL Injection Protection: SQLAlchemy ORM parameterization
- Input Validation: Pydantic schema validation
- Environment Variables: Sensitive data not in code
- Password Requirements: Minimum 8 characters
Problem: DATABASE_URL environment variable not set
Solution:
# Verify .env file exists and contains DATABASE_URL
cat .env | grep DATABASE_URL
# Reload environment variables
source .env # Linux/Mac
# or restart terminalProblem: psycopg2 installation fails
Solution:
# Install binary version instead
pip install psycopg2-binaryProblem: Unauthorized: No API key provided
Solution:
- Verify
SENDGRID_API_KEYis set in.env - Check that the API key has "Mail Send" permissions
- Ensure there are no extra spaces in the API key
Problem: Sender email not verified
Solution:
- Go to SendGrid Dashboard β Settings β Sender Authentication
- Verify your sender email address
- Use the exact verified email as
SENDGRID_USER
Problem: Emails not arriving
Solution:
- Check spam folder
- Verify sender email is verified in SendGrid
- Check SendGrid dashboard for delivery status
- Ensure recipient email is valid
Problem: Payment initialization failed
Solution:
- Verify
PAYSTACK_SECRET_KEYin.env - Check if using test keys (
sk_test_...) for development - Ensure amount is positive and in correct currency
- Check Paystack dashboard for API status
Problem: Circuit breaker is OPEN
Solution:
- Wait 60 seconds for circuit breaker to reset
- Check Paystack API status at status.paystack.com
- Verify network connectivity
- Check API key validity
Problem: QR codes not generating
Solution:
# Ensure qr_codes directory exists and is writable
mkdir -p qr_codes
chmod 755 qr_codes
# Check Pillow installation
pip install --upgrade PillowProblem: Cannot write mode RGBA as PNG
Solution:
- Update Pillow:
pip install --upgrade Pillow - This is fixed in the current implementation (converts to RGB)
Problem: Address already in use
Solution:
# Find process using port 8000
# Linux/Mac:
lsof -i :8000
kill -9 <PID>
# Windows:
netstat -ano | findstr :8000
taskkill /PID <PID> /F
# Or use a different port:
uvicorn app.main:app --port 8001Problem: ModuleNotFoundError
Solution:
# Ensure virtual environment is activated
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate
# Reinstall dependencies
pip install -r requirements.txtProblem: Tests failing with database errors
Solution:
- Tests use in-memory SQLite, no external database needed
- Ensure pytest and pytest-asyncio are installed
- Run:
pip install pytest pytest-asyncio
For issues, questions, or contributions:
- Check existing issues in the repository
- Create a new issue with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, Python version)
- Relevant logs or error messages
This project is provided as-is for educational and commercial use.
- FastAPI for the excellent framework
- Paystack for payment processing
- SendGrid for email delivery
- All open-source contributors
Happy Coding! π