Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
APIs & SDKsHelp
Overview
Billing
OverviewAbout the Billing APIs
Subscriptions
    Overview
    How subscriptions work
    Get started
    Quickstart
    Plan an integration
    Build an integration
    Use cases
    About subscriptions
    Enable billing mode
    Configure subscription events
    Entitlements
    Subscription invoices
    Subscription schedules
    Recurring pricing models
    Strong Customer Authentication (SCA)
    Set up subscriptions
    Configure collection methods
    Embed a pricing table
    Set billing cycles
    Manage subscriptions
    Migrate subscriptions to Stripe
    Set product or subscription quantities
    Mixed interval subscriptions
    Backdate subscriptions
    Set trial periods
    Handle subscriptions with deferred payment
    Apply coupons
    Modify subscriptions
    Manage subscription payment methods
    Analytics
    Manage subscriptions on iOS
Invoicing
Usage-based billing
Quotes
Customer management
Billing with other products
Revenue recovery
Automations
Test your integration
Tax
Overview
Use Stripe tax
Manage compliance
Reporting
Overview
Select a report
Configure reports
Reports for multiple accounts
Reports API
Revenue recognition
Data
Overview
Query business data
Sigma
Data Pipeline
Import external data
HomeRevenueSubscriptions

Build a subscriptions integration

Create and manage subscriptions to accept recurring payments.

Embedded Checkout previewEmbedded Checkout preview
togethere.work

Integration effort

Low code

UI customisation

Customise the appearance.

Integration type

Use pre-built embedded forms to collect payments and manage subscriptions.

Set up the server

Set up Stripe

Install the Stripe client of your choice:

Command Line
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
# Available as a gem sudo gem install stripe
Gemfile
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Create a product and price

Create your products and their prices in the Dashboard or with the Stripe CLI.

This example uses a fixed-price service with two different service-level options: Basic and Premium. For each service-level option, you need to create a product and a recurring price. (If you want to add a one-off charge for something like a setup fee, create a third product with a one-off price. To keep things simple, this example doesn’t include a one-off charge.)

In this example, each product bills at monthly intervals. The price for the Basic product is 5 USD. The price for the Premium product is 15 USD.

Go to the Add a product page and create two products. Add one price for each product, each with a monthly recurring billing period:

  • Premium product: Premium service with extra features

    • Price: Flat rate | 15 USD
  • Basic product: Basic service with minimum features

    • Price: Flat rate | 5 USD

After you create the prices, record the price IDs so you can use them in other steps. Price IDs look like this: price_G0FvDp6vZvdwRZ.

When you’re ready, use the Copy to live mode button at the top right of the page to clone your product from a sandbox to live mode.

If you offer multiple billing periods, use Checkout to upsell customers on longer billing periods and collect more revenue upfront.

For other pricing models, see Billing examples.

Create a Checkout Session

Add an endpoint on your server that creates a Checkout Session.

When you create the Checkout Session, pass the following parameters:

  • To use the embedded payment form, set ui_mode to embedded.
  • To create subscriptions when your customer checks out, set mode to subscription.
  • To define the page your customer returns to after completing or attempting payment, specify a return_url. Include the {CHECKOUT_SESSION_ID} template variable in the URL. Checkout replaces the variable with the Checkout Session ID before redirecting your customer. You create and host the return page on your website.
  • To include your subscription and cancellation terms and a link to where your customers can update or cancel their subscription, optionally use custom text. We recommend configuring email reminders and notifications for your subscribers.

To mount Checkout, use the Checkout Session’s client_secret returned in the response.

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
curl https://api.stripe.com/v1/checkout/sessions \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d mode=subscription \ -d "line_items[0][price]"=
"{{PRICE_ID}}"
\ -d "line_items[0][quantity]"=1 \ -d ui_mode=embedded \ --data-urlencode return_url="https://example.com/checkout/return?session_id={CHECKOUT_SESSION_ID}"

Build your subscription page
Client

Mount Checkout

Load Stripe.js

