Subscription Billing Architecture: Stripe & Webhooks | KEHEM - KEHEM IT
← Back to Studio Notes

Subscription Billing Architecture: Stripe Integration with Webhooks

Learn how to build a bulletproof subscription billing architecture with Stripe and Django, including webhooks, proration, grace periods, and dunning management.

KEHEM / Studio Notes
  KEHEM

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

  1. Verify Webhook Signatures: Always check the secret key on incoming payloads.
  2. Design for idempotency: Protect your database against duplicate webhook deliveries.
  3. 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.

Talk to KEHEMExplore Services