Cookies on softitland.com

We use essential storage to run this site. With your permission we would also like to use analytics cookies to see which pages help visitors, and marketing cookies to measure our ads. You can change your choice at any time with “Cookie settings” in the footer. Privacy Policy

SaaS & MVP

SaaS billing with Stripe: plans, seats and trials for multi-tenant apps

How to design SaaS billing on Stripe for a multi-tenant app: plans and prices, per-seat quantities, free trials, prorations, failed payments and webhooks.

Cover image for the article SaaS billing with Stripe: plans, seats and trials for multi-tenant apps
Key takeaways
  • Bill the account (tenant), not the individual user: one Stripe customer and subscription per workspace keeps data and billing aligned.
  • Per-seat pricing in Stripe is a subscription quantity; your app should update it when members are added or removed.
  • By default Stripe sends the trial_will_end event 3 days before a trial ends and calculates prorations down to the second.
  • Grant and remove access from webhooks such as invoice.paid and customer.subscription.deleted, not from the checkout redirect.
  • Stripe recommends Smart Retries for failed payments, with a default of 8 tries within 2 weeks.

How should you design SaaS billing on Stripe for a multi-tenant app?

Model one Stripe customer and subscription per account or workspace, define plans as products and prices, use the subscription quantity for seats, run trials through Stripe, and let webhooks, not the checkout redirect, decide who has access. Billing decisions shape your data model, so it pays to settle them before you build the rest of the product. Done well, upgrades, seat changes, trials and failed payments all run without manual work.

This is the structure our SaaS and MVP development team uses for subscription products.

Who is the customer: the user or the account?

In a multi-tenant app, the paying customer is usually the account (also called the workspace, organisation or team), not each person who logs in. Store a Stripe customer ID and subscription ID on the account record. Users belong to accounts, and their access comes from the account's subscription status. This keeps billing clean when people join, leave or belong to several accounts.

How do you set up plans and prices?

In Stripe, a product represents what you sell, such as the Pro plan, and prices define how it is charged: monthly, yearly, per seat or by usage. Keep plan limits and features in your own app, keyed to the price or product, so you can change what a plan includes without rebuilding billing. Avoid creating a new price for every customer; use coupons or quotes for special deals instead.

How does per-seat pricing work?

Stripe's documentation on quantities describes setting a quantity on a subscription as per-seat licensing, with a linear cost: ten seats cost ten times the base price. Your app decides what counts as a seat, such as active members or invited users, and updates the subscription quantity whenever that number changes.

  • Decide whether removing a member lowers the bill immediately or at renewal.
  • Decide whether pending invitations count as seats.
  • Show the current seat count and price on the billing page, so there are no surprises.

How should free trials work?

Trials can start with or without a payment method. Asking for a card up front usually means fewer, more serious trials; not asking means more sign-ups but more work to convert them. According to Stripe's trials documentation, the trial_will_end event is sent by default 3 days before a trial ends, and Stripe-hosted reminder emails go out 7 days before. Use that event to send your own reminder and to prompt users without a saved card to add one.

What happens on upgrades and downgrades?

When a customer changes plan or seat count mid-cycle, Stripe can prorate: crediting unused time on the old price and charging for the new one. Stripe's proration documentation says prorations are calculated down to the second by default, and the proration_behavior setting lets you create prorations, invoice them immediately, or skip them.

ChangeA common choice
Upgrade to a higher planApply immediately and charge the prorated difference now
Add seatsApply immediately; prorate on the next invoice or charge now
Downgrade or remove seatsApply at the end of the billing period
Switch monthly to yearlyStart the yearly period now with a credit for unused time

Why should webhooks control access?

A customer can close the browser after paying, and a renewal happens with nobody on your site at all. That is why access should follow Stripe's webhook events rather than the checkout redirect. Stripe's subscription webhooks guide lists the key events, including invoice.paid when an invoice is paid, invoice.payment_failed when a payment fails, and customer.subscription.deleted when a subscription ends. It also advises checking that the subscription is active before extending access after invoice.paid.

  • Verify every webhook signature.
  • Make handlers idempotent, because the same event can arrive more than once.
  • Store the subscription status and current period end on the account, and check them on every request that needs a paid plan.

How do you handle failed payments?

Cards expire and payments fail, so plan for it. Stripe's Smart Retries documentation lets you choose how many times and over what period to retry, with a recommended default of 8 tries within 2 weeks. In your app, show a clear banner while a payment is failing, send reminder emails, give a grace period, and only then restrict the account. Decide in advance whether a lapsed account becomes read-only or locked.

Do you need to build a billing page?

Often only a small one. Stripe's customer portal can be set up without code or through the API, and lets customers update payment methods, view invoices and, if you configure a product catalog, upgrade, downgrade or change quantities. Your own billing page can show the plan and seat usage and link to the portal for the rest. That saves weeks of building and testing payment screens.

What should your database store?

  • On the account: Stripe customer ID, subscription ID, price or plan, seat quantity, status and current period end.
  • Plan limits and features in your own configuration, keyed to the price.
  • A log of processed webhook event IDs, to prevent double processing.
  • Never card numbers: Stripe keeps those, and your app only stores references.

Does the tech stack matter?

Stripe has official libraries for the common back-end languages, and Laravel offers Laravel Cashier as a first-party package for Stripe subscriptions, which speeds up the standard flows. If you are still choosing a stack, read our comparison of Laravel and Node.js, and see how billing fits into planning an MVP. Our Laravel and custom software teams build both routes.

Frequently asked questions

Should we use Stripe Checkout or build our own payment form?

For most SaaS products, Stripe Checkout or Stripe's embedded payment elements are the better choice. They handle card input, authentication and many payment methods, and they keep card data off your servers. A fully custom form adds work and security scope with little benefit unless you have unusual requirements.

How do we handle annual and monthly plans together?

Create separate prices on the same product, one monthly and one yearly, often with the yearly price discounted. Customers switch between them by changing the subscription's price, and Stripe handles the proration rules you choose. Show the saving clearly on your pricing page.

Do we need a free plan as well as a free trial?

Not necessarily. A free trial gives full access for a limited time and pushes a decision, while a free plan keeps limited access forever and suits products that spread through usage. Many products start with a trial only, then add a free plan later if data shows it helps growth.

What happens to the customer's data when a subscription ends?

That is your product decision, and it should be written in your terms. Common approaches are a read-only period, an export option and deletion after a stated time. Build these states into your app from the start, triggered by subscription status, so you do not delete or lock data by accident.

Can Stripe handle usage-based pricing too?

Yes. Stripe supports usage-based billing, where you report usage and customers are charged for what they used. It can be combined with a base subscription or seats. Usage-based pricing needs careful metering in your app, so start simple and add it once you know which usage customers value.

How do we test billing before launch?

Use Stripe's test mode with test cards, and simulate the lifecycle: trial start and end, upgrades, seat changes, failed payments and cancellations. Stripe's test clocks let you move time forward on test subscriptions, so you can check renewals and trial endings without waiting days or weeks.

Last updated

Start a project

Have a project in mind? Let’s build it.

Tell us what you need. You get a clear plan, a fixed quote and a timeline before any work starts.

Prefer email? info@softitland.com