JTalony
Back

Women's Legacy Store

High-load Django E-commerce

#Django#AsyncIO#YooKassa#APScheduler#Atomic

Key Features

Atomic Transactions

Prevents money loss if inventory fails.

Background Jobs

APScheduler for automated mailing.

Admin Panel

Custom dashboard for order management.

How It Works

Built a production-ready e-commerce platform for Women's Legacy brand. The biggest challenge was ensuring data consistency during payments - we couldn't charge customers and then fail to reserve their items. I solved this using Django's atomic transactions with select_for_update() to lock database rows during critical operations. The system also uses APScheduler for background jobs like order confirmation emails and inventory alerts. Payment processing is handled through YooKassa API with webhook integration for real-time payment updates.

Code Highlights

Atomic Payment Processing

This is the heart of the payment system. Using Django's transaction.atomic() with select_for_update(), we lock the order row to prevent race conditions. If payment succeeds, we decrease inventory. If anything fails, the entire transaction rolls back - no money charged, no inventory changed.

from django.db import transaction

def process_payment(order_id, payment_data):
    with transaction.atomic():
        # Lock the order row to prevent concurrent modifications
        order = Order.objects.select_for_update().get(id=order_id)
        
        if order.status != 'pending':
            raise ValueError("Order already processed")
        
        # Process payment with YooKassa
        payment_result = yookassa_client.create_payment(payment_data)
        
        if payment_result.status == 'succeeded':
            # Update inventory atomically
            for item in order.items.all():
                variant = item.variant
                if variant.quantity < item.quantity:
                    raise ValueError("Insufficient stock")
                
                variant.quantity -= item.quantity
                variant.save()
            
            order.status = 'paid'
            order.save()
        else:
            raise ValueError("Payment failed")

Background Email Jobs

Using APScheduler, I set up automated email campaigns. This job runs every day at 10 AM, finds orders that need confirmation emails, and sends them asynchronously without blocking the main application.

from apscheduler.schedulers.background import BackgroundScheduler
from django.core.mail import send_mail

def send_order_confirmations():
    # Find all paid orders without confirmation email
    orders = Order.objects.filter(
        status='paid',
        confirmation_sent=False
    )
    
    for order in orders:
        # Send confirmation email
        send_mail(
            subject=f'Order #{order.id} Confirmed',
            message=f'Thank you for your order!\n\nDetails: {order.items_summary()}',
            from_email='noreply@womenslegacy.com',
            recipient_list=[order.customer.email],
        )
        
        order.confirmation_sent = True
        order.save()

# Setup scheduler
scheduler = BackgroundScheduler()
scheduler.add_job(send_order_confirmations, 'cron', hour=10)
scheduler.start()

YooKassa Webhook Handler

This webhook receives real-time payment updates from YooKassa. When a payment succeeds, we update the order status. The signature verification ensures the request actually came from YooKassa and wasn't forged.

from django.views.decorators.csrf import csrf_exempt
import hashlib
import hmac

@csrf_exempt
def yookassa_webhook(request):
    # Verify webhook signature
    signature = request.headers.get('X-Signature')
    secret = settings.YOOKASSA_SECRET
    
    expected_signature = hmac.new(
        secret.encode(),
        request.body,
        hashlib.sha256
    ).hexdigest()
    
    if signature != expected_signature:
        return HttpResponse(status=403)
    
    # Parse webhook data
    event = json.loads(request.body)
    
    if event['event'] == 'payment.succeeded':
        payment_id = event['object']['id']
        order = Order.objects.get(payment_id=payment_id)
        
        # Update order status
        order.status = 'paid'
        order.save()
    
    return HttpResponse(status=200)