Skip to content

behshadrhp/ShadPay

Repository files navigation

ShadPay - Easy To Pay

Table of Contents

  1. Project Overview
  2. Directory Structure
  3. Apps Description
  4. Models & Relationships
  5. Signals & Automation
  6. API Endpoints (Comprehensive)
  7. Serializers Summary
  8. Admin Panel & Management
  9. Templates & Email System
  10. Advanced Features & Implementation Details
  11. Project Configuration & Environment
  12. Type System & Constants
  13. Migrations & Database Schema
  14. Testing & Quality
  15. Deployment & Docker
  16. Common API Response Structure
  17. Future Improvements & Notes
  18. Troubleshooting & Common Issues
  19. File Organization & Best Practices
  20. Conclusion

Project Overview

  • Name: ShadPay - Django-based banking application
  • Purpose: Provide user account management, bank account handling, transactions with OTP verification, virtual cards, KYC/document handling, and admin tooling.
  • Stack: Python, Django, Django REST Framework, Celery, RabbitMQ, Redis, djmoney, SQLite (dev) / PostgreSQL (prod).

Directory Structure

(top-level — run tree -L 3 for full tree)

  • apps/: Django apps (account, cards, common)
  • core/: Django project entry (ASGI/WSGI, settings, celery, urls)
  • utils/: Helper functions (email, generate, path, validation, permissions, types)
  • templates/: Email and frontend templates
  • static/: Static assets
  • media/: Uploaded files (avatars, KYC docs)
  • requirements/: grouped requirements
  • Dockerfile, docker-compose.yml, manage.py etc.

Apps Description

  • Account App (apps/account): Core user and account management

    • Models: User, Settings, Profile, BankAccount, Currency, BankAccountType, Transaction, AccountRecharge, KYC, KYCDocument.
    • APIs: profile, settings, bank account, account recharge, KYC, transactions, authentication endpoints (OTP, activation token).
    • Signals: create related Profile and Settings on user creation; create BankAccount side-effects (VirtualCard, KYC); set balances and send emails for account recharge; KYC lifecycle updates; transaction OTP completion updates balances and sends notifications.
  • Cards App (apps/cards): Virtual card generation and card-to-card transfers

    • Models: VirtualCard (linked to BankAccount).
    • APIs: list virtual cards, card-to-card transfer endpoint.
  • Common App (apps/common): Shared models and utilities

    • Models: BaseModel (abstract base with UUID id, created_at, updated_at), ContentView.

Models & Relationships

