Stripe | Financial Infrastructure to Grow Your Revenue

Stripe | Financial Infrastructure to Grow Your Revenue

5282 articles

Migrate to the Payment Element with the Checkout Sessions API


Migrate to the Payment Element with the Checkout Sessions API

Accept many payment methods with a single Element, while also managing taxes, shipping, discounts, currency conversion, and more.

Previously, each payment method (cards, iDEAL, and so on) required a separate Element. By migrating to the Payment Element, you can accept many payment methods with a single Element. You can use additional capabilities by migrating to Checkout Sessions from Payment Intents, which enables your integration to manage subscriptions, discounts, shipping, and currency conversion.

If you’re using the Card Element with PaymentIntents or SetupIntents, and only want to migrate to the Payment Element, see migrate to the Payment Element instead. You can also compare other payment integrations if neither fit your use case.

PaymentIntents and SetupIntents each have their own set of migration guidelines. See the appropriate guide for your integration path, including example code.

If your existing integration uses the Payment Intents API to create and track one-time payments or save card details during a payment, follow the steps below to use the Payment Element with Checkout Sessions.

Enable payment methods

Caution

This integration path doesn’t support pre-authorised debits that use the Automated Clearing Settlement System (the related setting). Also, if you create the deferred intent from the client-side, you can’t use customer_balance with dynamic payment methods because the PaymentIntent requires a customer-configured Account or Customer object, which the client-side flow doesn’t support. To use customer_balance, create the PaymentIntent server-side with an Account or Customer and return its client_secret to the client.

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 Checkout Session.

By default, Stripe enables cards and other common 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.

Migrate your PaymentIntent creation call Server-side

Upgrade your SDK to use the latest API version.

Command Line

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# Available as a gem
sudo gem install stripe

Gemfile

Select a language

Ruby

Python

PHP

Java

Node.js

Go

.NET

No results

# If you use bundler, you can add this line to your Gemfile
gem 'stripe'

Because the Payment Element allows you to accept multiple payment methods, we recommend using dynamic payment methods, which are automatically enabled if you don’t pass payment_method_types into the Checkout Session. When enabled, Stripe evaluates the currency, payment method restrictions, and other parameters to determine the list of payment methods available for your customers. We prioritise payment methods that increase conversion and are most relevant to the currency and location of the customer.

Update your PaymentIntent creation call to create a Checkout Session instead. In the Checkout Sessions instance, you’ll pass:

  • line _ items : Represents what’s in the order
  • ui _ mode: elements : Indicates that you’re using Elements
  • mode: payment : Indicates that you’ll accept one-off payments for the Checkout Session
  • return _ url : Represents the URL to redirect your customer back to after they authenticate or cancel their payment on the payment method’s app or site.

In addition, return the Checkout Session’s client_secret to the client-side to use later.

Each Checkout Session generates a PaymentIntent upon confirmation. If you want to retain any extra parameters from your current integration while creating a PaymentIntent, refer to the options available in payment_intent_data.

Before

After

Select a language

Ruby

Python

PHP

Node.js

Go

Java

.NET

No results

require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('sk_test_Ou1w6LVt3zmVipDVJsvMeQsc')

intent = client.v1.payment_intents.create({
 amount: 1099,
 currency: 'usd',
 payment_method_types: ['card'],
})

Select a language

Ruby

Python

PHP

Node.js

Go

Java

.NET

No results

require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('sk_test_Ou1w6LVt3zmVipDVJsvMeQsc')

session = client.v1.checkout.sessions.create({
 line_items: [
 {
 price_data: {
 currency: 'usd',
 product_data: {name: 'T-shirt'},
 unit_amount: 1099,
 },
 quantity: 1,
 },
 ],
 mode: 'payment',
 ui_mode: 'elements',
 return_url: '{{RETURN_URL}}',
})

{
 clientSecret: session.client_secret,
}.to_json

Optional Additional Checkout Session options Server-side

Migrate your Elements instance Client-side

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.

Ensure you’re on the latest Stripe.js version by including the following script tag <script src=“https://js.stripe.com/dahlia/stripe.js”></script>. Learn more about Stripe.js versioning and support policy.

checkout.html

Create clientSecret as a Promise<string> | string that contains the client secret returned by your server.

Replace your stripe.elements() call with stripe.initCheckoutElementsSdk, passing in clientSecret. initCheckoutElementsSdk returns a Checkout instance.

The Checkout object acts as the foundation of your checkout page, because it contains data from the Checkout Session and methods to update the Session.

Use the object returned by actions.getSession() as your reference for prices. We recommend reading and displaying the total and lineItems from the session in your UI.

This lets you turn on new features with minimal code changes. For example, adding manual currency prices requires no UI changes if you display the total.

Before

After

const stripe =
 Stripe('pk_test_GvF3BSyx8RSXMK5yAFhqEd3H');
const elements = stripe.elements();

checkout.js

const stripe =
 Stripe('pk_test_GvF3BSyx8RSXMK5yAFhqEd3H');

const clientSecret = fetch('/create-checkout-session', {method: 'POST'})
 .then((response) => response.json())
 .then((json) => json.checkoutSessionClientSecret);
const checkout = stripe.initCheckoutElementsSdk({clientSecret});
const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
 const session = loadActionsResult.actions.getSession();
 const checkoutContainer = document.getElementById('checkout-container');
 checkoutContainer.append(JSON.stringify(session.lineItems, null, 2));
 checkoutContainer.append(document.createElement('br'));
 checkoutContainer.append(`Total: ${session.total.total.amount}`);
}

