Back to Articles

24/7 Cloud Banking Suite

Posted: 2 years ago·Last Updated: 2 weeks ago
Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link

For full experience in demo - deposit, withdraw and check financial reports

A secure banking management system built with Django, featuring:

  • Custom user authentication
  • Account management
  • Transaction processing
  • Balance tracking
  • Role-based access control

Technology Stack:

  • Frontend: Django Templates
  • Backend: Django 4.2
  • Database: Supabase PostgreSQL
  • Deployment: Vercel
  • Auth: Django Allauth (Custom Implementation)
Image
graph TD
    A[Client] --> B[Vercel Edge Network]
    B --> C[Django Application]
    C --> D[Supabase Database]
    C --> E[Redis Cache]
    D --> F[(PostgreSQL Tables)]
    C --> G[Static Files]
    H[Admin User] --> C
accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    # Remove username field
    username = None
    email = models.EmailField(unique=True)
    
    # Additional fields
    phone_number = models.CharField(max_length=20)
    date_of_birth = models.DateField()
    is_verified = models.BooleanField(default=False)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['phone_number', 'date_of_birth']
    
    def __str__(self):
        return self.email
Image
sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Supabase
    
    User->>Frontend: Access Login Page
    Frontend->>Backend: GET /accounts/login/
    Backend-->>Frontend: Login Form
    User->>Frontend: Submit Credentials
    Frontend->>Backend: POST /accounts/login/
    Backend->>Supabase: Verify Credentials
    Supabase-->>Backend: User Data
    alt Valid Credentials
        Backend->>Backend: Create Session
        Backend-->>Frontend: Redirect to Dashboard
    else Invalid Credentials
        Backend-->>Frontend: Error Message
    end
  1. User Registration Endpoint:
accounts/views.py
# accounts/views.py
from django.views.generic import CreateView
from .forms import CustomUserCreationForm

class SignUpView(CreateView):
    form_class = CustomUserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'registration/signup.html'
  1. Registration Steps:
/accounts/signup/
POST /accounts/signup/
Content-Type: application/x-www-form-urlencoded

email=user@example.com&
password1=securePassword123&
password2=securePassword123&
phone_number=+254712345678&
date_of_birth=1990-01-01
  1. Verification Workflow:
    • Email verification sent via Celery task
    • Account activation required before login
    • JWT token generation for API access

Reset Flow:

accounts/urls.py
# accounts/urls.py
from django.urls import path
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('password_reset/', auth_views.PasswordResetView.as_view(
        template_name='registration/password_reset.html',
        email_template_name='registration/password_reset_email.html',
        subject_template_name='registration/password_reset_subject.txt'
    ), name='password_reset'),
    path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(
        template_name='registration/password_reset_done.html'
    ), name='password_reset_done'),
    path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(
        template_name='registration/password_reset_confirm.html'
    ), name='password_reset_confirm'),
    path('reset/done/', auth_views.PasswordResetCompleteView.as_view(
        template_name='registration/password_reset_complete.html'
    ), name='password_reset_complete'),
]
core/settings.py
# core/settings.py
DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get('DATABASE_URL'),
        engine='django.db.backends.postgresql',
        conn_max_age=600,
        conn_health_checks=True,
        ssl_require=True
    )
}
-- accounts_customuser
CREATE TABLE accounts_customuser (
    id SERIAL PRIMARY KEY,
    password VARCHAR(128) NOT NULL,
    last_login TIMESTAMP WITH TIME ZONE,
    is_superuser BOOLEAN NOT NULL,
    email VARCHAR(254) UNIQUE NOT NULL,
    phone_number VARCHAR(20) NOT NULL,
    date_of_birth DATE NOT NULL,
    is_verified BOOLEAN NOT NULL,
    ...
);

-- accounts_account
CREATE TABLE accounts_account (
    id SERIAL PRIMARY KEY,
    account_type VARCHAR(20) NOT NULL,
    account_number VARCHAR(20) UNIQUE NOT NULL,
    balance NUMERIC(12,2) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL,
    user_id INTEGER REFERENCES accounts_customuser(id)
);

Entity Relationship Diagram (ERD)