Below are the main models, field summaries, relationships, constraints and indexes discovered in apps/*/models.

  • User (apps/account/models/user.py)

    • Fields: id (Char pk, uuid default), unique_id (Char), email (EmailField, unique), is_active, is_staff, is_verified, token_used, OTP fields: otp_code, otp_expire_time, otp_used, activation_token, date_joined.
    • Manager: UserManager with create_user and create_superuser.
    • Indexes: email index.
    • Methods: set_otp_code(), verify_otp_code(otp), set_activation_token(), verify_activation_token(token) — these send emails via utils.email.send_email.
  • Settings (apps/account/models/settings.py)

    • Fields: user (OneToOne -> User), role (choices), status (choices), is_notification_enabled.
    • Indexes: user.
    • Notes: created automatically on User creation by signal.
  • Profile (apps/account/models/profile.py)

    • Fields: user (OneToOne -> User), avatar (ImageField upload via utils.path.avatar.user_profile_avatar_upload_path), first_name, last_name, gender, birthday.
    • Validators: image size and extension via utils.validation.base_validator.
    • On save: deletes previous avatar file when replaced (transactional).
  • Currency (apps/account/models/bank_account.py)

    • Fields: code (unique), name, symbol, is_active, rate_to_usd (MoneyField).
  • BankAccountType

    • Fields: code (unique), name, description, is_active.
  • BankAccount

    • Fields: user (FK -> User), currency (FK -> Currency, SET_NULL), account_type (FK -> BankAccountType, SET_NULL), account_number (Char unique, default generator), account_balance (MoneyField), status, is_primary (bool), kyc_status, verified_at, verification_notes.
    • Indexes: status, account_type, (user, kyc_status), (user, account_number).
    • On save: when is_primary=True, updates other bank accounts for the user to is_primary=False (atomic transaction).
  • Transaction

    • Fields: sender (FK -> User), receiver (FK -> User), initiated_by (FK -> User), sender_account (FK -> BankAccount), receiver_account (FK -> BankAccount), initiated_by_card (FK -> VirtualCard, nullable), approved_by (FK -> User, nullable), approved_at, description, amount (MoneyField), status (choices), transaction_type (choices), reference_code (unique), OTP fields: otp_code, otp_expire_time, otp_used.
    • Indexes: many, including status, sender, receiver, accounts, reference_code, created_at, approved_by/at.
    • Validation: clean() enforces positive amount, restrictions by transaction type, and sufficient balance.
    • OTP: set_otp_code() generates OTP, emails sender via send_transaction_otp_code; verify_otp_code() checks expiry and usage.
  • AccountRecharge

    • Fields: user (FK -> User), bank_account (FK -> BankAccount), money_requested (MoneyField), status (choices), rejection_status, rejection_description.
    • Indexes: user, status, bank_account, rejection_status.
    • save() enforces the requestor is owner of the bank account; post-save signal updates bank account balance when status == APPROVED and triggers email notifications.
  • KYC and KYCDocument (apps/account/models/kyc_document.py)

    • KYC: user (FK), bank_account (FK), status, kyc_status, notes. Indexes and unique_together = ('user','bank_account').
    • KYCDocument: kyc (FK), document_file (FileField upload via utils.path.kyc_document.user_kyc_document_upload_path), document_type, status, notes. Unique per (kyc, document_type).
    • Signals: when KYC.kyc_status changes to VERIFIED/PENDING/REJECTED/NOT_SUBMITTED, corresponding BankAccount status and kyc_status are updated and timestamps/notes are saved.
  • VirtualCard (apps/cards/models/virtual_card.py)

    • Fields: bank_account (FK -> BankAccount), card_number (Char unique), cvv, expire_month, expire_year, status, daily_limit, monthly_limit (MoneyField).
    • On BankAccount creation a VirtualCard is created by a signal; on save, expiry month/year are generated if missing (via utils.generate.expire_time.get_random_expiry).

Signals & Automation

  • apps/account/signals/create_profile.py: post_save on User creates a Profile.
  • apps/account/signals/create_settings.py: post_save on User creates a Settings row.
  • apps/account/signals/create_bank_account.py: post_save on BankAccount creates a VirtualCard, a KYC entry, and sends bank-account-created email.
  • apps/account/signals/account_recharge.py: post_save on AccountRecharge will add money to BankAccount.account_balance when status==APPROVED and sends status email.
  • apps/account/signals/kyc_documents.py: post_save on KYC updates BankAccount status/kyc_status based on KYC.kyc_status and persists notes and verified_at when appropriate.
  • apps/account/signals/verify_otp_transaction_code.py: post_save on Transaction when status==COMPLETED adjusts sender/receiver balances and sends notification emails.

API Endpoints (Comprehensive)

All API endpoints are mounted under api/v1/ (see core/urls.py). Full endpoint and view details:

Authentication & Activation Endpoints

  • SendOTPCodeAPIView (apps/account/api/v1/views/send_otp_code.py)

    • Endpoint: POST /api/v1/account/auth/token/send-otp-code/
    • Purpose: Initiate OTP login/registration flow.
    • Request: { "email": "user@example.com" }
    • Response (existing user): { "error": false, "detail": "Success: OTP code sent to your email.", "code": 1001 }
    • Response (new user): { "error": false, "detail": "Success: OTP code sent for registration.", "code": 1002 }
    • Logic: If email exists, send OTP; if not, create user and send OTP.
  • VerifyOTPCodeAPIView (apps/account/api/v1/views/verify_otp_code.py)

    • Endpoint: POST /api/v1/account/auth/token/verify-otp-code/
    • Purpose: Verify OTP and return JWT tokens (access + refresh).
    • Request: { "email": "user@example.com", "otp_code": 123456 }
    • Response (success): { "error": false, "detail": "JWT Token generated.", "access": "<jwt>", "refresh": "<jwt>", "code": 1003 }
    • Response (invalid OTP): { "error": true, "detail": "Verify otp code is expired or invalid.", "code": 2003 }
    • Logic: Calls user.verify_otp_code() and user.set_activation_token(), then generates JWT tokens via RefreshToken.for_user(user).
  • SendActivationTokenAPIView (apps/account/api/v1/views/send_activation_token.py)

    • Endpoint: POST /api/v1/account/auth/token/send-activation-token/
    • Permission: IsAuthenticated
    • Purpose: Resend activation token if account not yet verified.
    • Request: Empty body (authentication via Bearer token)
    • Response (success): { "error": false, "detail": "Send Activation Token for your Mail Address.", "code": 1004 }
    • Response (already verified): { "error": true, "detail": "Your account has already been verified.", "code": 2010 }
    • Logic: Checks user.token_used and calls user.set_activation_token() if False.
  • VerifyActivationTokenAPIView (apps/account/api/v1/views/verify_activation_token.py)

    • Endpoint: POST /api/v1/account/auth/token/verify-activation-token/
    • Permission: IsAuthenticated
    • Purpose: Verify email address and finalize account activation.
    • Request: { "activation_token": "<token>" }
    • Response (success): { "error": false, "detail": "Activation Token is Successful.", "code": 1005 }
    • Response (invalid): { "error": true, "detail": "Your Token is Expired or Invalid.", "code": 2007 }
    • Logic: Calls user.verify_activation_token(token), setting is_verified = True.
  • LogoutView (apps/account/api/v1/views/user_logout.py)

    • Endpoint: GET|POST /api/v1/account/auth/token/logout/ (mapped in admin URLs)
    • Purpose: Logout authenticated user (session-based).
    • Logic: Calls Django's logout(request) and redirects to /.

Profile & Settings

  • ProfileViewSet (apps/account/api/v1/views/profile.py)

    • Endpoints: GET|PUT|PATCH /api/v1/account/profile/
    • Permission: IsAuthenticated
    • Serializer: ProfileSerializer — fields: id, avatar, first_name, last_name, gender, birthday, created_at
    • Behavior: User can only view/edit their own profile.
  • SettingsViewSet (apps/account/api/v1/views/settings.py)

    • Endpoints: GET|PUT|PATCH /api/v1/account/settings/
    • Permission: IsAuthenticated
    • Serializer: SettingsSerializer — fields: id, role, status, is_notification_enabled, created_at
    • Behavior: User can only view/edit their own settings. Role-based permissions (BRANCH_MANAGER, ACCOUNT_EXECUTIVE, TELLER, CUSTOMER) are checked elsewhere.

Bank Accounts & Currencies

  • CurrencyViewSet (apps/account/api/v1/views/bank_account.py)

    • Endpoints: GET /api/v1/account/bank-account-currency/
    • Serializer: CurrencySerializer — fields: id, code, name, symbol, rate_to_usd
    • Behavior: List only active currencies.
  • BankAccountTypeViewSet

    • Endpoints: GET /api/v1/account/bank-account-type/
    • Serializer: BankAccountTypeSerializer — fields: id, code, name, description
    • Behavior: List only active account types.
  • BankAccountViewSet

    • Endpoints: GET|POST /api/v1/account/bank-account/
    • Permission: IsAuthenticated
    • Serializer (GET): GetBankAccountSerializer — includes related currency and account_type objects
    • Serializer (POST): PostBankAccountSerializer — user is auto-attached in perform_create()
    • Behavior: User can only view/create their own bank accounts. On create, a signal triggers VirtualCard and KYC creation and sends confirmation email.

Account Recharge

  • AccountRechargeViewSet (apps/account/api/v1/views/account_recharge.py)
    • Endpoints: GET|POST /api/v1/account/account-recharge/
    • Permission: IsAuthenticated
    • Serializer: AccountRechargeSerializer — fields: id, bank_account, money_requested, status, rejection_status, rejection_description, created_at, updated_at (some read-only)
    • Behavior: perform_create() validates that user owns the bank account. On approval (via admin), a signal updates bank_account.account_balance and sends status email.

KYC Documents

  • KYCViewSet (apps/account/api/v1/views/kyc_document.py)

    • Endpoints: GET /api/v1/account/kyc/
    • Permission: IsAuthenticated
    • Serializer: KYCSerializer — includes nested kyc_document array
    • Behavior: User can only view their own KYC entries. Read-only (no POST allowed).
  • KYCDocumentViewSet

    • Endpoints: GET|POST /api/v1/account/kyc-document/
    • Permission: IsAuthenticated
    • Serializer: KYCDocumentSerializer — fields: id, kyc, document_file, document_type, status, notes (status & notes read-only)
    • Behavior: User can upload KYC documents; admin reviews and updates status via admin panel, triggering KYC lifecycle signal.

Transactions

  • TransactionViewSet (apps/account/api/v1/views/transaction_request.py)

    • Endpoints: GET|POST /api/v1/account/transaction/
    • Permission: IsAuthenticated
    • Serializer (GET): GetTransactionSerializer — detailed transaction info
    • Serializer (POST): PostTransactionSerializer — fields: sender_account, receiver_account, amount, description, transaction_type
    • Behavior: User can view transactions where they are sender, receiver, or initiator. perform_create() sets sender/receiver/initiator and calls transaction.set_otp_code() which sends OTP email.
  • VerifyOTPTransactionCodeAPIView (apps/account/api/v1/views/transaction_verify.py)

    • Endpoint: POST /api/v1/account/transaction/verify-otp-code/
    • Permission: IsAuthenticated
    • Purpose: Verify transaction OTP and finalize transfer.
    • Request: { "transaction_id": "<uuid>", "otp_code": 123456 }
    • Response (success): { "error": false, "detail": "Transaction verified successfully.", "code": 1001 }
    • Logic: Calls transaction.verify_otp_code(), updates status to COMPLETED, sets approved_by and approved_at, and triggers signal to move funds and send notifications.
  • ResendOTPTransactionCodeAPIView

    • Endpoint: POST /api/v1/account/transaction/resend-otp-code/
    • Permission: IsAuthenticated
    • Purpose: Resend OTP for a pending transaction.
    • Request: { "transaction_id": "<uuid>" }
    • Logic: Fetches transaction, calls transaction.set_otp_code(), and sends new OTP email.

Cards & Card-to-Card Transfer

  • VirtualCardViewSet (apps/cards/api/v1/views/virtual_card.py)

    • Endpoints: GET /api/v1/cards/virtual-cards/
    • Permission: IsAuthenticated
    • Serializer: VirtualCardSerializer — includes nested bank account, card_number, cvv, expiry, status, daily/monthly limits
    • Behavior: User can only view virtual cards linked to their bank accounts.
  • CardToCardTransferAPIView (apps/cards/api/v1/views/card_to_card_transfer.py)

    • Endpoint: POST /api/v1/cards/cart-to-card-transfer (note: typo "cart" instead of "card")
    • Permission: IsAuthenticated
    • Purpose: Transfer money between virtual cards with OTP verification.
    • Request: { "recipient_card": "<card_number>", "sender_card": "<optional>", "amount": { "amount": 100, "currency": "USD" } }
    • Response (success): { "success": true, "transaction_id": "<uuid>", "message": "OTP sent to your email..." }
    • Logic:
      • Validates recipient card exists.
      • If sender_card provided, uses that card's bank account; otherwise uses user's primary bank account.
      • Validates currency match and sufficient balance.
      • Creates Transaction object, calls set_otp_code(), and returns transaction ID.
    • Notes: Transaction must still be verified via transaction/verify-otp-code/ endpoint.

Serializers Summary

All serializers are in apps/*/api/v1/serializers/:

  • Account Serializers:

    • ProfileSerializer: user profile data
    • SettingsSerializer: user role, status, notifications
    • CurrencySerializer: currency codes and exchange rates
    • BankAccountTypeSerializer: account type metadata
    • GetBankAccountSerializer / PostBankAccountSerializer: bank account CRUD
    • TransactionSerializer (GET & POST variants): transaction details
    • KYCSerializer / KYCDocumentSerializer: KYC document submission
    • AccountRechargeSerializer: account recharge requests
  • Card Serializers:

    • VirtualCardSerializer: card details with nested bank account

