A robust, production-grade Fintech API built with Django Ninja, designed for payments, wallets, transactions, and compliance.
β οΈ Important Notice: The live server will be discontinued on February 14th, 2026 due to hosting costs. This is a demonstration project and not intended for long-term production use. You can run the project locally following the setup instructions below.
π Performance Note: The live URL may experience slower response times. This is not due to the application or implementation itself, but rather the hosting service configuration. The basic tier uses limited RAM and server resources. For optimal performance, please run the project locally using the setup instructions below.
- Production API: https://paycore-api.fly.dev
- API Documentation (Swagger): https://paycore-api.fly.dev
- Admin Panel: https://paycore-api.fly.dev/admin
- GitHub Repository: kayprogrammer/paycore-api-1
- Live Application: https://paycore-fe.netlify.app
- GitHub Repository: kayprogrammer/paycore-frontend
- Multi-currency Wallet System - NGN, KES, GHS, USD support with real-time exchange rates
- Transaction Management - Deposits, withdrawals, transfers, bill payments with fee calculation
- Card Issuing & Management - Virtual and physical cards via Flutterwave and Sudo Africa
- Loan & Investment Products - Personal loans, fixed deposits, mutual funds with automated calculations
- Payment Processing - Payment links, invoices, merchant API integration
- Google OAuth Authentication - Secure third-party authentication as primary login method
- Email/OTP Authentication - Alternative authentication with 6-digit OTP verification
- JWT Token Management - Access & refresh tokens with automatic rotation
- KYC Verification - Multi-tier verification (Tier 1-3) with Onfido integration
- Compliance & Risk Assessment - AML checks, sanctions screening, transaction monitoring
- Audit Logs - Complete activity tracking with IP logging and user agents
- PIN Authorization - Transaction-level security with wallet PINs
- Notifications - Email, SMS, push notifications via Firebase Cloud Messaging
- Support System - Ticketing with SLA tracking, FAQs, canned responses, escalation management
- Real-time Updates - WebSocket support via Django Channels
- Prometheus Metrics - System and application-level metrics
- Grafana Dashboards - Pre-configured dashboards for monitoring
- AlertManager - Automated alerting for critical issues
- Health Checks - System and Celery health endpoints
- Celery Task Queue - Background job processing with specialized queues
- Django 5.2 - High-level Python web framework
- Django Ninja - Fast, type-safe API framework with automatic OpenAPI documentation
- PostgreSQL 16 - Primary database with JSONB support
- Redis - Caching, rate limiting, and session storage
- Celery - Distributed task queue for background jobs
- RabbitMQ - Message broker with priority queues
- Django Celery Beat - Periodic task scheduler
- Flower - Real-time Celery monitoring
- Django Channels - WebSocket support for real-time features
- Firebase Cloud Messaging - Push notifications for mobile/web
- SMTP - Email notifications with templating
- Google OAuth 2.0 - Primary authentication provider
- JWT (JSON Web Tokens) - Stateless authentication with refresh tokens
- Onfido - KYC verification and identity checks
- bcrypt - Password hashing
- Paystack - Payment processing for NGN
- Flutterwave - Virtual card issuing (USD, NGN, GBP)
- Sudo Africa - Virtual card issuing (USD, NGN)
- Internal Provider - Mock provider for development/testing
- Cloudinary - Cloud-based image and file storage
- WhiteNoise - Static file serving for production
- Prometheus - Metrics collection and time-series database
- Grafana - Visualization and dashboards
- AlertManager - Alert routing and management
- Django Prometheus - Django metrics exporter
- Docker & Docker Compose - Containerization and orchestration
- Fly.io - Production hosting platform
- pytest - Testing framework with coverage
- Black & isort - Code formatting
- Python 3.11+ - Runtime environment
The easiest way to run PayCore API is using Docker Compose, which sets up the entire stack including PostgreSQL, Redis, RabbitMQ, Celery workers, and monitoring tools.
- Docker Desktop (or Docker Engine + Docker Compose)
- Git
git clone <repository-url>
cd paycore-api-1
# Copy environment template
cp .env.example .env
# Update .env with your settings (database, Redis, RabbitMQ, API keys)# Build Docker images
make docker-build
# Start all services
make docker-up
# View logs
make docker-logsThe API will be available at http://localhost:8000
# Access the web container shell
make docker-exec-web
# Inside the container:
python manage.py migrate
python manage.py init # Runs all seed commands
# Or run seed commands individually:
python manage.py upsert_countries
python manage.py seed_bill_providers
python manage.py seed_loan_products
python manage.py seed_investment_products
python manage.py seed_faqs
python manage.py seed_users # Creates test users with KYC & wallets
# Create superuser (optional)
python manage.py createsuperuser
# Exit container
exitThe Docker setup includes 10 services:
| Service | Port | Description |
|---|---|---|
| web | 8000 | Django API (uvicorn with 4 workers) |
| db | 5432 | PostgreSQL 16 database |
| redis | 6379 | Redis cache and session store |
| rabbitmq | 5672, 15672 | RabbitMQ message broker + management UI |
| celery-general | - | General background tasks worker |
| celery-emails | - | Email queue worker (4 concurrency) |
| celery-payments | - | Payment processing worker (2 concurrency) |
| celery-beat | - | Periodic task scheduler |
| flower | 5555 | Celery monitoring dashboard |
- API Documentation: http://localhost:8000
- RabbitMQ Management: http://localhost:15672 (guest/guest)
- Flower Monitoring: http://localhost:5555
After running make init or make su, you can use these test accounts:
| Password | PIN | Type | KYC Status | Wallet | |
|---|---|---|---|---|---|
| test@example.com | password123 | 1234 | Regular User | Tier 2 Verified | β¦100,000 NGN |
| test2@example.com | password123 | 1234 | Staff User | Tier 2 Verified | β¦100,000 NGN |
Both accounts have:
- Verified email
- Approved KYC (Tier 2)
- Active NGN wallet with β¦100,000 balance
- Default wallet PIN: 1234
All commands are available via the Makefile:
# Build and Run
make docker-build # Build images from scratch
make docker-up # Start all services
make docker-down # Stop all services
make docker-down-v # Stop and remove volumes
make docker-restart # Restart all services
make docker-rebuild # Full rebuild (down -v, build, up)
# Logs
make docker-logs # View all logs
make docker-logs-web # View Django logs only
make docker-logs-celery # View Celery worker logs
# Shell Access
make docker-exec-web # Access web container shell
make docker-exec-db # Access PostgreSQL shell
# Status
make docker-ps # View running containers
# Quick start with everything
make build # Build and start (quick rebuild)If you prefer to run services locally:
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install requirements
make reqYou can use Docker for infrastructure only:
make infrastructure-up # Starts RabbitMQ + Redis in DockerOr install PostgreSQL, Redis, and RabbitMQ locally.
# Run migrations
make mig
# Seed data
make initYou'll need multiple terminal windows:
# Terminal 1: Django API
make run
# Terminal 2: Celery worker (general tasks)
make celery
# Terminal 3: Celery worker (email queue)
make celery-emails
# Terminal 4: Celery worker (payment queue)
make celery-payments
# Terminal 5: Celery Beat (scheduler)
make celery-beat
# Terminal 6: Flower (monitoring)
make flowerKey environment variables in .env:
# Database
DB_NAME=paycore
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=db # Use 'localhost' for local development
DB_PORT=5432
# Redis
REDIS_HOST=redis # Use 'localhost' for local development
REDIS_PORT=6379
# RabbitMQ
RABBITMQ_HOST=rabbitmq # Use 'localhost' for local development
CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
# Django
SECRET_KEY=your-secret-key
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
SITE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# CORS (for frontend)
CORS_ALLOWED_ORIGINS=http://localhost:3000 http://127.0.0.1:3000
# Authentication
GOOGLE_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-oauth-client-secret
# JWT Token Settings
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_MINUTES=10080
TRUST_TOKEN_EXPIRE_DAYS=30
# Payment Providers
USE_INTERNAL_PROVIDER=False # Set to True for development without external APIs
PAYMENT_PROVIDERS_TEST_MODE=True
PAYSTACK_TEST_SECRET_KEY=sk_test_xxx
PAYSTACK_TEST_PUBLIC_KEY=pk_test_xxx
FLUTTERWAVE_TEST_SECRET_KEY=FLWSECK_TEST-xxx
SUDO_TEST_SECRET_KEY=your-sudo-test-key
# Card Providers
CARD_PROVIDERS_TEST_MODE=True
# KYC Provider
KYC_PROVIDER=onfido
ONFIDO_API_KEY=your-onfido-api-key
ONFIDO_WEBHOOK_TOKEN=your-onfido-webhook-token
# Email
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_SSL=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
DEFAULT_FROM_EMAIL=noreply@paycore.com
# Firebase Cloud Messaging (for push notifications)
FIREBASE_CREDENTIALS_JSON={"type":"service_account",...} # Production
# OR
FIREBASE_CREDENTIALS_PATH=firebase-credentials.json # Development
# Notifications
NOTIFICATION_RETENTION_DAYS=90For production with full monitoring stack:
# Start production services
make prod-up
# Start monitoring (Prometheus, Grafana, AlertManager)
make monitoring-up
# View all dashboards
make show-dashboardsProduction services include:
- Grafana: http://localhost:3000 (admin/paycore123)
- Prometheus: http://localhost:9090
- AlertManager: http://localhost:9093
- Node Exporter: Metrics for host system
- Redis Exporter: Metrics for Redis
PayCore API supports multiple authentication methods:
POST /api/v1/auth/google-oauth
Content-Type: application/json
{
"token": "google-id-token-from-frontend"
}
Response:
{
"status": "success",
"message": "Login successful",
"data": {
"user": { ... },
"access": "jwt-access-token",
"refresh": "jwt-refresh-token"
}
}# Step 1: Request OTP
POST /api/v1/auth/request-otp
{
"email": "user@example.com"
}
# Step 2: Verify OTP
POST /api/v1/auth/verify-otp
{
"email": "user@example.com",
"otp": "123456"
}POST /api/v1/auth/refresh
{
"refresh": "jwt-refresh-token"
}All authenticated requests require the JWT token in the Authorization header:
Authorization: Bearer <access-token>Once the server is running, visit:
- Swagger UI: http://localhost:8000/ (Interactive API documentation)
- OpenAPI Schema: http://localhost:8000/api/openapi.json (Machine-readable API spec)
- Admin Panel: http://localhost:8000/admin (Django admin interface)
| Category | Endpoints | Description |
|---|---|---|
| Authentication | /api/v1/auth/* |
Login, register, OTP, OAuth, token refresh |
| Profiles | /api/v1/profiles/* |
User profiles, avatar upload, device management |
| Wallets | /api/v1/wallets/* |
Multi-currency wallets, balance, currency exchange |
| Cards | /api/v1/cards/* |
Virtual/physical cards, transactions, controls |
| Transactions | /api/v1/transactions/* |
Deposits, withdrawals, transfers, history |
| Bills | /api/v1/bills/* |
Bill providers, packages, payments, beneficiaries |
| Payments | /api/v1/payments/* |
Payment links, invoices, merchant API |
| Loans | /api/v1/loans/* |
Loan products, applications, repayments |
| Investments | /api/v1/investments/* |
Investment products, portfolios, returns |
| Support | /api/v1/support/* |
Tickets, messages, FAQs |
| Notifications | /api/v1/notifications/* |
Push, email, SMS notifications |
| Compliance | /api/v1/compliance/* |
KYC verification, document upload |
PayCore has comprehensive unit tests focused on business logic (not API integration tests, which Swagger handles).
- Unit Tests: Business logic, calculations, validations (β What we test)
- Integration Tests: API endpoints, request/response (Swagger handles this)
# Run all unit tests
pytest -m unit -v
# Run tests by category
pytest -m auth # Authentication tests
pytest -m loan # Loan calculation & credit score tests
pytest -m wallet # Wallet validation tests
pytest -m payment # Payment processing tests
pytest -m compliance # KYC/compliance tests
pytest -m transaction # Transaction tests
# Run specific test file
pytest apps/accounts/test_authentication.py -v
pytest apps/loans/test_credit_score_service.py -v
pytest apps/loans/test_loan_calculations.py -v
# Run with coverage
pytest -m unit --cov=apps --cov-report=html
open htmlcov/index.htmlCompleted (2,100+ lines of unit tests):
- β Accounts: JWT tokens, OAuth, expiration, security (289 lines, 26 tests)
- β Wallets - Validation: Amount/PIN/email validation, split payments (440 lines, 60+ tests)
- β Wallets - Operations: Balance operations, spending limits, status checks (525 lines, 35+ tests)
- β Loans - Credit Score: FICO-like scoring 300-850 (397 lines, 30+ tests)
- β Loans - Calculations: Interest, fees, installments (464 lines, 40+ tests)
Test Focus Areas:
- Authentication: Token generation & validation, OAuth flow, security
- Wallet Validation: Amount boundaries ($0.01-$1M), PIN strength (4-6 digits), weak PIN rejection, currency codes (ISO 4217), email lists (2-20), split payment types (EQUAL/CUSTOM/PERCENTAGE)
- Balance Operations: Credit/debit/hold/release, spending limits (daily/monthly), balance consistency (
balance = available + pending) - Credit Scoring: 35% payment history + 30% utilization + 15% age + 20% history
- Loan Calculations: Interest formula
(Principal Γ Rate Γ Time) / 12, Processing feemax(amount Γ 2%, fixed_fee), Installments by frequency - Edge Cases: Decimal precision (8 places), boundaries (min/max), state transitions
@pytest.mark.unit
@pytest.mark.loan
class TestInterestCalculation:
"""Test simple interest formula."""
async def test_simple_interest_formula(self, loan_product):
"""Formula: Total Interest = Principal Γ Rate Γ Time / 12"""
result = await LoanManager.calculate_loan(
product=loan_product,
amount=Decimal("100000"),
tenure_months=12,
repayment_frequency=RepaymentFrequency.MONTHLY,
)
# For 100k at 15% for 12 months: (100000 * 0.15 * 12) / 12 = 15000
expected_interest = Decimal("15000")
assert result["total_interest"] == expected_interest- conftest.py: Shared fixtures (users, wallets, loans, mocks)
- pytest.ini: Test discovery, markers, coverage settings
- Fixtures:
verified_user,funded_wallet,loan_product,mock_email_send
# Create migrations
make mmig # All apps
make mmig app='accounts' # Specific app
# Run migrations
make mig # All apps
make mig app='accounts' # Specific app
# Django shell
make shell
# Create superuser
make suser
# Seed commands
make init # Run all seed commands
make su # Seed test users (with KYC & wallets)
make upc # Seed countries
make sbp # Seed bill providers
make slp # Seed loan products
make sip # Seed investment products
make sf # Seed FAQs
# Update requirements
make ureqpaycore-api-1/
βββ apps/
β βββ accounts/ # User management, KYC
β βββ bills/ # Bill payments
β βββ cards/ # Card issuing & management
β βββ common/ # Shared utilities, monitoring
β βββ compliance/ # Audit logs, risk assessment
β βββ investments/ # Investment products
β βββ loans/ # Loan products & repayments
β βββ notifications/ # Email, SMS, push notifications
β βββ support/ # Tickets, FAQs, chat
β βββ transactions/ # Deposits, withdrawals, transfers
β βββ wallets/ # Multi-currency wallets
βββ paycore/
β βββ settings.py # Django settings
β βββ urls.py # URL routing
β βββ asgi.py # ASGI application
βββ Dockerfile # Multi-stage Docker build
βββ docker-compose.yml # Development stack
βββ Makefile # Command shortcuts
βββ requirements.txt # Python dependencies
# Check system health
make health
# Check Celery health
make health-celery
# View Celery stats
make inspect-stats
# View active tasks
make inspect-activeHealth endpoints:
- System: http://localhost:8000/health/system/
- Celery: http://localhost:8000/health/celery/
- Metrics: http://localhost:8000/metrics/
# View logs for specific service
docker-compose logs -f web
# Restart a specific service
docker-compose restart web
# Rebuild a specific service
docker-compose up -d --build web
# Clear everything and start fresh
make docker-down-v
make docker-build
make docker-up# Access PostgreSQL shell
make docker-exec-db
# Inside PostgreSQL:
\dt # List tables
\d notifications_notification # Describe table
SELECT COUNT(*) FROM notifications_notification;# View Celery logs
make docker-logs-celery
# Purge all queued tasks
make purge-tasks
# Check worker stats
make inspect-stats
# Access Flower UI
open http://localhost:5555
