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
    Overview
    Payment Element
      Payment Element best practices
      Card Element comparison
      Migrate to the Payment Element with Payment Intents
      Migrate to the Payment Element with Checkout Sessions
      Migrate to Confirmation Tokens
    Express Checkout Element
    Address Element
    Currency Selector Element
    Link Authentication Element
    Payment Method Messaging Element
In-app Elements
Payment scenarios
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Other Stripe products
Financial Connections
Crypto
Climate
HomePaymentsWeb ElementsPayment Element

Migrate to the Payment Element with the Payment Intents API

Accept many payment methods with a single Element.

Copy page

Note

If your integration still uses the Charges API with tokens, follow the Migrating to the Payment Intents API guide first.

Interested in using Stripe Billing, Tax, discounts, shipping, or currency conversion?

We’re developing a Payment Element integration that manages subscriptions, tax, discounts, shipping, and currency conversion. To learn more, see Build a checkout page.

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.

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 Setup Intents API for future payments, follow the steps below to use the Payment Element.

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 SetupIntent.

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.

Update Elements instance
Client-side

Next, update your client-side code to pass the mode and currency when you create the Elements instance. For use with a SetupIntent, set the mode to 'setup' and the currency to what you’ll charge your customer in the future.

Before
After
const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
); const elements = stripe.elements();
const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
); const options = { mode: 'setup', currency: 'usd', }; const elements = stripe.elements(options);

OptionalAdditional Elements options
Client-side

Add the Payment Element
Client-side

If you’re using React Stripe.js, update to the latest package to use the Payment Element.

You can now replace the Card Element and individual payment methods 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 SEPA Direct Debit) so you don’t have to maintain customized input fields anymore.

The following example replaces CardElement with PaymentElement:

checkout.html
JavaScript
<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
JavaScript
const cardElement = elements.create("card"); cardElement.mount("#card-element"); const paymentElement = elements.create("payment"); paymentElement.mount("#payment-element");

If your payment flow already always collects details like the customer’s name or email address, you can prevent the Payment Element from collecting this information by passing the fields option when creating the Payment Element. If you disable the collection of a certain field, you must pass that same data back with stripe.confirmSetup.

Update your SetupIntent creation call
Server-side

Because the Payment Element allows you to accept multiple payment methods, we recommend using automatic_payment_methods. When you enable it, Stripe evaluates the currency, payment method restrictions, and other parameters to determine the list of payment methods available for your customers. We prioritize payment methods that increase conversion and are most relevant to the currency and location of the customer.

Add the automatic_payment_methods attribute to the endpoint on your server that creates the SetupIntent.

Any of the additional elements options passed when creating the Elements group in the earlier step should also be passed when creating the SetupIntent.

Command Line
curl
curl https://api.stripe.com/v1/setup_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -H "Stripe-Version: 2025-05-28.basil" \ -d "customer"="{{CUSTOMER_ID}}" \ -d "automatic_payment_methods[enabled]"=true

Caution

You can’t save some payment methods for future payments. For more information, see Payment method integration options.

Update the submit handler
Client-side

Instead of using individual confirm methods like stripe.confirmCardSetup or stripe.confirmP24Setup, use stripe.confirmSetup to collect payment information and submit it to Stripe.

To confirm the SetupIntent, make the following updates to your submit handler:

  • Call await elements.submit() to trigger form validation and collect any data required for wallets.
  • Optional: Move SetupIntent creation to the submit handler. This way you only create the SetupIntent when the user is ready to submit their payment method details.
  • Pass the elements instance you used to create the Payment Element and the clientSecret from the SetupIntent as parameters to stripe.confirmSetup.

When called, stripe.confirmSetup attempts to complete any required actions, such as authenticating your customers by displaying a 3DS dialog or redirecting them to a bank authorization page. When confirmation is complete, users are directed to the return_url you configured, which normally corresponds to a page on your website that provides the status of the SetupIntent.

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

The following code examples replaces stripe.confirmCardSetup with stripe.confirmSetup:

Before
After
// Create the SetupIntent 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.confirmCardSetup(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); // Trigger form validation and wallet collection const {error: submitError} = await elements.submit(); if (submitError) { handleError(submitError); return; } // Create the SetupIntent and obtain clientSecret const res = await fetch("/create-intent", { method: "POST", headers: {"Content-Type": "application/json"}, }); const {client_secret: clientSecret} = await res.json(); // Use the clientSecret and Elements instance to confirm the setup const {error} = await stripe.confirmSetup({ elements, clientSecret, confirmParams: { return_url: 'https://example.com/order/123/complete', }, // Uncomment below if you only want redirect for redirect-based payments // redirect: "if_required", }); if (error) { handleError(error); } };

Test the integration

Use test payment details and the test redirect page to verify your integration. Click the tabs below to view details for each payment method.

Payment methodScenarioHow to test
Credit cardThe card setup 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 requires authentication for the initial setup, then succeeds for subsequent payments.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 requires authentication for the initial setup and also requires authentication for subsequent payments.Fill out the credit card form using the credit card number 4000 0027 6000 3184 with any expiration, CVC, and postal code.
Credit cardThe card is declined during setup.Fill out the credit card form using the credit card number 4000 0000 0000 9995 with any expiration, CVC, and postal code.

Test charging a saved SEPA Debit PaymentMethod

Confirming the SetupIntent using iDEAL, Bancontact, or Sofort, generates a SEPA Direct Debit PaymentMethod. SEPA Direct Debit is a delayed notification payment method that transitions to an intermediate processing state before transitioning several days later to a succeeded or requires_payment_method state.

Set payment_method.billing_details.email to one of the following values to test the PaymentIntent status transitions. You can include your own custom text at the beginning of the email address followed by an underscore. For example, test_1_generatedSepaDebitIntentsFail@example.com results in a SEPA Direct Debit PaymentMethod that always fails when used with a PaymentIntent.

Email AddressDescription
generatedSepaDebitIntentsSucceed@example.comThe PaymentIntent status transitions from processing to succeeded.
generatedSepaDebitIntentsSucceedDelayed@example.comThe PaymentIntent status transitions from processing to succeeded after at least three minutes.
generatedSepaDebitIntentsFail@example.comThe PaymentIntent status transitions from processing to requires_payment_method.
generatedSepaDebitIntentsFailDelayed@example.comThe PaymentIntent status transitions from processing to requires_payment_method after at least three minutes.
generatedSepaDebitIntentsSucceedDisputed@example.comThe PaymentIntent status transitions from processing to succeeded, but a dispute is created immediately.
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