index.html

<div id="checkout-container"></div>

Collect customer email Client-side

Migrating to Elements requires the additional step of collecting your Customer’s email.

You must provide a valid customer email when completing a Checkout Session.

Use the Contact Details Element to collect your customer’s email address. It handles email collection and validation for you, and helps customers sign in to Link.

Alternatively, you can:

  • Pass in customer_email , customer_account (for customers represented as customer-configured Account objects), or customer (for customers represented as Customer objects) when creating the Checkout Session. Stripe validates emails provided this way.
  • These prefill the session with an email that customers can’t edit on the checkout page. If you want to prefill with an editable email, use defaultValues.email when initialising Checkout.
  • Pass in an email you already validated on updateEmail or checkout.confirm .

checkout.html

<div id="contact-details-element">
 <!--Stripe.js injects the Contact Details Element-->
</div>

checkout.js

const contactDetailsElement = checkout.createContactDetailsElement();
contactDetailsElement.mount("#contact-details-element");

Add the Payment Element Client-side

You can now replace the Card Element and individual payment method Elements with the Payment Element. The Payment Element automatically adjusts to collect input fields based on the payment method and country (for example, full billing address collection for the related setting Direct Debit) so you don’t have to maintain customised input fields any more.

The following example replaces CardElement with PaymentElement:

checkout.html

<form id="payment-form">
 <div id="card-element">
 </div>
 <div id="payment-element">
 <!-- Mount the Payment Element here -->
 </div>
 <button id="submit">Submit</button>
</form>

checkout.js

const cardElement = elements.create("card");
cardElement.mount("#card-element");
const paymentElement = checkout.createPaymentElement();
paymentElement.mount("#payment-element");

Update the submit handler Client-side

Instead of using individual confirm methods like stripe.confirmCardPayment or stripe.confirmP24Payment, use actions.confirm to collect payment information and submit it to Stripe.

To confirm the Checkout Session, update your submit handler to use actions.confirm instead of individual confirm methods.

When called, actions.confirm attempts to complete any required actions, such as displaying a 3D Secure authentication dialog or redirecting them to a bank authorisation page. When confirmation is complete, customers redirect to the return_url you configured, which normally corresponds to a page on your website that provides the status of the payment.

If you want to keep the same checkout flow for card payments and only redirect for redirect-based payment methods, you can set redirect to if_required.

The following code example replaces stripe.confirmCardPayment with actions.confirm:

Before

After

// Create the PaymentIntent and obtain clientSecret
const res = await fetch("/create-intent", {
 method: "POST",
 headers: {"Content-Type": "application/json"},
});

const {client_secret: clientSecret} = await res.json();

const handleSubmit = async (event) => {
 event.preventDefault();

 if (!stripe) {
 // Stripe.js hasn't yet loaded.
 // Make sure to disable form submission until Stripe.js has loaded.
 return;
 }

 setLoading(true);

 const {error} = await stripe.confirmCardPayment(clientSecret, {
 payment_method: {
 card: elements.getElement(CardElement)
 }
 });

 if (error) {
 handleError(error);
 }
};
const handleSubmit = async (event) => {
 event.preventDefault();

 if (!stripe) {
 // Stripe.js hasn't yet loaded.
 // Make sure to disable form submission until Stripe.js has loaded.
 return;
 }

 setLoading(true);

 const {error} = await actions.confirm();

 if (error) {
 handleError(error);
 }
};

Optional Save payment details during a payment

Handle post-payment events Server-side

Stripe sends a checkout.session.completed event when the payment completes. Use the Dashboard webhook tool or follow the webhook guide to receive these events and run actions, such as sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events is what enables you to accept different types of payment methods with a single integration.

In addition to handling the checkout.session.completed event, we recommend handling these other events when collecting payments with the Payment Element:

EventDescriptionAction
checkout.session.completedSent when a customer successfully completes a payment.Send the customer an order confirmation and fulfill their order.
checkout_session.async_payment_succeededSent when payment by a customer using a delayed payment method finally succeeds.Send the customer an order confirmation and fulfill their order.
checkout.session.async_payment_failedSent when a customer attempts a payment, but the payment fails.If a payment transitions from async_payment_failed, offer the customer another attempt to pay.
checkout.session.expiredSent when a customer’s checkout session has expired, which is after 24 hours.If a payment transitions from expired to payment_failed, offer the customer an attempt to reload the checkout page and create a new checkout session.

Test the integration

  1. Navigate to your checkout page.
  2. Fill out the payment details with a payment method from the following table. For card payments:
  • Enter any future date for card expiry.
  • Enter any 3-digit number for CVC.
  • Enter any billing postal code.
  1. Submit the payment to Stripe.
  2. Go to the Dashboard and look for the payment on the Transactions page . If your payment succeeded, you’ll see it in that list.
  3. Click your payment to see more details, like billing information and the list of purchased items. You can use this information to fulfil the order .
Card numberScenarioHow to test
The card payment succeeds and doesn’t require authentication.Fill in the credit card form using the credit card number with any expiry date, CVC, and postal code.
The card payment requires authentication.Fill in the credit card form using the credit card number with any expiry date, CVC, and postal code.
The card is declined with a decline code like insufficient_funds.Fill in the credit card form using the credit card number with any expiry date, CVC, and postal code.
The UnionPay card has a variable length of 13-19 digits.Fill in the credit card form using the credit card number with any expiry date, CVC, and postal code.

See Testing for additional information to test your integration.

See also

Last verified 2026-09-25

Is this helpful?