EUNICE M. Get in Touch
Back to Projects

Backend Architecture · Multi-tenant SaaS

WhatsApp Commerce Platform

A multi-tenant SaaS backend that lets small businesses run a complete storefront through WhatsApp. Fifteen Django apps covering catalogue, orders, deliveries, payments and analytics, with the WhatsApp Business API and M-Pesa Daraja wired in and every tenant's data isolated at the model layer.

Django 4.2 Django REST Framework PostgreSQL SimpleJWT Celery Redis M-Pesa Daraja Twilio drf-spectacular

Demo status: this is a backend service and isn't publicly hosted — it needs PostgreSQL, Redis and live API credentials for WhatsApp and M-Pesa. The repository is complete and the OpenAPI schema documents every endpoint. Local setup steps are at the bottom of this page.

Scope at a glance

This is the largest backend in my portfolio, and the one where the architectural decisions matter most.

15 Django apps Each a bounded domain
Multi Tenancy Isolation at the model layer
2 External APIs WhatsApp Business + M-Pesa
JWT Auth SimpleJWT access & refresh
Celery Async Redis-backed task queue
OpenAPI Docs Generated, not hand-written

The Problem

A huge share of small commerce in Kenya already happens in WhatsApp threads — a customer messages, the owner quotes a price, an M-Pesa code arrives, and the order lives in someone's memory. It works until volume grows, and then orders get lost, stock is wrong, and nobody can tell which conversation is still waiting on a reply.

The platform keeps the interface customers already like and puts real infrastructure behind it: a catalogue, an order lifecycle, payment reconciliation, delivery tracking and analytics, with each business's data kept strictly separate from every other one's.

My Role

Sole developer. I designed the multi-tenant data model, built all fifteen apps and their REST APIs, integrated the WhatsApp Business and M-Pesa Daraja APIs, set up the Celery task pipeline, and implemented encrypted storage for per-tenant credentials.

  • Multi-tenant architecture
  • REST API & serializers
  • Payment & messaging integrations
  • Async task pipeline
  • Credential encryption

The domains

Fifteen apps, each owning one part of the business rather than one technical layer.

businesses

Tenant records and per-business WhatsApp automation settings.

accounts

Users, JWT authentication and role assignment.

whatsapp_integration

Conversations, messages, delivery and read receipts, reply templates.

products

Catalogue, variants and stock levels.

orders

Order lifecycle from cart through fulfilment.

payments

M-Pesa Daraja integration and payment reconciliation.

deliveries

Dispatch, tracking and delivery fees.

customers

Customer records linked to WhatsApp identities.

Storefront

Public-facing catalogue configuration and settings.

notifications

Outbound SMS and message dispatch via Twilio.

analytics

Sales, conversation and performance reporting.

core

Shared base models, including the tenant-aware base class.

Engineering decisions

Tenancy in the base model

Every tenant-scoped model inherits TenantAwareModel. Isolation is a property of the schema rather than something each query has to remember to filter, which is the difference between a safe default and a leak waiting to happen.

Encrypt credentials at the field

Each business stores its own WhatsApp and M-Pesa credentials. Those are encrypted at the field level with cryptography, so a database dump doesn't hand over every tenant's payment access.

Celery for anything outbound

Sending messages, calling Daraja and dispatching SMS all happen in Redis-backed Celery tasks. A slow third-party API delays a job, not the customer's HTTP request.

Generated API docs

drf-spectacular produces the OpenAPI 3 schema from the serializers themselves. I moved off drf-yasg deliberately — it can't import on Python 3.12 and is still stuck on Swagger 2.0.

Message state, not just text

Messages record sent, delivered and read timestamps and flag AI-generated replies. Support questions like “did the customer ever see it?” are answerable from the data.

PostgreSQL from the start

JSON metadata fields, tenant-scoped indexes and reporting queries all lean on Postgres features. Starting on SQLite would have meant a migration later on the least convenient day.

Run it locally

Needs PostgreSQL and Redis running locally, plus API credentials for the integrations.

Backend — Django REST

git clone https://github.com/Eunice-ctrlz/Whatsapp-ecommerce.git
cd Whatsapp-ecommerce
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env   # add DB, Redis and API credentials
python manage.py migrate
python manage.py runserver

API on localhost:8000, generated docs at /api/schema/swagger-ui/.

Worker — Celery

redis-server

celery -A core worker -l info

Handles outbound WhatsApp messages, M-Pesa calls and SMS dispatch.

Want a walkthrough of the code?

I'm happy to talk through the architecture or the decisions above — and I build systems like this for other people too.