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 tools
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
Payment methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Custom payment flows
    Overview
    Payments for existing customers
    Authorize and capture a payment separately
    Build a two-step confirmation experience
    Collect payment details before creating an Intent
    Finalize payments on the server
    Take mail orders and telephone orders (MOTO)
    US and Canadian cards
    Forward card details to third-party API endpoints
    Payments line items
Flexible acquiring
Orchestration
In-person payments
Terminal
Other Stripe products
Financial Connections
Crypto
Climate
HomePaymentsCustom payment flows

Collect payment details before creating an Intent

Build an integration where you can render the Payment Element prior to creating a PaymentIntent or SetupIntent.

Copy page

Subscriptions is a pricing model where users make recurring payments to access a product. In this integration guide, learn how to build a custom payment flow that enables you to render the Payment Element, create a Subscription, and confirm the payment from the customer’s browser.

Set up Stripe
Server-side

First, create a Stripe account or sign in.

Use our official libraries to access the Stripe API from your application:

Command Line
Ruby
# Available as a gem sudo gem install stripe
Gemfile
Ruby
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Enable payment methods

Caution

This integration path doesn’t support BLIK or pre-authorized debits that use the Automated Clearing Settlement System (ACSS).

View your payment methods settings and enable the payment methods you want to support. You need at least one payment method enabled to create a PaymentIntent.

By default, Stripe enables cards and other prevalent payment methods that can help you reach more customers, but we recommend turning on additional payment methods that are relevant for your business and customers. See Payment method support for product and payment method support, and our pricing page for fees.

For Subscriptions, configure your invoice settings and supported payment methods. To prevent mismatches and errors, your invoice settings must match your Payment Element settings.

Collect payment details
Client-side

You’re ready to 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.

The Payment Element contains an iframe that securely sends payment information to Stripe over an HTTPS connection. Avoid placing the Payment Element within another iframe because some payment methods require redirecting to another page for payment confirmation.

The checkout page address must start with https:// rather than http:// for your integration to work. You can test your integration without using HTTPS, but remember to enable it when you’re ready to accept live payments.

Set up Stripe.js

The Payment Element is automatically available as a feature of Stripe.js. Include the Stripe.js script on your checkout page by adding it to the head of your HTML file. Always load Stripe.js directly from js.stripe.com to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself.

checkout.html
<head> <title>Checkout</title> <script src="https://js.stripe.com/v3/"></script> </head>

Create an instance of Stripe with the following JavaScript on your checkout page:

checkout.js
// Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
);

Add the Payment Element to your checkout page

The Payment Element needs a place to live on your checkout page. Create an empty DOM node (container) with a unique ID in your payment form:

checkout.html
<form id="payment-form"> <div id="payment-element"> <!-- Elements will create form elements here --> </div> <button id="submit">Submit</button> <div id="error-message"> <!-- Display error message to your customers here --> </div> </form>

When the form above has loaded, create an Elements instance with the mode, amount, and currency. These values determine which payment methods are shown to your customer.

Then, create an instance of the Payment Element and mount it to the container DOM node.

Note

The amount passed to the Payment Element should reflect what a customer will be charged immediately. This could either be the first installment of the subscription or 0 if the subscription has a trial period.

