Taking Recurring Payments in a Web App

A subscription is more than a card charge that repeats. Your web app has to know at every moment whether a customer has paid, what to do when a renewal fails, how people change or cancel their plan, and who is legally the seller. This guide walks through those decisions using Stripe's Billing documentation as a worked example, the European Banking Authority's Q&A on payments a merchant starts on its own, and Paddle's description of the merchant of record model. It draws on those sources, checked on 4 November 2026.

What this guide covers

This guide is about subscriptions sold on the web, inside a browser-based app or SaaS product. Two related topics have their own guides. Card security rules (PCI DSS), checkout options and 3-D Secure for one-off payments are in accepting payments online. Subscriptions sold inside an iPhone or Android app, where Apple's and Google's billing rules apply, are in in-app purchases and subscriptions.

The examples below use Stripe Billing because its documentation describes each step in detail. Other billing providers use different names, but your app has to handle the same states and events.

The subscription lifecycle

Stripe's How subscriptions work page says that when you create a subscription, Stripe "automatically generates invoices, attempts payment collection, and manages the subscription status throughout its lifecycle". Unlike a one-time payment, a subscription requires storing customer and payment method details for future billing cycles.

The same page lists the statuses a subscription can have:

  • incomplete: the first payment has not succeeded yet. The customer has 23 hours to pay; after that the subscription becomes incomplete_expired and does not bill.
  • trialing: in a trial period; Stripe says you can safely give access.
  • active: in good standing.
  • past_due: payment on the latest invoice failed or was not attempted. Invoices keep being created.
  • unpaid: set only if you choose it in your settings; invoices are still created but no payment is attempted.
  • canceled: a final state that cannot be updated. To come back, the customer gives payment details again and a new subscription is created.
  • paused: a trial ended without a payment method and you chose to pause in that case.

Your app's own data model needs room for each of these, even if some screens show them the same way.

Granting and removing access

The key rule is that your app, not the checkout page, decides who gets in, and it decides from events the billing provider sends. Stripe's subscription webhooks page describes, among others:

  • invoice.paid, sent when an invoice is paid; Stripe says you can provision access when you receive it and the subscription is active.
  • invoice.payment_failed, sent when a renewal payment fails.
  • invoice.payment_action_required, sent when an invoice needs customer authentication.
  • customer.subscription.trial_will_end, sent three days before a trial ends.
  • customer.subscription.deleted, sent when a subscription ends.

For the unpaid status, Stripe's lifecycle page says to revoke access, because payment has already been attempted and retried. What you do during past_due (keep full access, show a warning, or limit features) is your choice; write it down before launch so support and code agree. How webhooks are received and checked is covered in our API integrations guide.

Trials

Stripe's trial offers documentation covers trials with or without a payment method collected up front. When a trial ends and the customer has no payment method, the trial_settings option controls what happens, and one choice is to pause the subscription rather than cancel it. Use the trial_will_end event to remind people before they are charged and to ask for card details if you did not take them at sign-up.

Failed renewals and retries

Stripe's Automate payment retries page says payments "can fail for a number of reasons, but many of them are recoverable". Its Smart Retries feature picks retry times, and you set a policy of a number of retries within 1 week, 2 weeks, 3 weeks, 1 month or 2 months. For new accounts Stripe sets up to 8 retries within 2 weeks by default. You can turn Smart Retries off and write your own schedule.

Retries do not help with every failure. Stripe does not retry when no payment method is available or when the card issuer returns a "hard decline" such as a lost or stolen card; then the payment only goes through once the customer adds a new payment method. Each failure sends invoice.payment_failed with an attempt_count.

Retries work alongside messages to the customer. Stripe's revenue recovery overview lists automatic emails when a payment fails, a card expires or a payment method needs updating, and automatic card updates when a customer gets a new card number. When all retries are used up, your settings decide whether the subscription moves to canceled or unpaid, or stays past_due.

Renewals and Strong Customer Authentication

In Europe, PSD2 requires Strong Customer Authentication for many online card payments; our payments guide explains the basics. Stripe's SCA readiness page says it affects you if your business is in the European Economic Area, you serve customers there and you accept cards.

Renewals are different from the first purchase because the customer is not there. In Q&A 2018_4131, published by the European Banking Authority with an answer prepared by the European Commission, payments that the merchant starts under a standing agreement, in which the customer has authorised later payments, "can be considered as payee initiated transactions" and are not subject to SCA, provided they do not depend on a specific action by the payer. Without such a mandate from the customer, they cannot be treated that way. The answer is marked as an unofficial opinion; only the EU Court of Justice gives definitive interpretations.

In practice this means two things. Authenticate the customer properly when they first subscribe and save the card, and show clear terms for the later charges. And plan for the case where the bank still asks for authentication: Stripe sends invoice.payment_action_required, and its guide to saving a payment method says that when an off-session payment fails you should notify the customer to return to your app and complete payment there.

Letting customers manage their plan

People expect to update a card, switch plans, download invoices and cancel without writing to support. You can build these screens yourself or use a hosted page. Stripe's customer portal lets customers update billing information and payment methods, change, pause, resume or cancel subscriptions (immediately or at the end of the period), and view, pay and download invoices. It can offer a coupon when someone tries to cancel and collect cancellation reasons.

Check its limits against your pricing. Stripe says customers can cancel but not update subscriptions that use multiple products, usage-based billing or trial offers, or that are paid by sending invoices. Portal sessions are temporary: a new session expires after 5 minutes if unused, and within 1 hour of the most recent activity. For the wider account area around billing, see our guide to planning a customer portal.

Payment provider or merchant of record

One decision shapes your tax and support work more than any other: who is the seller. With a payment provider such as Stripe used directly, your business is the seller and handles sales tax and VAT itself; Stripe's documentation has a separate section on calculating and collecting taxes on subscriptions.

A merchant of record works differently. Paddle's developer documentation defines it as "a legal entity responsible for selling goods or services to an end customer" that takes on liabilities "such as collecting sales tax, ensuring PCI compliance, and honoring refunds and chargebacks". Its help centre says that, legally, Paddle "acts as a reseller of your product" and is responsible for collecting and paying VAT and tax instead of you. Paddle also says your name appears on the customer's statement alongside Paddle's.

The trade-off is control against workload: as your own merchant you deal with tax registrations and disputes yourself; with a merchant of record those sit with the reseller, under its terms and its list of what you may sell. Compare both on your target countries and product before writing code, because moving later means moving every saved subscription.

Frequently asked questions

Do subscription renewals need Strong Customer Authentication?

In the European Banking Authority's Q&A 2018_4131, payments a merchant starts under a standing agreement in which the customer authorised later charges can be treated as payee-initiated and are not subject to SCA, as long as they do not depend on an action by the customer. The bank can still ask for authentication, so your app needs a way to bring the customer back to complete it.

When should my app give or remove access?

Base it on events from the billing provider, not on the checkout page. Stripe says you can provision access when you receive invoice.paid and the subscription is active, and should revoke access when a subscription becomes unpaid. Decide in advance what a past_due customer can still do.

How many times are failed payments retried?

It depends on the provider and your settings. Stripe says new accounts get Smart Retries with up to 8 retries within 2 weeks by default, and you can choose other windows or a custom schedule. Stripe does not retry hard declines such as a lost or stolen card until a new payment method is added.

What is a merchant of record?

Paddle defines it as a legal entity responsible for selling goods or services to an end customer, which takes on liabilities such as collecting sales tax, PCI compliance, refunds and chargebacks. With a payment provider used directly, your own business is the seller and handles those itself.

Building a web app or SaaS product with paid plans? See our web app development service and all our services, or email [email protected].