Fintech

Subscription Billing Architecture for SaaS Platforms

How to architect subscription billing systems that handle plan changes, prorations, trials, and failed payments reliably.

Subscription Billing Is Harder Than It Looks

Charging a customer monthly sounds simple. But real subscription billing involves plan upgrades, downgrades, prorated charges, trial periods, grace periods, failed payment retries, tax calculation, and invoice generation. Each of these has edge cases that multiply when combined.

This article covers the architectural decisions that make or break a subscription billing system.

Core Data Model

Start with a clean separation of concerns:

Plan - defines the product offering (name, price, billing interval, features). Plans are templates, not per-customer records.

Subscription - the relationship between a customer and a plan. Contains the current state, billing cycle dates, and any customer-specific overrides.

Billing Period - a discrete time window within a subscription. Each period generates one invoice.

Invoice - the financial record for a billing period. Contains line items, tax, discounts, and payment status.

Payment - a record of a payment attempt against an invoice. An invoice may have multiple payment attempts if the first one fails.

Customer -> Subscription -> Billing Period -> Invoice -> Payment
                |
                v
              Plan

Keep these as separate models. Flattening subscriptions and invoices into a single table creates a mess when you need prorations, credits, or partial refunds.

The Billing Cycle Engine

The billing cycle engine is the heart of your system. It runs on a schedule (typically daily) and does three things:

  1. Identifies subscriptions due for renewal based on their current period end date
  2. Generates invoices for the upcoming period
  3. Attempts payment for open invoices

Renewal Logic

For each active subscription where current_period_end <= today:
    1. Calculate the next billing period (start date, end date)
    2. Calculate the amount (plan price, adjusted for proration/credits)
    3. Create an invoice with line items
    4. Attempt to charge the customer's payment method
    5. If successful: extend the subscription period
    6. If failed: enter retry logic

Critical rule: Generate the invoice before attempting payment. This creates an auditable record regardless of whether the charge succeeds or fails. Never charge first and record later.

Plan Changes and Proration

When a customer changes plans mid-cycle, you need a proration strategy:

Immediate Proration

Calculate the unused portion of the current plan and credit it, then charge the prorated amount for the new plan:

Days remaining in current period: 15 out of 30
Current plan: 100 EUR/month
New plan: 200 EUR/month

Credit for unused current plan: 100 * (15/30) = 50 EUR
Charge for remaining period on new plan: 200 * (15/30) = 100 EUR
Net charge: 50 EUR

This is fair but adds complexity. You need to track credits and apply them to the next invoice.

Next-Cycle Changes

Apply the plan change at the next renewal date. Simpler to implement and easier for customers to understand. The downside is that feature access either changes immediately (customer pays old price for new features) or at renewal (customer cannot access new features until they pay).

Recommended Approach

For most SaaS platforms, apply the feature change immediately but defer the billing change to the next renewal. This gives customers instant access to the new plan while keeping billing predictable. If the new plan is cheaper, credit the difference on the next invoice.

Trial Periods

Trials add a state before the first payment:

Trial -> Active -> (Canceled | Past Due | Expired)

Trial expiration handling:

  1. Send a reminder before the trial ends (3 days, 1 day)
  2. On trial end, attempt the first payment
  3. If payment succeeds, transition to Active
  4. If payment fails, you decide: extend grace period or cancel

Gotcha: Some customers sign up for trials with invalid payment methods. Validate the payment method at signup (a zero-amount authorization) to catch this early.

Failed Payment Retry (Dunning)

Payments fail. Credit cards expire, insufficient funds, temporary bank issues. A good dunning process recovers most of these:

Retry schedule example:

Attempt Timing Action
1 Day 0 (renewal) Initial charge attempt
2 Day 3 Retry + email notification
3 Day 7 Retry + stronger warning
4 Day 14 Final retry + cancellation warning
- Day 21 Cancel subscription

During retries, the subscription enters a "past due" state. Decide whether customers retain access during this period. Most SaaS platforms maintain access for the first 7-14 days, then restrict features.

Update payment method flow: Make it trivially easy for customers to update their card. Include a direct link in every dunning email. Do not make them log in, navigate to settings, and hunt for the billing page.

Tax Calculation

EU VAT rules for digital services are complex:

  • B2C: Charge VAT at the customer's country rate
  • B2B with valid VAT ID: Reverse charge (0% VAT)
  • B2B without valid VAT ID: Charge VAT at customer's country rate

Validate VAT IDs against the VIES system. Cache validation results (VIES has downtime) but re-validate periodically.

Store the tax amount, rate, and customer location on each invoice. Tax authorities want to see this breakdown.

Invoice Generation

Every billing event should produce a proper invoice with:

  • Sequential invoice number (required by law in most EU countries)
  • Seller and buyer details (name, address, VAT ID)
  • Line items with amounts
  • Tax breakdown
  • Payment status and date
  • Currency

Generate invoices as PDF and store them permanently. Retroactively changing invoice data is illegal in most jurisdictions. If a correction is needed, issue a credit note and a new invoice.

Webhooks and Event Architecture

Build your billing system around events:

  • subscription.created
  • subscription.renewed
  • subscription.plan_changed
  • subscription.canceled
  • invoice.created
  • invoice.paid
  • invoice.payment_failed
  • payment.succeeded
  • payment.failed

Internal consumers react to these events: feature provisioning, email notifications, analytics, and accounting integrations. External consumers (your customers' systems) receive webhooks for the same events.

Subscription billing rewards careful architecture. Get the data model right, handle prorations cleanly, build robust dunning, and generate proper invoices. The complexity is finite and well-understood. Rush it, and you will spend years patching edge cases.

Let's talk about your fintech needs

Whether you're modernizing your infrastructure, navigating compliance, or building new software - we can help.

Book a 30-min Call