Build a subscriptions integration with Elements
Create and manage subscriptions to accept recurring payments with Elements.

Customize with the Appearance API.
Use this guide to learn how to sell fixed-price subscriptions. You’ll use the Payment Element with Checkout Sessions API to create a custom payment form that you embed in your application.
If you don’t want to build a custom payment form, you can integrate with hosted checkout. For an immersive version of that end-to-end integration guide, see the Billing quickstart.
If you aren’t ready to code an integration, you can set up basic subscriptions manually in the Dashboard. You can also use Payment Links to set up subscriptions without writing any code. Learn more about designing an integration to understand the decisions you need to make and the resources you need.
What you’ll build 
This guide shows you how to:
- Model your business by building a product catalog.
- Build a registration process that creates a customer.
- Create subscriptions and collect payment information.
- Test and monitor payment and subscription status.
- Let customers change their plan or cancel the subscription.
API object definitions
Resource | Definition |
---|---|
Customer | Represents a customer who purchases a subscription. Use the Customer object associated with a subscription to make and track recurring charges and to manage the products that they subscribe to. |
Entitlement | Represents a customer’s access to a feature included in a service product that they subscribe to. When you create a subscription for a customer’s recurring purchase of a product, an active entitlement is automatically created for each feature associated with that product. When a customer accesses your services, use their active entitlements to enable the features included in their subscription. |
Feature | Represents a function or ability that your customers can access when they subscribe to a service product. You can include features in a product by creating ProductFeatures. |
Invoice | A statement of amounts a customer owes that tracks payment statuses from draft through paid or otherwise finalized. Subscriptions automatically generate invoices. |
PaymentIntent | A way to build dynamic payment flows. A PaymentIntent tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods. Invoices automatically create PaymentIntents. |
PaymentMethod | A customer’s payment methods that they use to pay for your products. For example, you can store a credit card on a Customer object and use it to make recurring payments for that customer. Typically used with the Payment Intents or Setup Intents APIs. |
Price | Defines the unit price, currency, and billing cycle for a product. |
Product | A good or service that your business sells. A service product can include one or more features. |
ProductFeature | Represents a single feature’s inclusion in a single product. Each product is associated with a ProductFeature for each feature that it includes, and each feature is associated with a ProductFeature for each product that includes it. |
Subscription | Represents a customer’s scheduled recurring purchase of a product. Use a subscription to collect payments and provide repeated delivery of or continuous access to a product. |
Here’s an example of how products, features, and entitlements work together. Imagine that you want to set up a recurring service that offers two tiers: a standard product with basic functionality, and an advanced product that adds extended functionality.
- You create two features:
basic_
andfeatures extended_
.features - You create two products:
standard_
andproduct advanced_
.product - For the standard product, you create one ProductFeature that associates
basic_
withfeatures standard_
.product - For the advanced product, you create two ProductFeatures: one that associates
basic_
withfeatures advanced_
and one that associatesproduct extended_
withfeatures advanced_
.product
A customer, first_
, subscribes to the standard product. When you create the subscription, Stripe automatically creates an Entitlement that associates first_
with basic_
.
Another customer, second_
, subscribes to the advanced product. When you create the Subscription, Stripe automatically creates two Entitlements: one that associates second_
with basic_
, and one that associates second_
with extended_
.
You can determine which features to provision for a customer by retrieving their active entitlements or listening to the Active Entitlement Summary event. You don’t have to retrieve their subscriptions, products, and features.
Set up Stripe
Install the Stripe client of your choice:
And then install the Stripe CLI. The CLI provides webhook testing and you can run it to make API calls to Stripe. This guide shows how to use the CLI to set up a pricing model in a later section.
For additional install options, see Get started with the Stripe CLI.
Create the pricing modelStripe CLI or Dashboard
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-time charge for something like a setup fee, create a third product with a one-time price. To keep things simple, this example doesn’t include a one-time 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.
Create the CustomerClient and Server
Stripe needs a Customer for each subscription. In your application front end, collect any necessary information from your users and pass it to the back end.
If you need to collect address details, the Address Element enables you to collect a shipping or billing address for your customers. For more information on the Address Element, see the Address Element page.
<form id="signup-form"> <label> Email <input id="email" type="email" placeholder="Email address" value="test@example.com" required /> </label> <button type="submit"> Register </button> </form>
const emailInput = document.querySelector('#email'); fetch('/create-customer', { method: 'post', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: emailInput.value, }), }).then(r => r.json());
On the server, create the Stripe Customer object.
Note
Make sure you store the Customer ID to use in the Checkout Session
Create a Checkout SessionServer
On the back end of your application, define an endpoint that creates the session for your front end to call. You’ll need the price ID of the subscription the customer is signing up for—your front end passes this value.
If you created a one-time price in step 2, pass that price ID also. After creating a Checkout Session, make sure you pass the client secret back to the client in the response.
Note
You can use lookup_keys to fetch prices rather than Price IDs. See the sample application for an example.
From your Dashboard, enable the payment methods you want to accept from your customers. Checkout supports several payment methods.
Collect payment informationClient
Collect payment details on the client with the Payment Element. The Payment Element is a prebuilt UI component that simplifies collecting payment details for a variety of payment methods.
Listen for webhooksServer
To complete the integration, you need to process webhooks sent by Stripe. These events are triggered whenever the status in Stripe changes, such as subscriptions creating new invoices. In your application, set up an HTTP handler to accept a POST request containing the webhook event, and verify the signature of the event:
During development, use the Stripe CLI to observe webhooks and forward them to your application. Run the following in a new terminal while your development app is running:
stripe listen --forward-to localhost:4242/webhook
For production, set up a webhook endpoint URL in the Dashboard, or use the Webhook Endpoints API.
You need to listen to a few events to complete the remaining steps in this guide. See Subscription events for more details about subscription-specific webhooks.
Provision access to your serviceClient and Server
Now that the subscription is active, give your user access to your service. To do this, listen to the customer.
, customer.
, and customer.
events. These events pass a subscription object that contains a status
field indicating whether the subscription is active, past due, or canceled. See the subscription lifecycle for a complete list of statuses.
In your webhook handler:
- Verify the subscription status. If it’s
active
then your user has paid for your product. - Check the product the customer subscribed to and grant access to your service. Checking the product instead of the price gives you more flexibility if you need to change the pricing or billing period.
- Store the
product.
,id subscription.
andid subscription.
in your database along with thestatus customer.
you already saved. Check this record when determining which features to enable for the user in your application.id
The status of a subscription 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 into 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.
Cancel the subscriptionClient and Server
It’s common to allow customers to cancel their subscriptions. This example adds a cancellation option to the account settings page.

