Two billing models, six mechanical differences in Stripe. A decision framework for solo and indie SaaS founders picking which one to ship.
Pick one-time when the value you deliver is a discrete, one-shot outcome (a template, a calculator, a course, a lifetime tool). Pick subscription when the value is ongoing delivery (hosting, monitoring, content). Supporting both roughly doubles the Stripe surface area, so most indie SaaS founders should commit to one and ship deeper, not wider.
A billing model decision is the architectural choice between charging customers once for an outcome (one-time billing, Stripe Checkout in mode: 'payment') or charging them on a recurring cycle for ongoing access (subscription billing, Stripe Checkout in mode: 'subscription'). The decision determines which Stripe webhook events your app listens for, how much customer data you retain, whether you need dunning infrastructure, and how refunds map to access revocation.
Stripe Checkout in mode: 'payment'. One charge per customer. One checkout.session.completed event per purchase. Guest customers by default. No recurring contract to keep alive.
Best for: discrete value delivery (templates, courses, tools, lifetime deals)
Stripe Checkout in mode: 'subscription'. Recurring charges per cycle. Five or more lifecycle events per customer. Customer object required. Smart Retries dunning included.
Best for: ongoing value delivery (hosting, monitoring, content, continuous service)
The decision is not about pricing strategy; it is about which Stripe surface area your app commits to building and maintaining. These six categories cover the full mechanical diff.
| Dimension | One-Time | Subscription |
|---|---|---|
| Stripe Webhook Events | ||
| Primary purchase event | `checkout.session.completed` | `checkout.session.completed` + lifecycle events |
| Subscription lifecycle events | `customer.subscription.created/updated/deleted` | |
| Invoice events | `invoice.paid`, `invoice.payment_failed` | |
| Required handlers (minimum) | 1 | 5+ |
| Customer Object | ||
| Customer object required | No (guest customers by default) | Yes (required for recurring) |
| Customer data retention | Optional, your choice | Required for the customer lifetime |
| PII surface area | Email at minimum, name optional | Email, name, address, payment method on file |
| Stripe Tax | ||
| Tax computation | Once at checkout | On every recurring invoice |
| Tax-rate change handling | N/A after sale | Compounds across customer lifetime |
| Tax-ID validation | One-time check | Ongoing per renewal |
| Failed Payments & Dunning | ||
| Built-in retry layer | Smart Retries + custom rules | |
| Failure status surface | Single failed `checkout.session` | `past_due`, `unpaid`, `canceled` states |
| Customer notification flow | You build it | Stripe-managed emails available |
| Recovery responsibility | Customer re-initiates | Stripe + your dunning rules |
| Refunds & Access Revocation | ||
| Refund model | Single refund per purchase | Partial refunds + cancellation logic |
| Access revocation pattern | Optional (often skipped) | Required (gate every request) |
| Pro-rata math | N/A | Required on mid-period cancel |
| Idempotency & Scale | ||
| Webhook events per customer | 1 | 12+ per year minimum |
| Idempotency dedupe pressure | Bounded by purchases | Scales with customer base |
| Idempotency-Key TTL (Stripe default) | 24 hours | 24 hours |
| Code Surface & Buyer Fit | ||
| Webhook handler files (minimum) | 1 | 3-5 |
| Best for | Templates, calculators, courses, lifetime deals | Hosting, monitoring, content, ongoing service |
| Cash flow shape | Front-loaded | Smoothed across the customer lifetime |
| Churn surface | None (no recurring contract) | Recurring; needs retention metrics |
Webhook event names and Smart Retries / dunning behavior verified against Stripe's official documentation (Checkout, Subscriptions, Idempotency).
Both billing models have a genuine fit. The question is which one fits the value you deliver.
A single `checkout.session.completed` handler is enough to fulfill orders. Per Stripe docs: "Process the checkout.session.completed event to fulfill orders when a customer completes their purchase." No lifecycle state machine, no dunning rules, no pro-rata math.
Guest customers are the default. You can ship a one-time SaaS with email-and-payment as the only customer data on file. Less PII to retain means less to encrypt, audit, and rotate when keys leak.
There is no recurring contract to keep alive. Customer service load is bounded by the support window you commit to, not by month-over-month retention. Indie SaaS founders can ship and move on instead of running renewal funnels.
Each purchase fires one event with one Stripe Event ID. Idempotency dedupe is straightforward: a unique constraint on `processed_events.event_id` catches duplicates. Subscription apps generate 12+ events per customer per year just from renewals.
Subscription billing smooths cash flow across the customer lifetime. For SaaS that funds ongoing infrastructure (hosting, monitoring, content delivery), recurring revenue matches the recurring cost structure.
Stripe Smart Retries automatically re-attempt failed payments using machine-learned timing. Per Stripe docs: subscriptions can transition to `past_due` and Stripe re-attempts payment "using Smart Retries or based on your custom retry rules." Recovered payments add directly to ARR.
The full event surface (`subscription.created`, `subscription.updated`, `subscription.deleted`, `invoice.paid`, `invoice.payment_failed`) lets your app track entitlement state continuously. Tier upgrades, downgrades, and pauses all map to specific events.
For B2B SaaS billing across jurisdictions, Stripe Tax computes the right rate on every invoice. The cost of managing this manually for a SaaS with even a few hundred customers across regions usually outweighs the operational overhead of running subscriptions.
Decision rules grounded in the value you deliver, not in pricing psychology.
Both models are valid. The decision is not philosophical; it is architectural. Subscription billing adds at least four lifecycle event handlers, a dunning state machine, ongoing customer data retention, pro-rata refund math, and Stripe Tax computation on every invoice. That overhead is justified when the value you deliver is itself recurring. It is not justified when you are selling a one-shot outcome dressed up as recurring access.
For developer-tool SaaS in particular, one-time pricing aligns price with value delivery. A template is adopted once. A calculator is used until the customer outgrows it. A course is completed. Wrapping these in a subscription forces the founder to manufacture ongoing reasons to charge, which usually shows up as feature bloat, artificial limits, or aggressive dunning emails. None of those are good for the brand.
SecureStartKit ships mode: 'payment' exclusively. The template is a one-time architectural decision, not a recurring service. The code path is one Stripe webhook handler, one delivery email, one access grant. We document this choice openly: it is not the right model for every SaaS, but it is the right model for what we deliver. If your SaaS delivers ongoing value, subscription is the right call and the additional surface area is worth it.
Discrete value. One handler. SecureStartKit's choice.
Ongoing value. Full lifecycle. Worth the overhead when fit.
All five run on the security-first one-time pricing model.
SecureStartKit ships mode: 'payment' exclusively. One Stripe webhook handler, one delivery email, one access grant. The pricing page below is the worked example.