Guides / Prompting and shipping

How to Add Subscriptions with Stripe: Plans, Trials, Webhooks and Failed Payments

Add recurring billing with Stripe: Checkout in subscription mode, trials, the customer portal, the webhook events to handle, and what to do when a card fails.

Mythex Team · 2026-09-29 · 6 min read

To add subscriptions with Stripe, create a product with a recurring price in the Stripe Dashboard, send customers to Stripe Checkout in subscription mode from your server, and let a webhook tell your app when each payment succeeds, fails, changes or ends. Store the subscription status against the user and grant access from that, not from the success page. Add a Manage billing button that opens Stripe's hosted customer portal so people can change cards, switch plans and cancel without you building those screens.

This guide builds on how to add payments to your app, which covers one-time Checkout, test mode and going live. Here we focus on what's different about recurring billing. Stripe details are from Stripe's documentation as of September 2026.

Why subscriptions are harder than one-time payments

A one-time payment happens while the customer is in your app. A subscription keeps happening after they leave: renewals every month, cards that expire, upgrades mid-cycle, cancellations that take effect at period end. Your app only hears about these through webhooks. Most subscription bugs come from treating the first payment as the whole story.

Your options

ApproachWhat you buildBest for
Payment Link with a recurring priceNothing but a linkTesting demand; handling access by hand
Checkout (subscription mode) + customer portalA server route, a webhook, a billing buttonMost SaaS apps and memberships
Subscriptions API with your own payment formMuch more: payment UI, authentication, retriesApps with unusual billing flows
Merchant-of-record providerSimilar to CheckoutSellers who want the provider to handle sales tax

For most apps, Checkout plus the portal is the right trade-off. Compare providers in Stripe vs Paddle vs Lemon Squeezy if tax handling worries you.

Plan your pricing in Stripe first

In Stripe, a product is what you sell ("Pro plan") and a price is how you charge for it ("$20 per month", "$200 per year"). One product can have several prices. Decide:

  • Which tiers you offer and what each unlocks in your app
  • Monthly, yearly, or both
  • Whether you offer a trial, and whether it needs a card upfront

Create the products and prices in test mode, and keep their price IDs; your app refers to them. If you haven't settled pricing, read how to price a SaaS product and freemium vs free trial.

The subscription lifecycle you must handle

Stripe's subscriptions overview lists the statuses a subscription can have. The ones that matter most for access:

StatusWhat it meansGive access?
trialingIn a trial periodYes
activeIn good standingYes
incompleteFirst payment not yet completed; Stripe gives the customer 23 hoursNo
incomplete_expiredFirst payment never succeededNo
past_dueLatest payment failed or wasn't attempted; retries may followYour choice; many apps allow a short grace period
unpaidRetries finished, invoices stay open, no further attemptsNo, Stripe's docs say to revoke access
canceledEnded; can't be restartedNo
pausedA trial ended without a payment method and was set to pauseNo

Store the status on your user or account record and base access on it. Update it every time a relevant webhook arrives.

The webhook events to handle

EventWhat your app does
checkout.session.completedLink the new Stripe customer and subscription IDs to your user
invoice.paidMark the subscription active and record the new period end
invoice.payment_failedTell the user their card failed and link to the portal
invoice.payment_action_requiredAsk the user to complete card authentication
customer.subscription.updatedUpdate plan, status, and any scheduled cancellation
customer.subscription.deletedRemove paid access
customer.subscription.trial_will_endRemind the user before the trial ends; Stripe sends it three days before

Handler rules from Stripe's webhook docs still apply: verify the signature using the raw body, return a 2xx quickly, handle duplicate and out-of-order events, and expect retries. The what is a webhook guide explains the idea, and the payments guide lists the rules in detail.

Step by step

  1. Create products and prices in Stripe test mode.
  2. Add secrets: the secret key, the price IDs, and later the webhook signing secret. Never put them in frontend code.
  3. Pricing page: each plan's button calls your server, which creates a Checkout Session in subscription mode for the logged-in user. Pass your user ID so the webhook can match the payment to the account.
  4. Webhook endpoint: verify signatures and handle the events above, updating one subscription record per user.
  5. Gate features on the stored status and plan, checked on the server for every protected action, not just hidden buttons.
  6. Customer portal: configure it in the Stripe Dashboard (which plans people can switch to, whether they can cancel), then add a Manage billing button that creates a portal session on the server and redirects to it.
  7. Failed payments: in Stripe's billing settings, choose retry behaviour and what happens after the final retry. Show an in-app banner while past_due.
  8. Test the whole lifecycle, then publish and register the live webhook.

Prompts to give your AI builder

Add subscription billing with Stripe. Plans: Starter (price ID in STRIPE_PRICE_STARTER) and Pro (STRIPE_PRICE_PRO). The pricing page creates a Checkout Session in subscription mode on the server using STRIPE_SECRET_KEY, with the logged-in user's ID attached. Save the Stripe customer ID, subscription ID, plan and status on the user. Never expose the secret key to the browser.

