Billing Concept
Understand how billing is modeled in Starter Free and where to plug Stripe without rewrites.
OverviewLink to section
Billing is one of the most important SaaS workflows, but also one of the easiest to wire incorrectly.
Starter Free gives you the complete billing UX surface while keeping the implementation intentionally mocked.
You get the product-facing structure:
- current plan
- renewal date
- seats
- payment method
- invoice history
- billing portal entry point
But you do not get the billing engine yet.
Core idea
Starter Free models the billing experience. Starter Pro wires the billing business layer.
Billing philosophyLink to section
Starter Free ships the billing surface, not the billing engine.
| Included | Not included |
|---|---|
| Plan display | Stripe Checkout |
| Renewal information | Stripe webhooks |
| Seats concept | subscription lifecycle sync |
| Invoice history UI | real Stripe invoices |
| Billing portal entry point | customer portal session creation |
| Payment method surface | backend billing contracts |
This separation lets you validate monetization UX before committing to full Stripe architecture.
Why this matters
Billing is not just a page. It is a revenue workflow. The UI can be mocked early, but the production implementation must be wired carefully.
What you’ll understandLink to section
Billing surface
Invoice model
Stripe boundary
Mental modelLink to section
Billing UI is stable. Billing implementation evolves behind it.
| Layer | Responsibility |
|---|---|
| UI | Plan, invoices, payment method, seats, portal actions |
| Backend | Creates checkout and portal sessions |
| Stripe | Payments, subscriptions, invoices, customer state |
| Webhooks | Source of truth for lifecycle changes |
| Database | Stores synchronized billing state for the app |
Production rule
Never treat the billing page as the source of truth. Stripe events and your backend state must drive production billing.
StepsLink to section
Understand the billing routeLink to section
The billing surface lives here:
app/(app)/billing/page.tsxIt simulates:
- current plan
- seats
- renewal date
- subscription status
- payment method
- invoice history
- billing portal action
This route is designed to feel like a real SaaS billing area before backend wiring exists.
Review the mocked subscription modelLink to section
Starter Free uses a mocked subscription object.
const plan = {
name: "Pro",
priceLabel: "€29 / month",
seats: 10,
renewalIso: "2026-03-01",
status: "active",
};Later, this should be replaced by your backend subscription state.
const plan = await getCurrentSubscription(user.id);The UI should not need a rewrite when the source changes.
Replace invoices with Stripe-backed dataLink to section
Invoice history starts as mock data.
const MOCK_INVOICES = [
{
number: "INV-0001",
amountCents: 2900,
status: "paid",
},
];Later, invoices should come from Stripe or from a synchronized invoice table in your database.
export async function listInvoices(customerId: string) {
return stripe.invoices.list({
customer: customerId,
limit: 10,
});
}Wire the billing portal laterLink to section
The frontend should call your backend, not Stripe directly.
POST /api/billing/portalYour backend creates the portal session:
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
return_url: "https://yourapp.com/billing",
});Then the frontend redirects:
window.location.href = session.url;Security boundary
Stripe secret keys must stay on the server. The browser should only receive a session URL created by your backend.
Stripe lifecycleLink to section
A production billing system usually follows this flow:
User selects a plan
↓
Stripe Checkout opens
↓
Payment or subscription is created
↓
Stripe sends webhook event
↓
Backend verifies event
↓
Database subscription state updates
↓
App reads updated billing stateCheckout starts revenue
Checkout creates the initial payment or subscription flow between your product and Stripe.
Decision guideLink to section
Starter Free is enough when:
- you are validating pricing UX
- you need a credible billing page in a demo
- you want to show plan, invoice, and payment surfaces
- you are not accepting real payments yet
Starter Pro becomes the faster path when:
- real payments are required
- Stripe Checkout must work
- webhooks must update your app
- invoices must reflect real customer history
- subscription status controls product access
- auth and billing need to work together
Ready to turn billing into revenue?
Starter Free validates UX. Starter Pro lets you charge customers for real.
Prefer
- keep billing UI separate from Stripe implementation
- create Checkout and Portal sessions on the server
- use webhooks as the lifecycle source of truth
- store synchronized billing state in your app database
- make billing copy clear, specific, and trustworthy
Avoid
- calling Stripe secret APIs from the browser
- treating UI state as billing truth
- skipping webhook verification
- unlocking paid features before backend confirmation
- hiding payment failures behind vague error messages
UX copywritingLink to section
Billing pages are high-trust surfaces. Copy should reduce doubt and make money-related actions explicit.
Use clear labels:
- “Manage billing”
- “View invoices”
- “Update payment method”
- “Change plan”
- “Cancel subscription”
Avoid vague labels:
- “Continue”
- “Submit”
- “Open”
- “Confirm”
Conversion principle
Billing copy should make the next action obvious. Users should never wonder whether they are starting checkout, opening a portal, changing a plan, or cancelling access.
Common questionsLink to section
Next stepsLink to section
Upgrade when billing becomes real
Starter Free gives you the billing surface. Starter Pro gives you the production-ready business layer: auth, Stripe, backend structure, and secure delivery.
Upgrade to Starter Pro →