Image
classDiagram
    class accounts_customuser {
        +id: SERIAL (PK)
        +password: VARCHAR(128)
        +last_login: TIMESTAMPTZ
        +is_superuser: BOOLEAN
        +email: VARCHAR(254) UNIQUE
        +phone_number: VARCHAR(20)
        +date_of_birth: DATE
        +is_verified: BOOLEAN
        +created_at: TIMESTAMPTZ
        +updated_at: TIMESTAMPTZ
    }
    
    class accounts_account {
        +id: SERIAL (PK)
        +account_type: VARCHAR(20)
        +account_number: VARCHAR(20) UNIQUE
        +balance: NUMERIC(12,2)
        +created_at: TIMESTAMPTZ
        +user_id: INT (FK → accounts_customuser)
    }
    
    class accounts_transaction {
        +id: SERIAL (PK)
        +transaction_type: VARCHAR(20)
        +amount: NUMERIC(10,2)
        +timestamp: TIMESTAMPTZ
        +description: TEXT
        +account_id: INT (FK → accounts_account)
    }
    
    accounts_customuser "1" --> "many" accounts_account : has
    accounts_account "1" --> "many" accounts_transaction : contains
  • User → Account Relationship
Image
flowchart LR
    A[CustomUser] --> B[Account]
    style A fill:#f9f,stroke:#333
    style B fill:#ccf,stroke:#333
    B --> C[Transaction]
    style C fill:#cff,stroke:#333
  • Transaction Lifecycle
Image
journey
    title Transaction Flow
    section Initiation
      User Authentication: 5: User
      Account Verification: 5: System
    section Processing
      Balance Check: 4: System
      Transaction Execution: 3: System
    section Completion
      Update Balance: 5: System
      Record Transaction: 5: System

Account Creation SQL Equivalent

INSERT INTO accounts_account (
    account_type,
    account_number,
    balance,
    created_at,
    user_id
) VALUES (
    'SAVINGS',
    'ACC-123456789',
    0.00,
    NOW(),
    1
);

Transaction Recording Example

BEGIN;
UPDATE accounts_account 
SET balance = balance + 100.00
WHERE id = 1;

INSERT INTO accounts_transaction (
    transaction_type,
    amount,
    timestamp,
    account_id
) VALUES (
    'DEPOSIT',
    100.00,
    NOW(),
    1
);
COMMIT;
Image
accounts/api/views.py
# accounts/api/views.py
from rest_framework.viewsets import ModelViewSet
from .serializers import AccountSerializer

class AccountViewSet(ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return self.request.user.account_set.all()
vercel.json
// vercel.json
{
  "builds": [
    {
      "src": "core/wsgi.py",
      "use": "@vercel/python",
      "config": {
        "maxLambdaSize": "15mb",
        "runtime": "python3.9"
      }
    }
  ],
  "routes": [
    {
      "src": "/static/(.*)",
      "dest": "/static/$1"
    },
    {
      "src": "/media/(.*)",
      "dest": "/media/$1"
    },
    {
      "src": "/(.*)",
      "dest": "core/wsgi.py"
    }
  ]
}
  • Local Setup:
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
  • Vercel Deployment:
vercel env add DATABASE_URL
vercel env add SECRET_KEY
vercel env add DEBUG
vercel --prod
  • Data Encryption:
core/settings.py
# core/settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
  • Rate Limiting:
accounts/views.py
# accounts/views.py
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='5/m')
def login_view(request):
    ...
  • Security Headers:
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'csp.middleware.CSPMiddleware',
]

CSP_DEFAULT_SRC = ("'self'", "cdn.vercel.app")
accounts/tests/test_auth.py
# accounts/tests/test_auth.py
class AuthenticationTests(TestCase):
    def test_user_registration(self):
        response = self.client.post(reverse('signup'), {
            'email': 'test@example.com',
            'password1': 'complexpassword123',
            'password2': 'complexpassword123',
            'phone_number': '+254712345678',
            'date_of_birth': '2000-01-01'
        })
        self.assertEqual(response.status_code, 302)
        self.assertEqual(User.objects.count(), 1)

Common Issues:

  • Database Connection Errors:
# Test Supabase connection
psql "$DATABASE_URL"

# Check SSL configuration
openssl s_client -connect db.project-ref.supabase.co:6543
  • Static Files Not Loading:
# Collect static files
python manage.py collectstatic --noinput

# Verify vercel.json routes
  • Authentication Failures:
# Check email verification status
user = CustomUser.objects.get(email='user@example.com')
print(user.is_verified)

Check out the demo

Check out the source code

Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link

Ready to take your business to the next level? Let’s make it happen.

Recommended For You