Add POST /api/stripe/webhook. Verify the signature with STRIPE_WEBHOOK_SECRET using the raw request body. Handle checkout.session.completed, invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted and customer.subscription.trial_will_end. Record processed event IDs so duplicates are ignored. Update the user's status from the subscription object, not from the order events arrive in.

Gate Pro features on the server: allow them only when status is active or trialing and the plan is Pro. Show a "payment failed, update your card" banner when status is past_due. Add a Manage billing button that opens the Stripe customer portal for this user.

Upgrades, downgrades and proration

When a customer switches plans, Stripe's docs say the change often creates a proration: the new price is applied to the rest of the current billing period. You can preview prorations or turn them off. If the plans have the same interval, billing dates stay the same; moving from monthly to yearly moves the billing date to the day of the switch. Letting customers change plans in the portal saves you writing this logic, but decide in the portal settings which switches are allowed.

Testing checklist

Use Stripe test mode and test cards, and walk through every state:

  • New subscription grants the right plan to the right user
  • Closing the tab straight after paying still grants access (proves the webhook works)
  • A declined card on signup grants nothing
  • A renewal that fails puts the account in past_due and shows the banner
  • Updating the card in the portal restores active status
  • Upgrading and downgrading change features correctly
  • Cancelling at period end keeps access until the end date, then removes it
  • Trial users get access, get the reminder, and convert or lose access correctly
  • Sending the same webhook event twice changes nothing

Stripe's test clocks let you simulate time passing, so you can test renewals without waiting a month. Check Stripe's current testing docs for how to use them.

Common mistakes

  • Granting access on the success page. Customers may pay and never reach it. Use the webhook.
  • Only handling the first payment. Renewals, failures and cancellations arrive later, as events.
  • Checking plan status only in the frontend. Anyone can call your API directly.
  • One user, many subscriptions by accident. Check for an existing subscription before creating a new Checkout Session, or send existing customers to the portal instead.
  • Revoking access at the first failed renewal. Cards fail for boring reasons. A short grace period with a clear banner keeps customers.
  • Forgetting the live webhook. Test and live endpoints have different signing secrets.
  • Ignoring tax and refunds. Decide your refund policy and check your tax obligations with an accountant before launch.

Subscriptions in Mythex

Apps you build on Mythex use your own Stripe account and keys; there is no one-click Stripe connection for your app. Add your test keys and price IDs as project secrets, use prompts like the ones above, and test with Stripe's test cards in Preview. Webhooks need your app's public URL, so publish before registering the endpoint in Stripe, and publish again after you switch to live keys so the live app picks them up. See Accept payments with Stripe and Handle webhooks in your backend. For the wider product, how to build a SaaS MVP covers what to build around billing.

Questions

What is the simplest way to add subscriptions with Stripe?

Create a product with a recurring price in the Stripe Dashboard, send customers to Stripe Checkout in subscription mode, handle subscription events with a webhook, and link a Manage billing button to Stripe's hosted customer portal. That avoids building card forms and plan-change screens yourself.

Which Stripe webhook events do I need for subscriptions?

At minimum checkout.session.completed to link the new subscription to your user, invoice.paid to confirm each successful payment, invoice.payment_failed to warn about failed renewals, and customer.subscription.updated and customer.subscription.deleted to track plan changes and cancellations.

What happens when a subscription payment fails?

According to Stripe's documentation as of September 2026, the subscription usually becomes past_due and Stripe can retry the payment based on your settings. After the final retry, your Dashboard settings decide whether it is canceled, marked unpaid, or left past_due.

Can I offer a free trial without asking for a card?

Yes. Stripe supports free trials, and its docs describe a paused status for trials that end without a payment method when configured that way. Collecting a card upfront usually means fewer sign-ups but more paying customers; test what suits your product.

Keep reading

  • Freemium vs Free Trial: Which Is Right for Your SaaS? — Freemium vs free trial for a SaaS: how each works, costs, trade-offs, card-required vs no-card trials, and a simple way to decide which fits your product.
  • How to Add a Blog to Your Website: Options, SEO and Setup — How to add a blog to your website: Markdown files vs a built-in editor vs a CMS, subfolder vs subdomain, SEO basics, and prompts to build it with AI.
  • How to Add a Contact Form to Your Website (That Actually Reaches You) — How to add a contact form that works: save messages, get email alerts, stop spam, and avoid the mistakes that silently lose enquiries. Prompts included.
  • How to Add a Database to Your App (Without Losing Data Later) — How to add a database to an app you built: when you need one, Postgres vs hosted options, designing tables, prompts to use, and mistakes that lose data.
  • How to Add AI Features to Your App — Add summaries, chat, data extraction and classification to your app with an LLM API — keeping keys safe, costs under control and output trustworthy.
  • How to Add Analytics to Your App: GA4, Privacy-First Tools and Product Analytics — How to add analytics to your website or app: Google Analytics 4 vs privacy-first vs product analytics, what to track, cookie consent, and prompts to use.

Start building free · Templates · Docs