Skip to content

Commit 1817ed4

Browse files
Enhance Docker, update README, finish core features & Stripe webhook
docs: update README chore: enhance Docker setup - Modified Dockerfile and docker-compose for better development and deployment - Added .dockerignore to optimize image builds feat: finalize and test core features - Completed testing for orders, payments, authentication, cart, and views - Implemented Stripe webhook handling - Improved product recommender logic
1 parent 857c80c commit 1817ed4

File tree

26 files changed

+882
-330
lines changed

26 files changed

+882
-330
lines changed

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
__pycache__/
2+
*.pyc
3+
*.pyo
4+
*.pyd
5+
.env
6+
.git
7+
.gitignore
8+
media/
9+
static/

Dockerfile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
# Pull official base Python Docker image
2-
FROM python:3.12.3
2+
FROM python:3.12.3-slim
3+
4+
# Install system dependencies
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
libpq-dev \
8+
&& rm -rf /var/lib/apt/lists/*
9+
310
# Set environment variables
411
ENV PYTHONDONTWRITEBYTECODE=1
512
ENV PYTHONUNBUFFERED=1
13+
614
# Set work directory
715
WORKDIR /code
16+
817
# Install dependencies
9-
RUN pip install --upgrade pip
1018
COPY requirements.txt .
11-
RUN pip install -r requirements.txt
19+
RUN pip install --upgrade pip && pip install -r requirements.txt
20+
1221
# Copy the Django project
13-
COPY . .
22+
COPY . .

README.md

15.3 KB

Django-eCommerce-API-with-Redis-JWT-Celery-Smart-Search

A scalable RESTful eCommerce API using Django, JWT auth, Redis caching, Celery for task scheduling, advanced search, tagging, and personalized results.

# Django eCommerce API with Redis JWT Celery Smart Search

A scalable, modular, and performant eCommerce REST API built with Django and Django REST Framework. Features include JWT authentication, Stripe integration, Redis caching, Celery tasks, real-time chat with Django Channels, personalized search, tagging, and more.


🧭 Table of Contents


✅ Features

  • 🔐 User registration, login/logout, and JWT auth
  • 🛍️ Product catalog, categories, tagging, search
  • 🧺 Cart and order management
  • 💳 Stripe payment integration
  • 🧾 Coupon and discount system
  • 📦 Order history and tracking
  • 🔄 Background task scheduling (Celery + Redis)
  • 📡 Real-time chat (Django Channels)
  • 📬 Email notifications
  • 📄 Auto-generated OpenAPI/Swagger docs
  • 🐳 Docker support (optional)

🧰 Tech Stack

  • Backend: Django, Django REST Framework
  • Auth: JWT, Google OAuth2 (social-auth)
  • Database: PostgreSQL (configurable)
  • Caching/Queue: Redis
  • Background Tasks: Celery
  • Payments: Stripe API
  • Docs: drf-spectacular (OpenAPI/Swagger/Redoc)
  • Containerization: Docker & Docker Compose (optional)

📁 Project Structure

apps/
├── accounts/   → User auth, profiles
├── products/   → Products, categories, tags, search
├── orders/     → Order creation, history, cart
├── payments/   → Stripe integration & webhook
├── chat/       → Real-time chat via Channels
├── core/       → Shared utils, settings

🚀 Getting Started

Prerequisites

  • Python 3.10+
  • PostgreSQL
  • Redis
  • React.js (for frontend, optional)
  • pipenv or pip

Installation

# 1. Clone the repo
git clone https://github.com/YOUR_USERNAME/hypex-ecommerce-api.git
cd hypex-ecommerce-api

# 2. Create virtual env & install deps
pip install -r requirements.txt

# 3. Configure your environment variables
cp .env.example .env  # then edit with your secrets

# 4. Apply migrations
python manage.py migrate

# 5. Create superuser
python manage.py createsuperuser

# 6. Start Redis & Celery (in separate terminals)
redis-server
celery -A ecommerce_api worker -l info

# 7. Start development server
python manage.py runserver

📘 API Documentation


🔐 Environment Variables

Set these in .env:

SECRET_KEY=your_secret
DATABASE_URL=postgres://user:pass@localhost:5432/dbname
REDIS_HOST=localhost
REDIS_PORT=6379
EMAIL_HOST_USER=[email protected]
EMAIL_HOST_PASSWORD=password
DEFAULT_FROM_EMAIL=[email protected]
STRIPE_PUBLISHABLE_KEY=...
STRIPE_SECRET_KEY=...
STRIPE_WEBHOOK_SECRET=...
GOOGLE_OAUTH2_KEY=...
GOOGLE_OAUTH2_SECRET=...
DOMAIN=localhost
SITE_NAME=Hypex

🔀 Endpoints Overview

👤 User Management

Method Endpoint Description
GET /auth/me/ Get current user profile
PUT /auth/me/ Update user profile
PATCH /auth/me/ Partial update
DELETE /auth/me/ Delete user
POST /auth/reset-password/ Request password reset
POST /auth/reset-password-confirm/ Confirm reset token
POST /auth/set-password/ Set new password

🔐 Authentication

Method Endpoint Description
POST /auth/register/ Register a new user
POST /auth/activate/ Activate account
POST /auth/token/create/ Login
POST /auth/token/refresh/ Refresh JWT token
POST /auth/token/verify/ Verify token
POST /auth/token/destroy/ Logout

🛒 Products

Method Endpoint Description
GET /api/v1/products/ List all products
POST /api/v1/products/ Create a new product
GET /api/v1/products/{slug}/ Retrieve product
PUT /api/v1/products/{slug}/ Update product
PATCH /api/v1/products/{slug}/ Partial update
DELETE /api/v1/products/{slug}/ Delete product
GET /api/v1/products/user-products/ User's own listings

🧭 Categories

Method Endpoint
GET /api/v1/categories/
POST /api/v1/categories/
GET /api/v1/categories/{slug}/
PUT /api/v1/categories/{slug}/
PATCH /api/v1/categories/{slug}/
DELETE /api/v1/categories/{slug}/

🧺 Cart

Method Endpoint
GET /api/v1/cart/
POST /api/v1/cart/{product_id}/add/
DELETE /api/v1/cart/{product_id}/remove/

📦 Orders

Method Endpoint
GET /api/v1/orders/
POST /api/v1/orders/
GET /api/v1/orders/{order_id}/
PUT /api/v1/orders/{order_id}/
PATCH /api/v1/orders/{order_id}/
DELETE /api/v1/orders/{order_id}/

🏷️ Coupons

Method Endpoint
GET /api/v1/coupon/
POST /api/v1/coupon/
GET /api/v1/coupon/{id}/
PUT /api/v1/coupon/{id}/
PATCH /api/v1/coupon/{id}/
DELETE /api/v1/coupon/{id}/
POST /api/v1/coupon/apply-coupon/

💳 Payments

Method Endpoint
POST /payment/process/{order_id}/
GET /payment/completed/
GET /payment/canceled/

💬 Chat

Method Endpoint
GET /api/v1/chat/{product_id}/

🧪 Running Tests

python manage.py test

📜 License

This project is licensed under the MIT License. See the LICENSE file for details.


For more info, see in-line docstrings and source code. Happy coding!

Let me know if you want to add Docker setup, deployment notes (Heroku, Railway, etc.), or include a logo/banner at the top!

📬 Contact

Made by [Yousef M. Y. Al Sabbah] – Reach out via [[email protected]] or open an issue on GitHub.


Tip: Use Postman or Insomnia to explore the API endpoints quickly during development.

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Account Confirmation - Hypex eCommerce</title>
6+
<style>
7+
body {
8+
font-family: Arial, sans-serif;
9+
background-color: #f4f4f4;
10+
margin: 0;
11+
padding: 0;
12+
display: flex;
13+
justify-content: center;
14+
align-items: center;
15+
height: 100vh;
16+
}
17+
18+
.container {
19+
max-width: 400px;
20+
width: 100%;
21+
background: #fff;
22+
border-radius: 8px;
23+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
24+
padding: 20px;
25+
text-align: center;
26+
}
27+
28+
h1 {
29+
margin-bottom: 20px;
30+
}
31+
32+
p {
33+
margin-bottom: 30px;
34+
color: #555;
35+
}
36+
37+
form {
38+
display: flex;
39+
flex-direction: column;
40+
align-items: center;
41+
}
42+
43+
input[type="submit"] {
44+
padding: 10px 20px;
45+
background-color: #28a745;
46+
border: none;
47+
color: #fff;
48+
border-radius: 4px;
49+
cursor: pointer;
50+
font-size: 16px;
51+
}
52+
53+
input[type="submit"]:hover {
54+
background-color: #218838;
55+
}
56+
</style>
57+
</head>
58+
<body>
59+
<div class="container">
60+
<h1>Account Confirmation</h1>
61+
<p>Please confirm your account to activate it.</p>
62+
<form action="{% url 'auth:activate' %}" method="POST">
63+
{% csrf_token %}
64+
<input type="hidden" name="uid" value="{{ uid }}">
65+
<input type="hidden" name="token" value="{{ token }}">
66+
<input type="submit" value="Activate Account">
67+
</form>
68+
</div>
69+
</body>
70+
</html>

account/urls.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
TokenObtainPairView,
77
TokenRefreshView,
88
TokenVerifyView,
9-
TokenDestroyView,
9+
TokenDestroyView, ActivateView,
1010
)
1111

1212
# Application namespace to avoid conflicts
@@ -19,21 +19,14 @@
1919
user_reset_password = UserViewSet.as_view({'post': 'reset_password'})
2020
user_reset_password_confirm = UserViewSet.as_view({'post': 'reset_password_confirm'})
2121

22-
# Define 'me' endpoint with all desired HTTP methods mapped to 'me' action
23-
user_me = UserViewSet.as_view({
24-
'get': 'me',
25-
'put': 'me',
26-
'patch': 'me',
27-
'delete': 'me',
28-
})
29-
3022
urlpatterns = [
3123
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ AUTHENTICATION URLS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3224
# User registration
3325
path('register/', user_create, name='register'),
3426

3527
# User activation
3628
path('activate/', user_activate, name='activate'),
29+
path('activate/<str:uid>/<str:token>/', ActivateView.as_view(), name='activate-form'),
3730

3831
# Set password
3932
path('set-password/', user_set_password, name='set_password'),
@@ -46,7 +39,7 @@
4639

4740
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ USER Authentication URLS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4841
# Obtain a new JWT token
49-
path('token/obtain/', TokenObtainPairView.as_view(), name='jwt-create'),
42+
path('token/create/', TokenObtainPairView.as_view(), name='jwt-create'),
5043

5144
# Refresh an existing JWT token
5245
path('token/refresh/', TokenRefreshView.as_view(), name='jwt-refresh'),
@@ -56,7 +49,6 @@
5649

5750
# Destroy an existing JWT token
5851
path('token/destroy/', TokenDestroyView.as_view(), name='jwt-destroy'),
59-
6052
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ USER MANAGEMENT URLS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6153
# Current authenticated user endpoint with multiple HTTP methods
6254
path(

api/tests.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
from django.test import TestCase
2-
31
# Create your tests here.
4-
A

api/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
path('', include('orders.urls')),
88
path('', include('cart.urls')),
99
path('', include('coupons.urls')),
10-
path('', include('chat.urls')),
10+
path('chat/', include('chat.urls')),
1111
]

cart/serializers.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import decimal
2+
3+
from drf_spectacular.utils import extend_schema_field
14
from rest_framework import serializers
25

36
from shop.serializers import ProductSerializer
@@ -10,4 +13,42 @@ class CartItemSerializer(serializers.Serializer):
1013
total_price = serializers.DecimalField(max_digits=10, decimal_places=2)
1114

1215
items = CartItemSerializer(many=True)
13-
total_price = serializers.DecimalField(max_digits=10, decimal_places=2)
16+
coupon = serializers.SerializerMethodField()
17+
total_price = serializers.SerializerMethodField()
18+
19+
@extend_schema_field(serializers.DictField(child=serializers.CharField(), allow_null=True))
20+
def get_coupon(self, obj):
21+
"""
22+
Get the coupon code and discount applied to the cart.
23+
"""
24+
request = self.context.get('request')
25+
coupon_id = request.session.get('coupon_id')
26+
if not coupon_id:
27+
return None
28+
29+
from coupons.models import Coupon
30+
try:
31+
coupon = Coupon.objects.get(id=coupon_id, active=True)
32+
return {"code": coupon.code, "discount": coupon.discount}
33+
except Coupon.DoesNotExist:
34+
return None
35+
36+
@extend_schema_field(serializers.DictField(child=serializers.DecimalField(max_digits=10, decimal_places=2)))
37+
def get_total_price(self, obj):
38+
"""
39+
Calculate the final total price with and without the discount.
40+
"""
41+
total_price = obj.get('total_price', 0)
42+
coupon_data = self.get_coupon(obj)
43+
if coupon_data:
44+
discount = total_price * (decimal.Decimal(coupon_data['discount']) / decimal.Decimal(100))
45+
return {
46+
"without_discount": total_price,
47+
"with_discount": total_price - discount
48+
}
49+
return {"without_discount": total_price, "with_discount": total_price}
50+
51+
52+
class AddToCartSerializer(serializers.Serializer):
53+
quantity = serializers.IntegerField(min_value=1, default=1)
54+
override = serializers.BooleanField(default=False)

cart/urls.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from django.urls import path
1+
from django.urls import path, include
2+
from rest_framework.routers import DefaultRouter
23

3-
from .views import CartAPIView
4+
from .views import CartViewSet
5+
6+
router = DefaultRouter()
7+
router.register(r'cart', CartViewSet, basename='cart')
48

59
urlpatterns = [
6-
path('', CartAPIView.as_view(), name='cart'),
7-
path('cart/<uuid:product_id>/', CartAPIView.as_view(), name='cart-product'), # For POST and DELETE requests
10+
path('', include(router.urls)),
811
]

0 commit comments

Comments
 (0)