All serializers use DRF's ModelSerializer and include appropriate read_only_fields and fields declarations.

Admin Panel & Management

ShadPay uses Django's admin with the unfold package (modern admin UI) and admin_honeypot (security). Real admin is behind a variable URL prefix; fake honeypot at /admin/.

Admin Modules

  • apps/account/admin/user.py: Manage users, view OTP codes and activation tokens, mark verified/unverified.
  • apps/account/admin/profile.py: View/edit user profiles, avatars, personal data.
  • apps/account/admin/settings.py: Manage user roles, account status, notification settings.
  • apps/account/admin/bank_account.py: View bank accounts, update KYC status, manage account types and currencies.
  • apps/account/admin/account_recharge.py: Review recharge requests, approve/reject with reasons (triggers signal to update balance).
  • apps/account/admin/kyc_document.py: Review and approve KYC documents; updates trigger lifecycle signal.
  • apps/account/admin/key_document.py: (Likely for admin document management)
  • apps/cards/admin/virtual_card.py: View virtual cards, update status, manage limits.

Honeypot Admin Monitoring

  • apps/account/admin/honeypot.py registers LoginAttempt model with enhanced display:
    • Displays: username (color-coded), IP address (clickable), timestamp (human-readable), threat level, browser type, attempted path.
    • Threat Analysis: Color-coded risk levels (🔴 HIGH, 🟠 MEDIUM, 🟢 LOW) based on:
      • Common attack usernames (admin, root, etc.)
      • Suspicious user agents (bots, crawlers)
      • Repeated attempts from same IP.
    • Actions: Mark reviewed, block IPs, export to CSV.
    • Security Analysis: Detailed breakdown of detected threats, including IP geolocation stub (for future implementation).
    • Read-only: No add/delete permissions; view-only for security auditing.