Use Stripe.js to remain PCI compliant by ensuring that payment details are sent directly to Stripe without hitting your server. Always load Stripe.js from js.stripe.com to remain compliant. Don’t include the script in a bundle or host it yourself.

Define the payment form

To securely collect the customer’s information, create an empty placeholder div. Stripe inserts an iframe into the div.

Checkout is available as part of Stripe.js. Include the Stripe.js script on your page by adding it to the head of your HTML file. Next, create an empty DOM node (container) to use for mounting.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Accept a payment</title> <meta name="description" content="A demo of a payment on Stripe" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="style.css" /> <!-- Add the Stripe.js script here --> <script src="https://js.stripe.com/clover/stripe.js"></script> <script src="checkout.js" defer></script> </head> <body> <!-- Display a payment form --> <div id="checkout"> <!-- Checkout inserts the payment form here --> </div> </body> </html>

Initialise Stripe.js

Initialise Stripe.js with your publishable API key.

Fetch a Checkout Session client secret

Create an asynchronous fetchClientSecret function that makes a request to your server to create a Checkout Session and retrieve the client secret.

Initialise Checkout

Initialise Checkout with your fetchClientSecret function and mount it to the placeholder <div> in your payment form. Checkout is rendered in an iframe that securely sends payment information to Stripe over an HTTPS connection.

Avoid placing Checkout within another iframe because some payment methods require redirecting to another page for payment confirmation.

index.js
// Initialize Stripe.js const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
); initialize(); // Fetch Checkout Session and retrieve the client secret async function initialize() { const fetchClientSecret = async () => { const response = await fetch("/create-checkout-session", { method: "POST", }); const { clientSecret } = await response.json(); return clientSecret; }; // Initialize Checkout const checkout = await stripe.initEmbeddedCheckout({ fetchClientSecret, }); // Mount Checkout checkout.mount('#checkout'); }

Show a return page

After your customer attempts payment, Stripe redirects them to a return page that you host on your site. When you created the Checkout Session, you specified the URL of the return page in the return_url parameter.

Note

During payment, some payment methods redirect the customer to an intermediate page, such as a bank authorisation page. When the customer completes that page, Stripe redirects them to your return page.

Create an endpoint to retrieve a Checkout Session

Add an endpoint to retrieve a Checkout Session status with the Checkout Session ID in the URL.

Retrieve a Checkout Session

To use details for the Checkout Session, immediately make a request to the endpoint on your server to retrieve the Checkout Session status using the Checkout Session ID in the URL as soon as your return page loads.

Handle the session

Handle the result based on the session status:

  • complete: The payment succeeded. Use the information from the Checkout Session to render a success page.
  • open: The payment failed or was cancelled. Remount Checkout so that your customer can try again.
return.js
// Retrieve a Checkout Session // Use the session ID initialize(); async function initialize() { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const sessionId = urlParams.get('session_id'); const response = await fetch(`/session-status?session_id=${sessionId}`); const session = await response.json(); // Handle the session according to its status if (session.status == 'open') { // Remount embedded Checkout window.location.replace('http://localhost:4242/checkout.html') } else if (session.status == 'complete') { document.getElementById('success').classList.remove('hidden'); document.getElementById('customer-email').textContent = session.customer_email; // Show success page // Optionally use session.payment_status or session.customer_email // to customize the success page } }
server.js
// Add an endpoint to fetch the Checkout Session status app.get('/session_status', async (req, res) => { const session = await stripe.checkout.sessions.retrieve(req.query.session_id); const customer = await stripe.customers.retrieve(session.customer); res.send({ status: session.status, payment_status: session.payment_status, customer_email: customer.email }); });

OptionalConfigure the customer portal

You can set up the customer portal to let your customers directly manage their existing subscriptions and invoices.

You can configure the portal in the Dashboard. To reduce churn, you can configure the portal to allow customers to update their payment methods in the case of failed payments.

To help customers find it, add a button on your website to redirect to the customer portal to allow customers to manage their subscription. Clicking this button redirects your customer to the Stripe-hosted customer portal page.

