Subscription billing is the financial heartbeat of any SaaS business.
Yet, many software teams treat payment integration as an afterthought—slapping together a simple checkout redirect and hoping nothing breaks.
When credit cards expire, payments fail, users upgrade mid-month, or webhooks arrive out of order, poorly designed billing code causes lost revenue, incorrect account lockouts, and angry customer support tickets.
Here is how to design a production-grade subscription billing engine with Stripe and Django.
Core Principles of SaaS Billing Systems
┌──────────────────────────────────────────────────────────────┐
│ 1. Stripe is the Source of Truth for Payments │
│ 2. Your Database is the Source of Truth for Permissions │
│ 3. Never Trust Frontend Success Callbacks │
│ 4. Webhook Idempotency is Mandatory │
└──────────────────────────────────────────────────────────────┘
1. The Right Way to Use Stripe Webhooks
The golden rule of payment engineering: Never grant subscription access based on a frontend redirect URL.
A user might close their browser tab before the redirect completes, or a malicious user could spoof a frontend success query parameter.
Access must be granted exclusively when your backend receives and verifies a signed Stripe webhook event.
Key Webhooks to Handle:
customer.subscription.created$ ightarrow$ Provision tier features.customer.subscription.updated$ ightarrow$ Handle upgrades, downgrades, and cancellations.customer.subscription.deleted$ ightarrow$ Downgrade user to free tier or lock account.invoice.payment_succeeded$ ightarrow$ Record receipt, extend billing cycle.invoice.payment_failed$ ightarrow$ Trigger dunning emails, start 7-day grace period.
2. Handling Webhook Idempotency in Python
Stripe guarantees at-least-once delivery for webhooks. That means your server might receive the exact same event twice.
If your code charges a fee or extends an account balance without checking event IDs, you risk double-processing.
# Idempotent Webhook Handler in Django
class ProcessedWebhookEvent(models.Model):
event_id = models.CharField(max_length=255, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
# 1. Verify cryptographic signature
try:
event = stripe.Webhook.construct_event(payload, sig_header, settings.STRIPE_WEBHOOK_SECRET)
except Exception:
return HttpResponse(status=400)
# 2. Check for duplicate event
if ProcessedWebhookEvent.objects.filter(event_id=event['id']).exists():
return HttpResponse(status=200) # Already handled
# 3. Process event logic...
handle_stripe_event(event)
# 4. Record as processed
ProcessedWebhookEvent.objects.create(event_id=event['id'])
return HttpResponse(status=200)
3. Prorations, Upgrades & Downgrades
When a customer upgrades from a $49/mo Starter plan to a $199/mo Pro plan on Day 15 of a 30-day billing cycle:
- Stripe automatically calculates the unused balance on the Starter plan and applies it as a credit toward the Pro plan.
- Always use Stripe Customer Portal or pass
proration_behavior='always_invoice'to generate immediate line-item clarity for corporate accounting departments in the US, Europe, and Australia.
4. Grace Periods & Smart Dunning
Credit card transactions fail frequently due to bank fraud filters, expired expiration dates, or temporary balance limits.
- Do not lock accounts instantly: Provide a 5 to 7 day grace period where the user receives polite warning banners in the app.
- Enable Stripe Smart Retries: Use Stripe's machine-learning retry schedule to retry failed payments at optimal days and times.
- Self-Serve Card Updates: Provide a 1-click link to the Stripe Billing Portal where clients can update payment methods securely without contacting support.
Key Takeaways
- Verify Webhook Signatures: Always check the secret key on incoming payloads.
- Design for idempotency: Protect your database against duplicate webhook deliveries.
- Automate dunning: Grace periods recover up to 70% of unintentionally failed subscription renewals.
Need an enterprise-grade billing, invoicing, or marketplace payment integration? Contact KEHEM IT to engineer a resilient financial workflow.
Have a project in mind?
KEHEM designs and builds thoughtful websites, SaaS products, and business systems.