Admin Security

  • Real admin behind ADMIN_URL_PREFIX env var (default: "real-admin" in dev).
  • Fake /admin/ honeypot to trap attackers.
  • django-axes brute-force protection: locks after 5 failures (configurable), 1-hour cooloff, lockout template.
  • Session timeout: 1 week expiry on inactivity.

Templates & Email System

Email Templates

All email templates are in templates/email/:

  • send_otp_code.html: OTP code with 2-minute expiry (sent by User.set_otp_code()).
  • send_activation_link.html: Account activation link with token (sent by User.set_activation_token()).
  • bank_account_created.html: Confirms new bank account with account number and initial balance (sent by create_bank_account signal).
  • account_recharge_status.html: Notifies user of recharge approval/rejection (sent by account_recharge_send_email signal).
  • transaction_otp_email.html: OTP for transaction verification (sent by Transaction.set_otp_code()).
  • transaction_notification_email.html: Confirms transaction completion, showing debit/credit (sent by verify_otp_transaction_code signal).

Email Sending

All email functions in utils/email/send_email.py:

  • send_otp_code(email, otp_code, expire_time): Sends OTP for login/registration.
  • send_activation_email(email, activation_token): Sends activation link.
  • send_bank_account_information(instance): Sends bank account creation confirmation.
  • send_account_recharge_status_email(instance): Sends recharge request status.
  • send_transaction_otp_code(...): Sends transaction OTP with details.
  • send_transaction_notification(...): Sends transaction completion notification.