Learn more about the customer portal and other customer management options.

Create a portal session

To add a customer portal, define an endpoint that creates the customer portal session for your front end to call. The CUSTOMER_ID refers to the customer ID created by a Checkout Session that you saved while processing the checkout.session.completed webhook. You can also set a default redirect link for the portal in the Dashboard.

Pass an optional return_url value for the page on your site to redirect your customer to after they finish managing their subscription:

server.rb
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
View full sample
# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
# This is the URL that users are redirected to after they're done # managing their billing. return_url =
'{{DOMAIN_URL}}'
customer_id
=
'{{CUSTOMER_ID}}'
session
= Stripe::BillingPortal::Session.create({ customer: customer_id, return_url: return_url, }) # Redirect to the URL for the session # redirect session.url, 303

Send customers to the customer portal

On your front end, add a button to the page at the success_url that provides a link to the customer portal:

success.html
View full sample
<html> <head> <title>Manage Billing</title> </head> <body> <form action="/customer-portal" method="POST"> <!-- Note: If using PHP set the action to /customer-portal.php --> <button type="submit">Manage Billing</button> </form> </body> </html>

After exiting the customer portal, the customer returns to your website at the return_url. Continue to monitor events to track the status of the customer’s subscription.

If you configure the customer portal to allow actions such as cancelling a subscription, make sure to monitor additional events.

Provision access

When the subscription is active, give your user access to your service. To do this, listen to the customer.subscription.created, customer.subscription.updated and customer.subscription.deleted events. These events pass a Subscription object that contains a status field indicating whether the subscription is active, past due or cancelled. See the subscription lifecycle for a complete list of statuses. To manage access to your product’s feature, learn about integrating entitlements.

In your webhook handler:

  1. Verify the subscription status. If it’s active, your user has paid for your product.
  2. Check the product that your customer subscribed to and grant them access to your service. Checking the product instead of the price allows you to change the pricing or billing period, as needed.
  3. Store the product.id, subscription.id and subscription.status in your database, along with the customer.id you already saved. Check this record when determining which features to enable for the user in your application.

The subscription status might change at any point during its lifetime, even if your application doesn’t directly make any calls to Stripe. For example, a renewal might fail because of an expired credit card, which puts the subscription in a past due status. Or, if you implement the customer portal, a user might cancel their subscription without directly visiting your application. Implementing your handler correctly keeps your application status in sync with Stripe.

Test your integration

Test payment methods

Use the following table to test different payment methods and scenarios.

Payment methodScenarioHow to test
BECS Direct DebitYour customer successfully pays with BECS Direct Debit.Fill out the form using the account number 900123456 and BSB 000000. The confirmed PaymentIntent initially transitions to processing, then transitions to the succeeded status three minutes later.
BECS Direct DebitYour customer’s payment fails with an account_closed error code.Fill out the form using the account number 111111113 and BSB 000000.
Credit cardThe card payment succeeds and doesn’t require authentication.Fill out the credit card form using the credit card number 4242 4242 4242 4242 with any expiration, CVC, and postal code.
Credit cardThe card payment requires authentication.Fill out the credit card form using the credit card number 4000 0025 0000 3155 with any expiration, CVC, and postal code.
Credit cardThe card is declined with a decline code like insufficient_funds.Fill out the credit card form using the credit card number 4000 0000 0000 9995 with any expiration, CVC, and postal code.
SEPA Direct DebitYour customer successfully pays with SEPA Direct Debit.Fill out the form using the account number AT321904300235473204. The confirmed PaymentIntent initially transitions to processing, then transitions to the succeeded status three minutes later.
SEPA Direct DebitYour customer’s PaymentIntent status transitions from processing to requires_payment_method.Fill out the form using the account number AT861904300235473202.

Monitor events

Set up webhooks to listen to subscription change events, such as upgrades and cancellations. You can view subscription webhook events in the Dashboard or with the Stripe CLI.

Learn more about testing your Billing integration.

Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc