Stripe Integration Patterns for Multi-Tenant Platforms

BillStack Team
Stripe Integration Patterns for Multi-Tenant Platforms

Stripe Integration Patterns for Multi-Tenant Platforms

Stripe's API is well-designed for a single business. When your platform manages billing on behalf of dozens or hundreds of businesses, you need patterns that go beyond the getting-started guide.

The multi-key problem

In a multi-tenant setup, each tenant typically has their own Stripe account. That means your backend needs to:

The simplest pattern is a key manager that caches Stripe instances per tenant. When a request comes in for tenant A, you look up their key, create (or reuse) a Stripe client, and make the call. BillStack does this through a getTeamStripe(teamId) function that handles decryption and caching in one place.

Data synchronization

Once you can talk to multiple Stripe accounts, the next challenge is keeping your local database in sync. There are two approaches:

Pull-based (backfill)

When a tenant first connects their Stripe account, you run a one-time backfill that pages through their existing customers, products, prices, and subscriptions. This gives you a local copy of everything, indexed by your own IDs (like pc_ for customers, ps_ for subscriptions).

Push-based (webhooks)

After the initial sync, you rely on Stripe webhooks to keep data current. The tricky part is routing: when a customer.subscription.updated event arrives, you need to figure out which tenant it belongs to, look up the local record, and apply the change.

A common mistake is using a single webhook endpoint per tenant. This doesn't scale. Instead, use one endpoint and route based on the Stripe account ID in the event payload.

Checkout and portal sessions

Stripe Checkout and the Customer Portal work well in multi-tenant setups, but you need to be careful about session creation. Always create sessions server-side using the tenant's Stripe key, and make sure success_url and cancel_url point back to the right tenant's context.

For customer portals, BillStack uses token-based sessions: the platform generates a short-lived token, and the end-user opens a portal that's pre-authenticated for their specific customer record. No separate Stripe portal configuration per tenant required.

API key scoping

When exposing billing functionality through your own API, each API key should be scoped to a specific tenant and set of permissions. A key with customers:read shouldn't be able to create subscriptions. A key for tenant A shouldn't return tenant B's data.

BillStack generates keys with a bs_ prefix followed by 48 hex characters, SHA-256 hashed for storage. Each key is tied to a project and carries optional scopes that the middleware checks on every request.

Webhook signature verification

Every incoming webhook should be verified using the tenant's webhook signing secret — not a global one. Store the signing secret alongside the Stripe API key, and verify the signature before processing any event. This prevents both replay attacks and cross-tenant event injection.

Keep it boring

The best multi-tenant Stripe integrations are the ones that feel boring. No clever tricks, no shared Stripe accounts pretending to be isolated, no event processing that silently drops failures. Just a clear data flow from Stripe through your sync layer into your database, with proper tenant boundaries at every step.