Stripe subscriptions in a SaaS application: how they work

A SaaS subscription is a whole payment cycle. Using Offer Exchange as an example, we cover subscription states, webhooks, SCA and renewals in Stripe.

Stripe subscriptions in a SaaS application (software sold on a subscription) cover the whole cycle: choosing a plan, the first payment, renewals, failed charges, plan changes, cancellation and invoices. When we integrate Stripe, we follow one rule. Access to paid features depends on the subscription status that Stripe reports through webhooks (automatic notifications sent by Stripe). The user returning from the payment page proves nothing on its own. We show this using Offer Exchange, a SaaS platform with a free plan and three paid subscriptions. Facts about Stripe follow the provider’s documentation as of 25 September 2026.

Plans and limits in a SaaS application

Offer Exchange can be used free of charge, with monthly limits on the number of requests, recipients per request and products. One of three paid subscriptions raises those limits.

This model splits the work between the application and Stripe:

Stripe handles The application handles
prices and plans, card charges, renewals counting usage (how many requests were sent this month)
invoices and retrying failed payments blocking actions once a limit is reached
subscription status mapping limits to plans and messages for the user

Stripe does not know how many requests a user has sent. So the limits have to live in the application’s database and be checked on every action. The check must run in the server code, because blocking a button in the interface is not enough. Read more about how we build these products on our page about SaaS and MVP development.

Which subscription statuses does the application need to handle?

A Stripe subscription moves through several statuses (documentation: How subscriptions work). For each one, you have to decide what the user sees in the application.

Status in Stripe What it means What the application does
incomplete the first payment failed or is waiting for authentication; the customer has 23 hours does not switch the plan on, asks the customer to complete the payment
incomplete_expired the first payment did not go through within 23 hours treats it as no subscription
trialing trial period gives access to the plan
active the subscription is in good standing gives access to the plan
past_due the latest payment failed, and Stripe may retry it tells the user and asks them to update their card; whether to limit access is a business decision
unpaid retries have run out and invoices are unpaid removes paid features
canceled subscription cancelled (final status) returns to the free plan
paused the trial ended without a payment method (if configured that way) suspends paid features until the subscription resumes

past_due needs the most decisions. Cutting off access after the first failed payment puts off customers whose card has simply expired. Leaving full access for weeks means free use of a paid plan. Agree this with the product owner before launch.

How does the application know the customer has paid?

From webhooks, the notifications Stripe sends to the application’s address. The redirect after payment may never arrive (a closed browser tab, a dropped connection), and renewals happen without the user being present. For subscriptions, these are among the most important events (list of events):

  • invoice.paid: the invoice has been paid; once you confirm the subscription is active, you extend access,
  • invoice.payment_failed: the payment failed; you notify the customer,
  • invoice.payment_action_required: the bank requires authentication; you ask the customer to complete the payment,
  • customer.subscription.updated and customer.subscription.deleted: plan change, renewal, end of the subscription.

When handling webhooks, we follow a few rules from Stripe’s webhook documentation:

  • we verify the signature in the Stripe-Signature header against the raw request body, because otherwise someone could switch on a paid plan with a forged request,
  • we respond quickly with a 2xx code and move heavier logic to a queue; if delivery fails, Stripe retries it in live mode for up to three days,
  • we store the IDs of processed events, because the same event can arrive more than once,
  • we do not assume any order of events, because Stripe does not guarantee one; when in doubt, the application fetches the current subscription status from the Stripe API (application programming interface).

Strong customer authentication (SCA) for recurring payments

SCA comes from the revised Payment Services Directive (PSD2) and has applied since 14 September 2019. For card payments by customers in the European Economic Area, it means 3D Secure in practice: confirming the payment in the bank’s app (Stripe: Strong Customer Authentication).

With subscriptions, the customer authenticates the card on the first payment, while they are in the application. Stripe then charges renewals without their involvement. However, the bank can ask for confirmation again at any time, because SCA exemptions are not guaranteed. So the application has to be able to bring the customer back to complete the payment (the invoice.payment_action_required event). Stripe’s ready-made components (Checkout, Billing and Payment Intents) handle this flow, so we do not build it from scratch.

What happens when a renewal fails?

Stripe retries failed payments automatically. New accounts have Smart Retries switched on by default: up to 8 attempts within 2 weeks, at times chosen by Stripe’s model (Automate payment retries). What happens after the last attempt depends on your settings. The subscription is cancelled, marked as unpaid or left past due. If it meets the conditions, it can also be paused.

With some declines (for example, a card reported stolen or lost), retrying makes no sense without a new card. That is why the email to the customer after a failed payment should take them straight to the place where they can change their payment method.

Should you build your own billing portal for customers?

Usually not. In the Stripe customer portal, customers update their billing details and payment method, change or cancel their subscription and download invoices. The portal is available in many languages, including Polish, and can be matched to your brand (Customer portal). It does have limitations. For example, it cannot be embedded in a frame (iframe), and a subscription with several products can only be cancelled in the portal, not changed to another plan.

Local payment methods and invoices: the Polish example

Local markets often add their own requirements. In Poland, Stripe supports recurring BLIK payments in subscriptions (BLIK recurring payments). BLIK is a popular mobile payment method in Poland: the customer approves a mandate in their banking app, and later payments are collected without a new code. There are restrictions: PLN only, a maximum amount per charge and customers of selected banks only. For a product aimed at Polish businesses, offer it alongside cards.

Invoices are a separate question. A document generated by Stripe does not have to meet local invoicing rules. In Poland, for example, it may not qualify as a VAT invoice or reach KSeF, the national e-invoicing system. Before you decide whether Stripe or your accounting software issues the invoices, agree this with your accountants. That decision determines which integration you will need.

Our checklist for implementing subscriptions

  • subscription status stored in the application’s database and updated from webhooks,
  • limits checked on the server side,
  • a separate Stripe test environment and tested scenarios: card decline, 3D Secure, card expiry, plan change mid-period,
  • clear messages for the user in the past_due and incomplete statuses.

When payments should already be part of the first version of a product is covered in our article on what to include in an MVP. If you are planning an application with subscriptions, tell us about your pricing model. We will help you turn it into plans, limits and a Stripe integration.

Contact us

Let's talk about your application

Describe the idea or the process the application should improve. We will reply within one business day at the latest. You can also call +48 81 820 05 09.

The data controller is CONETSO GROUP Sp. z o.o. We process the data from this form to reply to your message. Details and your rights are described in our privacy policy.

Search this site