A Django REST Framework backend for a single-vendor e-commerce platform. It exposes a JSON API consumed by a separate frontend (single-vendor-ecommerce-front-end) and covers the typical online-store domain: catalog, cart, coupons, wishlists, addresses, orders, Stripe checkout, role-based admin access, and rich-text legal pages.
The Django project package is named shophive and is run from manage.py.
Verified from the source in this repository:
- JWT-based authentication with
djangorestframework-simplejwt(1440-minute access tokens, 1-day refresh tokens) — see shophive/settings.py:234-259. - Custom user model keyed by email, with phone number (via
django-phonenumber-field), profile image, and role — users/models.py. - OTP-based flows for password reset, email change, and phone-number change, delivered via SMTP — users/views.py.
- Role-Based Access Control using
Module → Permission → Rolemodels — roles/models.py. - Product catalog with categories (self-referential tree), variants (size + hex color via
django-colorfield), per-variant images, size guides, and reviews — products/models.py. - Rich-text content for product descriptions and legal pages via
django-tinymce— products/models.py, legal_policies/models.py. - Cart with auto-calculated subtotal, 10% tax, flat shipping (150), coupon discounts (percentage / fixed with max cap) — cart/models.py.
- Coupons with category scoping, validity windows, min purchase, and max discount caps — coupons/models.py.
- Wishlists and multi-address support (with a single-default-address invariant) — wishlists/models.py, address/models.py.
- Orders with auto-generated
ORD-<timestamp>-<random>numbers, multiple statuses, Cash-on-Delivery and Stripe checkout sessions — orders/models.py, orders/views.py. - Stripe Checkout integration including session creation, PaymentIntents, a payment-success endpoint, and a webhook handler for
checkout.session.completed— orders/views.py. - Banners module for marketing assets with audience (customer / merchant) and status toggles — banners/models.py.
- Search, filter, ordering, pagination baked into the product API (DjangoFilterBackend + SearchFilter + OrderingFilter).
- Custom DRF pagination with
page_sizequery param up to 100 — common/pagination.py. - Custom exception handler that normalizes DRF/JWT/Validation errors into
{ "message": ... }— shophive/exceptions.py. - Custom 404/500 JSON handlers — shophive/views.py.
- UUID primary keys and
created_at/updated_attimestamps on every model via a sharedBaseModel— common/model.py. - File logging to
logs/django_debug.log(directory auto-created on startup).
| Layer | Tooling |
|---|---|
| Language | Python 3 (3.10+ recommended) |
| Framework | Django 5.0.6 |
| API | Django REST Framework 3.15, SimpleJWT 5.3 |
| Auth helpers | dj-rest-auth, django-allauth (installed, not wired into INSTALLED_APPS) |
| Database | SQLite (default in settings.py); PostgreSQL and MySQL drivers are also installed |
| Payments | Stripe (stripe==10.6.0) |
| Rich text | django-tinymce |
| Filtering | django-filter |
| Phone numbers | django-phonenumber-field |
| Color picker | django-colorfield |
| Images | Pillow |
| Caching helper | django-cacheops + redis (installed; not configured in settings) |
| Static files | whitenoise |
| CORS | django-cors-headers (currently CORS_ALLOW_ALL_ORIGINS = True) |
| Server (prod) | gunicorn |
| Env loading | django-environ |
Full pinned list in requirements.txt.
The Django project (shophive) wires together a set of feature apps, each with its own models.py, serializers.py, views.py, urls.py, and admin.py. All apps mount under the /api/ prefix via shophive/urls.py.
Client (frontend at http://localhost:3000)
│
▼
CORS middleware ──► Django middleware stack ──► DRF Router
│
▼
<App>ViewSet (Model/Generic)
│
▼
Serializer ◄──► Model (BaseModel: UUID + timestamps)
│
▼
SQLite / MySQL / Postgres
- All viewsets extend DRF's
ModelViewSet(or use generic views inlegal_policies). - The product viewset additionally mixes in
SuccessMessageMixinfrom common/mixins.py to wrap responses with a{ "message": ..., "data": ... }envelope. - Exceptions funnel through
shophive.exceptions.custom_exception_handler, which also catches expired/invalid JWTs and emits a singlemessagefield.
- JWT issued via
POST /api/token/(standard SimpleJWT) and via the customPOST /api/users/login/action which acceptsemail_or_phone+password. - Permissions default to
AllowAny; individual viewsets opt intoIsAuthenticated. Sensitive user actions (change_password,update_email,update_phone,profile, …) require auth. - Tokens carry
user_idas the user claim. Refresh throughPOST /api/token/refresh/.
- Orders created with
payment_method=stripetrigger astripe.checkout.Session.create(...)and return apayment_url. - Stripe redirects success to
STRIPE_SUCCESS_URL?order_number=...; the frontend then callsGET /api/orders/payment-success/to mark the order/payment paid. - Stripe pushes
checkout.session.completedevents toPOST /api/webhook/, verified withSTRIPE_WEBHOOK_SECRET.
None. Even though redis is in requirements.txt, there is no Celery, Channels, or async worker configuration in the repo. Emails are sent synchronously through Django's SMTP backend.
- Every model inherits
common.model.BaseModel→id: UUIDField (primary key),created_at,updated_at,ordering = ['-created_at']. Categoryis self-referential (parent → subcategories) with cycle detection inclean().Product → ProductVariant → (Size, ColorField, image)with derivedin_stock.CartisOneToOneper user;CartItemis unique per(cart, product_variant).- Order numbers are generated as
ORD-<unix_ts>-<6 random chars>.
ecommerce-backend/
├── manage.py
├── requirements.txt
├── .env.sample
├── shophive/ # Django project (settings, root urls, wsgi/asgi)
│ ├── settings.py
│ ├── urls.py
│ ├── views.py # custom 404 / 500 JSON handlers
│ ├── exceptions.py # custom DRF exception handler
│ ├── wsgi.py
│ └── asgi.py
├── common/ # shared base model, pagination, response mixin
│ ├── model.py
│ ├── pagination.py
│ └── mixins.py
├── utils/
│ └── utils.py # ColorUtils.get_color_name_from_hex(...)
│
├── roles/ # Module / Permission / Role
├── users/ # custom User model + auth/OTP flows
├── banners/ # marketing banners
├── categories/ # nested product categories
├── products/ # products, variants, size guides, images, reviews
│ └── filters/
│ └── filters.py
├── cart/ # cart + cart items (totals/discount logic)
├── coupons/ # promo codes
├── wishlists/ # per-user wishlist
├── address/ # multi-address with default flag
├── orders/ # orders, order items, payments, shipping methods, Stripe
└── legal_policies/ # Privacy Policy + Terms (TinyMCE)
Each feature app follows the standard Django layout: models.py, serializers.py, views.py, urls.py, admin.py, apps.py, tests.py, and per-app migrations/.
Note: there are no
templates/orstatic/directories committed — this project is API-only and pairs with a separate frontend repo.
git clone https://github.com/rifadul/ecommerce-backend.git
cd ecommerce-backendpython3 -m venv shophive_venv
source shophive_venv/bin/activate # Linux / macOS
# shophive_venv\Scripts\activate # Windowspip install -r requirements.txtCopy the sample env file and fill in real values:
cp .env.sample .envThen edit .env (see Environment Variables below). Note that .env.sample only lists the bare minimum — the running settings additionally require EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, STRIPE_SECRET_KEY, STRIPE_PUBLIC_KEY, and STRIPE_WEBHOOK_SECRET, or the project will fail at import time.
The default database is SQLite (auto-created at db.sqlite3 in the project root). To use PostgreSQL or MySQL, edit the DATABASES block in shophive/settings.py — a commented-out MySQL block is provided as a reference. The .env.sample defines DB_NAME/DB_USER/etc., but settings.py does not currently read them; you would need to wire them in yourself (e.g. via dj-database-url or environ).
python3 manage.py makemigrations
python3 manage.py migratepython3 manage.py createsuperuserYou will be prompted for email, phone_number, first_name, last_name, and a password (the user model overrides USERNAME_FIELD = 'email').
Variables actually read by shophive/settings.py:
| Variable | Required | Default | Purpose |
|---|---|---|---|
DJANGO_SECRET_KEY |
yes | — | Django secret key; also the JWT signing key |
DJANGO_DEBUG |
optional | False |
Toggle debug mode |
DJANGO_ALLOWED_HOSTS |
optional | localhost,127.0.0.1,192.168.10.39 |
Comma-separated allowed hosts |
EMAIL_HOST_USER |
yes | — | SMTP username (Gmail SMTP is hard-coded) |
EMAIL_HOST_PASSWORD |
yes | — | SMTP password / app password |
STRIPE_SECRET_KEY |
yes | — | Stripe secret key |
STRIPE_PUBLIC_KEY |
yes | — | Stripe publishable key |
STRIPE_WEBHOOK_SECRET |
yes | — | Signing secret used to verify webhook events |
Variables present in .env.sample but not currently consumed by settings.py (would need wiring before they take effect):
| Variable | Note |
|---|---|
DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT |
Listed in .env.sample; DATABASES is still hard-coded to SQLite |
CLIENT_ID, CLIENT_SECRET |
Referenced in the old README; no code path reads them |
Other hard-coded values worth knowing about:
EMAIL_HOST = smtp.gmail.com,EMAIL_PORT = 587,EMAIL_USE_TLS = True.STRIPE_SUCCESS_URL = http://localhost:3000/order-success/andSTRIPE_CANCEL_URL = http://localhost:3000/order-cancel/.CORS_ALLOW_ALL_ORIGINS = True(in addition to an explicitCORS_ALLOWED_ORIGINS = ["http://localhost:3000"]).CSRF_COOKIE_SECUREandSESSION_COOKIE_SECUREareTrue— local HTTP testing will not set the session/CSRF cookies; use HTTPS or temporarily relax these for dev.
python3 manage.py runserver
# http://127.0.0.1:8000/The browsable DRF API is available under /api/... and the Django admin under /admin/.
gunicorn is in requirements.txt. A typical invocation:
gunicorn shophive.wsgi:application --bind 0.0.0.0:8000 --workers 3Static files are served by WhiteNoise (installed but not added to MIDDLEWARE). To enable it, insert
'whitenoise.middleware.WhiteNoiseMiddleware' right after SecurityMiddleware, then run:
python3 manage.py collectstatic --noinputNot present in this repo. There is no Dockerfile, docker-compose.yml, nginx.conf, or CI workflow committed.
Not configured.
All endpoints are mounted under /api/. Every resource is a DefaultRouter-registered viewset, so the standard list/retrieve/create/update/partial_update/destroy actions exist for each.
POST /api/token/— obtain JWT pair ({"username": ..., "password": ...}— SimpleJWT defaults to the user model'sUSERNAME_FIELD, which isemailhere).POST /api/token/refresh/— refresh access token.POST /api/users/register/— register a new user and receive an initial JWT pair.POST /api/users/login/— login withemail_or_phone+password.
Send Authorization: Bearer <access_token> on protected endpoints.
Standard CRUD plus custom actions:
POST /api/users/register/POST /api/users/login/POST /api/users/change_password/POST /api/users/forget_password/POST /api/users/reset_password_with_otp/GET /api/users/profile/POST /api/users/update_email/POST /api/users/update_phone/POST /api/users/verify_otp/POST /api/users/resend_otp/POST /api/users/update_image/DELETE /api/users/delete-multiple/?ids=<id1,id2,...>
/api/modules/,/api/permissions/,/api/roles/POST /api/roles/delete-multiple/?ids=<id1,id2,...>
/api/category/— nested categories./api/products/— products withsearch,ordering(price,name,sku) anddjango-filter-backed filters.GET /api/products/colors/— unique variant colors with their inferred names.POST /api/products/add-review/— add a review (authenticated).DELETE /api/products/delete-multiple/?ids=<id1,id2,...>
/api/banners/
/api/cart/,/api/cart-items/,GET /api/my-cart//api/coupons//api/wishlist//api/address/
/api/orders/,/api/payments/,/api/shipping-methods/GET /api/orders/my_orders/GET /api/orders/order_details/?order_number=<n>POST /api/orders/<id>/cancel_order/POST /api/orders/<id>/create_payment_intent/GET /api/orders/payment-success/?session_id=<stripe_session_id>POST /api/webhook/— Stripe webhook receiver
GET /api/privacy-policy/GET /api/terms-and-conditions/
All list endpoints use common.pagination.CustomPageNumberPagination:
GET /api/products/?page=2&page_size=20
page_size is capped at 100.
POST /api/products/add-review/
Authorization: Bearer <access_token>
Content-Type: application/json
{
"product_id": "<uuid>",
"rating": 5,
"comment": "Great quality!"
}POST /api/orders/
Authorization: Bearer <access_token>
Content-Type: application/json
{
"shipping_address": "<address_uuid>",
"billing_address": "<address_uuid>",
"shipping_method": "<shipping_method_uuid>",
"payment_method": "stripe"
}The response includes a payment_url to redirect the user to Stripe Checkout.
OpenAPI/Swagger: not installed. There is no
drf-spectacular/drf-yasgconfigured in this repo.
Workflow:
python3 manage.py makemigrations # generate migrations from model changes
python3 manage.py migrate # apply pending migrations
python3 manage.py showmigrations # inspect status
python3 manage.py sqlmigrate <app> <n> # preview SQLPer-app migrations live in each app's migrations/ directory and are committed to the repo. All PKs are UUIDs (set on BaseModel), so cross-table relations use UUID FKs.
STATIC_URL = '/static/'
STATIC_ROOT = <BASE_DIR>/staticfiles
MEDIA_URL = '/media/'
MEDIA_ROOT = <BASE_DIR>/media- In
DEBUGmode, shophive/urls.py servesMEDIA_URLfromMEDIA_ROOT. - Uploaded files end up under
media/profile_images/,media/banners/,media/categories/,media/products/images/, andmedia/products/variants/. - Product/variant images are validated against an allow-list of formats (JPEG, JPG, PNG, GIF, BMP, TIFF, WEBP).
- For production, run
collectstaticand serve/static/and/media/through your web server (or wire up WhiteNoise — see above).
Each app ships a stub tests.py. No tests are currently implemented in this repository, and there is no pytest.ini, pyproject.toml, tox.ini, or coverage configuration committed.
Run Django's built-in test runner anyway with:
python3 manage.py testThere is no deployment config committed (no Dockerfile, compose file, Heroku Procfile, GitHub Actions workflow, or Nginx config). What the code suggests about how this is intended to run:
gunicornis the production WSGI server.django-herokuis inrequirements.txt, hinting at past or planned Heroku deployment, butdjango_heroku.settings(locals())is not called insettings.py.whitenoiseis installed for serving static assets in production (you must add its middleware — see the production section above).- Stripe webhook URL to register with Stripe:
https://<your-domain>/api/webhook/.
A minimal deployment checklist:
- Set
DJANGO_DEBUG=Falseand a strongDJANGO_SECRET_KEY. - Populate
DJANGO_ALLOWED_HOSTSwith your domain(s). - Switch
DATABASESto PostgreSQL/MySQL. - Tighten
CORS_ALLOW_ALL_ORIGINS = Falseand useCORS_ALLOWED_ORIGINS. - Add the WhiteNoise middleware, then
collectstatic. - Run with Gunicorn behind Nginx (or equivalent), terminate TLS at the edge.
- Configure Stripe webhook →
/api/webhook/.
| Symptom | Likely cause / fix |
|---|---|
ImproperlyConfigured: Set the DJANGO_SECRET_KEY environment variable |
.env is missing or DJANGO_SECRET_KEY is not set. |
KeyError for EMAIL_HOST_USER / STRIPE_* at startup |
settings.py reads these unconditionally — fill them in .env, even with dummy values for local dev. |
| Login cookies missing locally | CSRF_COOKIE_SECURE / SESSION_COOKIE_SECURE = True block cookies over plain HTTP. Use HTTPS or relax these for dev. |
401 with "Your session has expired..." |
JWT expired or malformed — request a new pair via /api/token/ or /api/users/login/. |
| Stripe webhook returns 400 | STRIPE_WEBHOOK_SECRET is wrong or the request body was modified by a proxy. Use the Stripe CLI (stripe listen --forward-to localhost:8000/api/webhook/) for local testing. |
phonenumber_field validation error |
Phone numbers must be in E.164 format (e.g. +8801712345678). |
| Frontend blocked by CORS | Check CORS_ALLOWED_ORIGINS; while CORS_ALLOW_ALL_ORIGINS = True is also set, leaving the explicit list out of sync can cause confusion. |
MySQL/Postgres not used despite .env settings |
DATABASES is hard-coded to SQLite — edit shophive/settings.py to switch engines. |
- Fork the repo and create a feature branch:
git checkout -b feat/<short-name>. - Run
python3 manage.py makemigrationswhenever you change a model and commit the generated migration files. - Keep new models inheriting from
common.model.BaseModelfor the UUID PK + timestamps. - Follow existing patterns: one app per domain, viewsets registered with
DefaultRouter, and URL include lines added to shophive/urls.py. - Open a pull request describing the change and any new environment variables.
No license file is committed to this repository. Treat the code as All Rights Reserved until the owner adds an explicit license (e.g. MIT, Apache-2.0).