checkout.js
const options = { mode: 'subscription', amount: 1099, currency: 'usd', // Fully customizable with appearance API. appearance: {/*...*/}, }; // Set up Stripe.js and Elements to use in checkout form const elements = stripe.elements(options); // Create and mount the Payment Element const paymentElementOptions = { layout: 'accordion'}; const paymentElement = elements.create('payment', paymentElementOptions); paymentElement.mount('#payment-element');

The Payment Element renders a dynamic form that allows your customer to pick a payment method. The form automatically collects all necessary payments details for the payment method selected by the customer.

You can customize the Payment Element to match the design of your site by passing the appearance object into options when creating the Elements provider.

Collect addresses

By default, the Payment Element only collects the necessary billing address details. To collect a customer’s full billing address (to calculate the tax for digital goods and services, for example) or shipping address, use the Address Element.

OptionalCustomize the layout
Client-side

OptionalCustomize the appearance
Client-side

OptionalSave and retrieve customer payment methods

OptionalAdditional Elements options
Client-side

Create the pricing model
Stripe 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.

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: Standard pricing | 15 USD
  • Basic product: Basic service with minimum features

    • Price: Standard pricing | 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.

Create the customer
Client and Server

Stripe needs a customer for each subscription. In your application frontend, collect any necessary customer information and pass it to the backend.

If you need to collect address details, the Address Element enables you to collect a shipping or billing address for your customers. To learn more, see Address Element.

register.html
<form id="signup-form"> <label> Email <input id="email" type="text" placeholder="Email address" value="test@example.com" required /> </label> <button type="submit"> Register </button> </form>
register.js
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.

Command Line
cURL
curl https://api.stripe.com/v1/customers \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d email={{CUSTOMER_EMAIL}} \ -d name={{CUSTOMER_NAME}} \ -d "shipping[address][city]"=Brothers \ -d "shipping[address][country]"=US \ -d "shipping[address][line1]"="27 Fredrick Ave" \ -d "shipping[address][postal_code]"=97712 \ -d "shipping[address][state]"=CA \ -d "shipping[name]"={{CUSTOMER_NAME}} \ -d "address[city]"=Brothers \ -d "address[country]"=US \ -d "address[line1]"="27 Fredrick Ave" \ -d "address[postal_code]"=97712 \ -d "address[state]"=CA

Create the subscription
Server-side

When the customer submits your payment form, use a Subscription to facilitate the confirmation and payment process. To create a subscription, you need a Customer and a Price.

Note

If you’re using a multi-currency Price, use the currency parameter to tell the Subscription which of the Price’s currencies to use. (If you omit the currency parameter, then the Subscription uses the Price’s default currency.)

Included on a Subscription is a client secret. Return this value to your client for Stripe.js to use to securely complete the payment process. For subscriptions that don’t collect a payment up front (for example, subscriptions with a free trial period), use the client secret from the pending_setup_intent.

server.rb
Ruby
# 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'
post '/create-subscription' do content_type 'application/json' data = JSON.parse(request.body.read) customer_id = cookies[:customer] price_id = data['priceId'] subscription = Stripe::Subscription.create( customer: customer_id, items: [{ price: price_id, }], payment_behavior: 'default_incomplete', payment_settings: {save_default_payment_method: 'on_subscription'}, expand: ['latest_invoice.confirmation_secret', 'pending_setup_intent'] ) if subscription.pending_setup_intent != null { type: 'setup', clientSecret: subscription.pending_setup_intent.client_secret }.to_json else { type: 'payment', clientSecret: subscription.latest_invoice.confirmation_secret.client_secret }.to_json end end

Confirm the subscription
Client-side

Use stripe.confirmPayment or stripe.confirmSetup to confirm the subscription using details from the Payment Element. Indicate where Stripe should redirect the customer after confirmation by providing a return_url to the confirm function. With some payment methods, the customer is redirected to an intermediate site, like a bank authorization page, before being redirected to the return_url. You can also set redirect to if_required to only redirect customers that check out with redirect-based payment methods.

checkout.js
const form = document.getElementById('payment-form'); const submitBtn = document.getElementById('submit'); const handleError = (error) => { const messageContainer = document.querySelector('#error-message'); messageContainer.textContent = error.message; submitBtn.disabled = false; } form.addEventListener('submit', async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); // Prevent multiple form submissions if (submitBtn.disabled) { return; } // Disable form submission while loading submitBtn.disabled = true; // Trigger form validation and wallet collection const {error: submitError} = await elements.submit(); if (submitError) { handleError(submitError); return; } // Create the subscription const res = await fetch('/create-subscription', { method: "POST", }); const {type, clientSecret} = await res.json(); const confirmIntent = type === "setup" ? stripe.confirmSetup : stripe.confirmPayment; // Confirm the Intent using the details collected by the Payment Element const {error} = await confirmIntent({ elements, clientSecret, confirmParams: { return_url: 'https://example.com/order/123/complete', }, }); if (error) { // This point is only reached if there's an immediate error when confirming the Intent. // Show the error to your customer (for example, "payment details incomplete"). handleError(error); } else { // Your customer is redirected to your `return_url`. For some payment // methods like iDEAL, your customer is redirected to an intermediate // site first to authorize the payment, then redirected to the `return_url`. } });

Manage the subscription

To complete the integration, you may want to:

  • listen for webhooks
  • provision access to your service
  • allow customers to cancel their subscriptions

To learn more, see Build a subscription integration.

See also

  • Design an integration
Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc