Multi-Tenant SaaS Subscription and Billing Architecture | Adhya Enterprises
How to design a resilient subscription state machine, plan feature gating, and automated dunning workflows in multi-tenant B2B platforms.
Subscription Complexity in Modern B2B SaaS
Monetizing multi-tenant software platforms requires navigating complex billing lifecycles: recurring monthly and annual intervals, tier-based feature access, seat add-ons, and payment webhook event reconciliation.
A fragile billing implementation causes revenue leakage, accidental customer account lockouts, or unauthorized feature access. Engineering a reliable subscription engine requires a formal state machine and server-authoritative entitlement enforcement.
Designing the Subscription State Machine
Never treat subscription status as a binary boolean flag (isSubscribed: true/false). A production B2B SaaS models explicit lifecycle states using typed database enums: `TRIALING` → `ACTIVE` → `PAST_DUE` → `CANCELED` → `PAUSED`.
Each transition is triggered exclusively by verified server events: automated trial expiration, payment gateway webhook receipts, or customer-initiated plan cancellation.
// lib/billing/subscription-state.ts
export type SubscriptionStatus =
| "TRIALING"
| "ACTIVE"
| "PAST_DUE"
| "CANCELED"
| "PAUSED";
export interface TenantEntitlements {
maxMembers: number;
hasCustomDomain: boolean;
hasAuditLogs: boolean;
hasApiAccess: boolean;
}
export const PLAN_ENTITLEMENTS: Record<string, TenantEntitlements> = {
STARTER: { maxMembers: 5, hasCustomDomain: false, hasAuditLogs: false, hasApiAccess: false },
PROFESSIONAL: { maxMembers: 25, hasCustomDomain: true, hasAuditLogs: true, hasApiAccess: false },
ENTERPRISE: { maxMembers: 250, hasCustomDomain: true, hasAuditLogs: true, hasApiAccess: true },
};
export function canAccessFeature(
status: SubscriptionStatus,
plan: string,
feature: keyof TenantEntitlements
): boolean {
// Grace period for past due accounts
if (status !== "ACTIVE" && status !== "TRIALING" && status !== "PAST_DUE") {
return false;
}
const entitlements = PLAN_ENTITLEMENTS[plan];
return entitlements ? Boolean(entitlements[feature]) : false;
}Server-Authoritative Feature Gating
Feature entitlements must never reside solely in client-side state. A malicious user can easily toggle client JavaScript variables to reveal hidden UI buttons.
Enforce plan boundaries at the server action or API route layer. Query middleware checks the authenticated organization's subscription status and plan entitlements before executing sensitive business logic.
Upgrades, Downgrades, and Proration Logic
When a customer switches plan tiers mid-cycle (e.g. from Starter to Professional on day 15 of a 30-day billing cycle), the billing engine must calculate prorated credits for unused days and apply them to the upgraded plan charge.
Delegate recurring invoice calculations to your authorized gateway billing engine, updating internal database records only when webhook confirmations verify settlement.
Dunning Workflows and Grace Periods
Credit card expirations and bank authorization delays cause up to 10% of renewal attempts to fail initially. Rather than immediately revoking tenant access, implement an automated dunning lifecycle:
Common SaaS Billing Pitfalls
Watch out for these recurring subscription implementation errors:
Related Capabilities & Product Case Studies
Review our SaaS Product Engineering service page to explore our multi-tenant application and subscription billing capabilities.
See our multi-tenant community operating architecture in action with Panchayt — Smart Society Management. For database-level tenant boundary design, read our foundational guide on Building Scalable Multi-Tenant SaaS with PostgreSQL & Prisma.
Related Engineering Guides
Building Scalable Multi-Tenant SaaS with PostgreSQL & Prisma
A deep dive into organization boundaries, foreign key integrity, and preventing tenant data leakage in enterprise SaaS platforms.
Architecting Secure Payment Gateways & Webhooks in Next.js
Why frontend payment confirmations are inherently dangerous, and how to build server-authoritative HMAC webhook handlers with idempotency.