Account settings with the ability to cancel the subscription
function cancelSubscription(subscriptionId) { return fetch('/cancel-subscription', { method: 'post', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ subscriptionId: subscriptionId, }), }) .then(response => { return response.json(); }) .then(cancelSubscriptionResponse => { // Display to the user that the subscription has been canceled. }); }
On the back end, define the endpoint for your front end to call.
Your application receives a customer.
event.
After the subscription cancels, update your database to remove the Stripe subscription ID you previously stored, and limit access to your service.
When a subscription cancels, you can’t reactivate it. Instead, collect updated billing information from your customer, update their default payment method, and create a new subscription with their existing customer record.
Test your integration
Test payment methods
Use the following table to test different payment methods and scenarios.
Payment method | Scenario | How to test |
---|---|---|
BECS Direct Debit | Your 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 Debit | Your customer’s payment fails with an account_ error code. | Fill out the form using the account number 111111113 and BSB 000000 . |
Credit card | The 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 card | The 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 card | The card is declined with a decline code like insufficient_ . | Fill out the credit card form using the credit card number 4000 0000 0000 9995 with any expiration, CVC, and postal code. |
SEPA Direct Debit | Your 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 Debit | Your customer’s PaymentIntent status transitions from processing to requires_ . | 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. Learn more about subscription webhooks. You can view events in the Dashboard or with the Stripe CLI.
For more details, see testing your Billing integration.
OptionalLet customers change their plansClient and Server
To let your customers change their subscription, collect the price ID of the option they want to change to. Then send the new price ID from the front end to a back-end endpoint. This example also passes the subscription ID, but you can retrieve it from your database for your logged in user.
function updateSubscription(priceId, subscriptionId) { return fetch('/update-subscription', { method: 'post', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ subscriptionId: subscriptionId, newPriceId: priceId, }), }) .then(response => { return response.json(); }) .then(response => { return response; }); }
On the backend, define the endpoint for your frontend to call, passing the subscription ID and the new price ID. The subscription is now Premium, at 15 USD per month, instead of Basic at 5 USD per month.
Your application receives a customer.
event.
OptionalPreview a price changeClient and Server
When your customer changes their subscription, there’s often an adjustment to the amount they owe, known as a proration. You can use the create preview invoice endpoint to display the adjusted amount to your customers.
On the front end, pass the create preview invoice details to a back-end endpoint.
function createPreviewInvoice( customerId, subscriptionId, newPriceId, trialEndDate ) { return fetch('/create-preview-invoice', { method: 'post', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ customerId: customerId, subscriptionId: subscriptionId, newPriceId: newPriceId, }), }) .then(response => { return response.json(); }) .then((invoice) => { return invoice; }); }
On the back end, define the endpoint for your front end to call.
OptionalDisplay the customer payment methodClient and Server
Displaying the brand and last four digits of your customer’s card can help them know which card is being charged, or if they need to update their payment method.
On the front end, send the payment method ID to a back-end endpoint that retrieves the payment method details.
function retrieveCustomerPaymentMethod(paymentMethodId) { return fetch('/retrieve-customer-payment-method', { method: 'post', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ paymentMethodId: paymentMethodId, }), }) .then((response) => { return response.json(); }) .then((response) => { return response; }); }
On the back end, define the endpoint for your front end to call.
Example response:
{ "id": "pm_1GcbHY2eZvKYlo2CoqlVxo42", "object": "payment_method", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": null,
Note
We recommend that you save the paymentMethod.
and last4
in your database, for example, paymentMethod.
as stripeCustomerPaymentMethodId
in your users
collection or table. You can optionally store exp_
, exp_
, fingerprint
, billing_
as needed. This is to limit the number of calls you make to Stripe, for performance efficiency and to avoid possible rate limiting.
Disclose Stripe to your customers 
Stripe collects information on customer interactions with Elements to provide services to you, prevent fraud, and improve its services. This includes using cookies and IP addresses to identify which Elements a customer saw during a single checkout session. You’re responsible for disclosing and obtaining all rights and consents necessary for Stripe to use data in these ways. For more information, visit our privacy center.