Note: Email sending is synchronous (blocks request). For production, consider migrating to Celery tasks (see "Future Improvements").

Static & Frontend

  • static/img/: Logo, favicon, UI assets.
  • static/js/main.js: Frontend utilities.
  • static/style/: CSS for admin and public site.
  • templates/account/locked.html: Shown when user is locked out by django-axes.
  • templates/admin_honeypot/login.html: Fake admin login page (part of admin_honeypot package).

Advanced Features & Implementation Details

Transaction Lifecycle

  1. Request: POST /api/v1/account/transaction/ with sender/receiver accounts and amount.
  2. Validation: Transaction.clean() validates amount > 0, accounts exist, currency match, sufficient balance.
  3. OTP Generation: perform_create() calls transaction.set_otp_code():
    • Generates 6-digit OTP.
    • Sets expiry to now + 2 minutes.
    • Emails OTP to sender via send_transaction_otp_code().
  4. Verification: POST /api/v1/account/transaction/verify-otp-code/ with transaction_id and OTP:
    • Verifies OTP (value + expiry).
    • Sets transaction status to COMPLETED.
    • Records approved_by (current user) and approved_at (timestamp).
  5. Balance Update: Signal verify_otp_transaction_code triggers:
    • Debits sender_account.account_balance by amount.
    • Credits receiver_account.account_balance by amount.
    • Sends notifications to both parties.

KYC Lifecycle

  1. Account Creation: When BankAccount is created, signal creates KYC (status SUSPENDED, kyc_status NOT_SUBMITTED).
  2. Document Upload: User uploads KYCDocument(s) via POST /api/v1/account/kyc-document/.
  3. Admin Review: Admin updates KYC.kyc_status to PENDING/VERIFIED/REJECTED via admin panel.
  4. Signal Update: kyc_documents signal updates:
    • If VERIFIED: BankAccount.status = ACTIVE, kyc_status = VERIFIED, sets verified_at, verification_notes.
    • If PENDING: BankAccount.status = SUSPENDED, kyc_status = PENDING.
    • If REJECTED: BankAccount.status = INACTIVE, kyc_status = REJECTED, sets notes.
    • If NOT_SUBMITTED: BankAccount.status = SUSPENDED.

OTP System

  • Generation: utils/generate/unique_code.py::generate_otp_code() → 6-digit random int (100000-999999).
  • Storage: Stored on User or Transaction model.
  • Expiry: 2 minutes from generation.
  • Verification: User.verify_otp_code(otp) and Transaction.verify_otp_code(otp) both check time + value match.
  • Consumption: Set otp_used = True once verified (prevents replay).

File Upload System

  • Avatar: utils/path/avatar.py::user_profile_avatar_upload_path() → stored in media/profile/avatar/ with NanoID prefix.
  • KYC Documents: utils/path/kyc_document.py::user_kyc_document_upload_path() → stored in media/kyc/document/ with NanoID prefix.
  • Validators (in utils/validation/base_validator.py):
    • validate_file_size_volume(): max ~5MB (5000 KB).
    • validate_image_file_extension(): .jpg, .jpeg, .png, .webp, .heic, .heif, .heifs.
    • validate_kyc_file_extension(): .jpg, .jpeg, .png, .pdf, .bmp, .tiff.

Role-Based Access Control

Roles defined in utils/type/account/settings_type.py:

  • BRANCH_MANAGER: High-level permissions (not yet fully implemented in views).
  • ACCOUNT_EXECUTIVE: Account operations.
  • TELLER: Basic transactions.
  • CUSTOMER: Self-service only.

Permission classes in utils/permissions/account.py: IsBranchManagerPermission, IsAccountExecutivePermission, IsTellerPermission, IsCustomerPermission.

Currency & Exchange Rates

  • Currency model stores code (USD, EUR, etc.), name, symbol, and rate_to_usd (MoneyField).
  • All transactions and account balances use djmoney.MoneyField (amount + currency).
  • When creating transactions, currency mismatch between sender/receiver accounts triggers validation error.

Project Configuration & Environment

Settings Files

  • core/settings/common.py: Shared config (apps, middleware, logging, email, caching, Celery, JWT, admin).
  • core/settings/development.py: Debug = True, SQLite database, Django Debug Toolbar.
  • core/settings/production.py: Debug = False, PostgreSQL database, environment variables (SECRET_KEY, ALLOWED_HOSTS, DB credentials).

Key Environment Variables

# Django
SECRET_KEY=<strong-secret>
DJANGO_SETTINGS_MODULE=core.settings.production

# JWT
JWT_SECRET_KEY=<strong-secret>

# Email
EMAIL_HOST=<smtp-host>
EMAIL_PORT=<port>
EMAIL_HOST_USER=<username>
EMAIL_HOST_PASSWORD=<password>
DEFAULT_FROM_EMAIL=<sender-email>

# Database (production)
POSTGRES_DB=shadpay_db
POSTGRES_USER=shadpay_user
POSTGRES_PASSWORD=<password>
POSTGRES_HOST=db
POSTGRES_PORT=5432

# Cache & Broker
REDIS_URL=redis://redis:6379/0
RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672//

# Security
ADMIN_URL_PREFIX=<secret-admin-path>
ALLOWED_HOSTS=example.com,www.example.com

# API
SITE_URL=https://example.com
SITE_NAME=ShadPay

Logging

Configured in common.py with file-based handlers:

  • tmp/logs/debug.log: DEBUG level
  • tmp/logs/info.log: INFO level
  • tmp/logs/warning.log: WARNING level
  • tmp/logs/error.log: ERROR level
  • tmp/logs/critical.log: CRITICAL level

Caching

Redis cache backend configured at REDIS_URL for session and query caching.

Celery

  • Broker: RabbitMQ (RABBITMQ_URL)
  • Result Backend: Redis (REDIS_URL)
  • Current Tasks: None defined (placeholder beat_schedule)
  • Recommendation: Move email sending to Celery tasks for async processing.

Type System & Constants

Type choices/enums in utils/type/:

  • Account Types (utils/type/account/):

    • RoleType: BRANCH_MANAGER, ACCOUNT_EXECUTIVE, TELLER, CUSTOMER
    • StatusType: ACTIVE, SUSPENDED, INACTIVE
    • GenderType: (defined in models)
    • AccountStatusType: ACTIVE, SUSPENDED, INACTIVE
    • KYCStatusType: NOT_SUBMITTED, PENDING, VERIFIED, REJECTED
    • TransactionType: TRANSFER, DEPOSIT, WITHDRAWAL
    • TransactionStatusType: PENDING, COMPLETED, FAILED, CANCELLED
    • AccountRechargeStatusType: PENDING, APPROVED, REJECTED
    • AccountRechargeRejectionReasonType: (various reasons)
  • Card Types (utils/type/cards/):

    • VirtualCardStatusType: ACTIVE, INACTIVE, BLOCKED, EXPIRED

Migrations & Database Schema

Key migrations in apps/account/migrations/:

  • 0001_initial.py: Initial schema (User, Profile, Settings, BankAccount, Transaction, AccountRecharge, KYC).
  • 0002_alter_bankaccount_account_balance.py: MoneyField configuration.
  • 0003_transaction_otp_code_...: Added OTP fields to Transaction.
  • 0004-0006_alter_transaction_initiated_by_...: Relation tweaks.
  • 0007_transaction_initiated_by_card.py: Virtual card relation.

Migration strategy: Always run python manage.py migrate when deploying.

Testing & Quality

Tests are stubbed in apps/*/tests/__init__.py. Recommend adding:

  • Unit tests for OTP generation and verification.
  • Integration tests for transaction lifecycle.
  • API endpoint tests for all viewsets.
  • Permission tests for role-based access.

Run tests:

python manage.py test

Deployment & Docker

  • Dockerfile: Multi-stage build with Python dependencies, static file collection, migrations.
  • docker-compose.yml: Services for web (Django), db (PostgreSQL), redis, rabbitmq.
  • nginx.conf: Reverse proxy configuration in docker/nginx/.

Example docker-compose run:

docker-compose up -d

Common API Response Structure

All API responses follow a consistent JSON structure:

{
  "error": false,
  "detail": "Success message",
  "code": 1001,
  "data": {}
}

or on error:

{
  "error": true,
  "detail": "Error message",
  "code": 2001,
  "detail-error": "Technical error details"
}

Error Codes

Code Context Meaning
1001-1005 Authentication Success codes
2001-2025 Authentication/Validation Client errors
7001 Server Server errors
4001-4004 Cards Card transfer validation

Future Improvements & Notes

  1. Async Email: Migrate utils.email.send_email functions to Celery tasks for reliability and performance.
  2. Pagination: Most endpoints return paginated lists (default page size = 10).
  3. Rate Limiting: Currently throttled to 5 requests/day for anonymous and 5 for authenticated (adjust in settings if needed).
  4. IP Geolocation: Honeypot admin has stub for IP geolocation (can integrate with ipapi.co or ipinfo.io).
  5. Transaction Limits: Virtual cards have daily/monthly limits that can be enforced in views.
  6. Currency Conversion: Exchange rates stored but not yet used in conversion logic.
  7. Audit Trail: Consider adding admin action logs for sensitive operations.
  8. 2FA: Second factor authentication not yet implemented (OTP is single factor).

Troubleshooting & Common Issues

  • OTP not arriving: Check EMAIL_* env vars and SMTP settings in common.py.
  • Database migration errors: Run python manage.py makemigrations and python manage.py migrate.
  • Permission denied on endpoint: Verify user role in admin panel and check permission class decorators.
  • Card not found errors: Ensure virtual card exists and user owns the bank account.
  • Transaction fails with "Insufficient funds": Check sender_account.account_balance.

File Organization & Best Practices

apps/account/
  models/        # Data models (User, BankAccount, Transaction, etc.)
  admin/         # Admin panel registrations
  api/v1/
    views/       # API viewsets and APIView classes
    serializers/ # DRF serializers
    urls.py      # URL routing
  signals/       # Django signals (auto-triggers)
  tests/         # Unit tests
  migrations/    # Database schema

utils/
  email/         # Email sending helpers
  generate/      # ID and token generators
  path/          # File upload path helpers
  validation/    # File and data validators
  permissions/   # DRF permission classes
  type/          # Type choices and enums
  docs/          # API documentation (Swagger setup)

core/
  settings/      # Django settings (common, dev, prod)
  management/    # Management commands
  celery.py      # Celery configuration
  urls.py        # Root URL routing
  wsgi.py / asgi.py  # Application entry points

templates/       # HTML templates (email, admin)
static/          # CSS, JS, images
media/           # User uploads (avatars, KYC docs)

Conclusion

ShadPay is a comprehensive Django banking application with:

  • ✅ Custom user authentication via OTP and JWT
  • ✅ Multi-account bank system with KYC verification
  • ✅ Secure transaction processing with OTP confirmation
  • ✅ Virtual card generation and management
  • ✅ Admin honeypot and brute-force protection
  • ✅ Role-based access control
  • ✅ Email notifications for all critical events
  • ✅ Docker containerization for deployment

For new developers:

  1. Read this doc and model diagrams.
  2. Run docker-compose up to start services.
  3. Create a superuser: python manage.py createsuperuser.
  4. Visit http://localhost:8000/<ADMIN_URL_PREFIX>/ to access admin.
  5. Use Swagger/Scalar at http://localhost:8000/api/v1/docs/ for API exploration.

Utils & Helpers

  • utils/generate/unique_code.py — random generators:

    • unique_small_string() and unique_big_string() — short human-readable IDs.
    • generate_account_number() — returns ShadPay...-prefixed account numbers.
    • generate_otp_code() — 6-digit OTP.
    • generate_activation_token() — url-safe token via secrets.token_urlsafe(32).
    • generate_virtual_card_number() and generate_virtual_card_cvv2() — card identifiers.
  • utils/generate/expire_time.pyget_random_expiry() used by VirtualCard to pick expire month/year.

  • utils/email/send_email.py — centralized email helpers that render templates and send OTP, activation, bank-account, recharge, and transaction emails.

  • utils/path/* — upload path helpers:

    • avatar.user_profile_avatar_upload_path — names avatars with NanoID prefix.
    • kyc_document.user_kyc_document_upload_path — names KYC uploads with NanoID prefix.
  • utils/validation/base_validator.py — file validators used by Profile.avatar and KYCDocument.document_file:

    • validate_file_size_volume() enforces < ~5MB limit.
    • validate_image_file_extension() and validate_kyc_file_extension() enforce allowed extensions.
  • utils/permissions/account.py — role-based DRF permissions: IsBranchManagerPermission, IsAccountExecutivePermission, IsTellerPermission, IsCustomerPermission (based on user.user_settings.role).

Settings & Configuration

  • Main settings entry: core/settings/common.py with environment-aware overrides in development.py and production.py.
  • Important configurations:
    • AUTH_USER_MODEL = "account.User" (custom user).
    • REST_FRAMEWORK uses JWTAuthentication from rest_framework_simplejwt (configured in SIMPLE_JWT with ACCESS_TOKEN_LIFETIME = 7 days, algorithm HS512).
    • EMAIL_BACKEND and SMTP env vars used in utils.email.send_email.
    • AXES and admin_honeypot packages for admin security; AXES lockout and template configured (locked.html).
    • CACHES configured to use Redis (REDIS_URL env var).
    • Celery is configured (core/celery.py) to use RabbitMQ (RABBITMQ_URL) and Redis result backend.

Security, Validation & Permission Layers

  • Authentication: JWT via rest_framework_simplejwt (tokens returned by OTP verify view). SIMPLE_JWT settings control token expiry and signing key via env JWT_SECRET_KEY.
  • Brute-force protection: django-axes configured with AXES_FAILURE_LIMIT and AXES_COOLOFF_TIME and LOCKOUT_TEMPLATE.
  • Admin protection: admin_honeypot is installed and the real admin is behind ADMIN_URL_PREFIX (changeable); fake admin/ honeypot included.
  • OTP & Activation
    • OTP generation: generate_otp_code() creates a 6-digit int.
    • User OTP lifecycle: User.set_otp_code() sets otp_code, otp_expire_time = now + 2 minutes, otp_used=False, and sends OTP via email (commented in user.set_otp_code() but used elsewhere).
    • Transaction OTP: Transaction.set_otp_code() does similarly and emails via send_transaction_otp_code().
    • Verification endpoints check both OTP value and expiry and set otp_used=True when consumed.
  • Role-based permissions: utils.permissions.account provides DRF permission classes referencing user.user_settings.role (enum in utils/type/*).
  • File upload validation: utils.validation.base_validator ensures file size and allowed extensions.

Diagrams ERD

Examples & Code Snippets

  • Creating a user and sending OTP (via API):
curl -X POST "http://localhost:8000/api/v1/account/auth/token/send-otp-code/" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com"}'
  • Creating a transaction (authenticated request):
POST /api/v1/account/transaction/ HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "sender_account": "<bank_account_id>",
  "receiver_account": "<receiver_bank_account_id>",
  "amount": "100.00",
  "transaction_type": "TRANSFER",
  "description": "Payment for invoice #123"
}

Server behavior:

  • perform_create() in TransactionViewSet sets sender, initiated_by, receiver and calls set_otp_code() which generates OTP and emails the sender. Use POST /transaction/verify-otp-code/ to confirm.

  • Verifying OTP for a transaction (example payload):

POST /api/v1/account/transaction/verify-otp-code/
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "transaction_id": "<transaction_uuid>",
  "otp_code": 123456
}

Migrations & Schema Notes

  • Migrations exist under apps/*/migrations/ with incremental changes; notable model evolutions include transaction OTP fields and relation changes (see migration filenames in apps/account/migrations/).

Celery & Management Commands

  • core/celery.py sets up Celery, using RABBITMQ_URL and REDIS_URL from env. No scheduled tasks defined in repo (placeholder beat_schedule).
  • Management command: core/management/commands/startapp.py (utility for scaffolding new apps).

Notes / Important Details

  • Email sending is synchronous in utils.email.send_email — consider moving to Celery tasks for reliability and performance in production.
  • OTP is short-lived (2 minutes) — ensure client UX supports rapid delivery and retry/resend endpoints exist for transactions.
  • Sensitive defaults are present in development settings (SQLite, DEBUG=True). Ensure production environment variables are set and SECRET_KEY and JWT_SECRET_KEY are strong and not checked into VCS.
  • File upload size limits and extensions are validated — double-check storage backend (S3 or similar) in production if needed.

About

A secure and scalable Banking API built with Django REST Framework, Docker, PostgreSQL, Redis, and RabbitMQ. Implements real-time transactions, multi-currency support, KYC verification, fraud detection, PDF statement generation, and two-factor authentication — following modern FinTech best practices and production-grade deployment strategies.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors