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
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
Handle multiple currencies
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
Beyond payments
Incorporate your company
Crypto
Financial Connections
Climate
HomePaymentsCustom payment flows

Finalize payments on the server

Build an integration where you render the Payment Element before you create a PaymentIntent or SetupIntent, then confirm the Intent from your server.

A setup flow allows you to set up a payment method for future payments without immediately charging your customer. This integration builds a custom payment flow where you render the Payment Element, create the SetupIntent, and confirm the setup from your server.

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

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/basil/stripe.js"></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 ‘setup’. Then, create an instance of the Payment Element and mount it to the container DOM node.

checkout.js
const options = { mode: 'setup', currency: 'usd', paymentMethodCreation: 'manual', // 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 a Customer
Server-side

To set up a payment method for future payments, you must attach it to a Customer. Create a Customer object when your customer creates an account with your business. Customer objects allow for reusing payment methods and tracking across multiple payments.

Command Line
cURL
curl -X POST https://api.stripe.com/v1/customers \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"

Create the ConfirmationToken
Client-side

Use createPaymentMethod through a legacy implementation

If you’re using a legacy implementation, you might be using the information from stripe.createPaymentMethod to finalize payments on the server. While we encourage you to follow this guide to Migrate to Confirmation Tokens you can still access our old documentation to Finalize payments on the server

When the customer submits your payment form, call stripe.createConfirmationToken to create a ConfirmationToken to send to your server for additional validation or business logic before payment confirmation.

Confirming the SetupIntent generates a PaymentMethod. You can read the payment_method ID off the SetupIntent confirmation response.

Caution

You must immediately use the created ConfirmationToken to confirm a SetupIntent ; if unused, it expires after 12 hours.

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 ConfirmationToken using the details collected by the Payment Element // and additional shipping information const {error, confirmationToken} = await stripe.createConfirmationToken({ elements, params: { return_url: 'https://example.com/order/123/complete' } }); if (error) { // This point is only reached if there's an immediate error when // creating the ConfirmationToken. Show the error to your customer (for example, payment details incomplete) handleError(error); return; } // Create the SetupIntent const res = await fetch("/create-confirm-intent", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ confirmationTokenId: confirmationToken.id, }), }); const data = await res.json(); // Handle any next actions or errors. See the Handle any next actions step for implementation. handleServerResponse(data); });

Submit the payment details to Stripe
Server-side

When the customer submits your payment form, create a SetupIntent on your server. You can manage payment methods from the Dashboard. Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow.

You can use the PaymentMethod sent by your client to create and confirm the SetupIntent in a single request.

app.js
const stripe = require('stripe')(
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.use(express.static(".")); app.post('/create-confirm-intent', async (req, res) => { try { const intent = await stripe.setupIntents.create({ confirm: true, customer: req.cookies['customer'], // the Customer ID you previously created // In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. automatic_payment_methods: {enabled: true}, confirmation_token: req.body.confirmationTokenId }); res.json({ client_secret: intent.client_secret, status: intent.status }); } catch (err) { res.json({ error: err }) } }); app.listen(3000, () => { console.log('Running on port 3000'); });

Handle any next actions
Client-side

When the SetupIntent requires additional action from the customer, such as authenticating with 3D Secure or redirecting to a different site, you need to trigger those actions. Use stripe.handleNextAction to trigger the UI for handling customer action and completing the setup.

checkout.js
JavaScript
const handleServerResponse = async (response) => { if (response.error) { // Show error from server in payment setup form } else if (response.status === "requires_action") { // Use Stripe.js to handle the required next action const { error, setupIntent } = await stripe.handleNextAction({ clientSecret: response.clientSecret }); if (error) { // Show error from Stripe.js in payment setup form } else { // Actions handled, show success message } } else { // No actions needed, show success message } }

Charge the saved payment method later
Server-side

Warning

bancontact and ideal are one-time payment methods by default. When set up for future usage, they generate a sepa_debit reusable payment method type, so you need to use sepa_debit to query for saved payment methods.

Compliance

You’re responsible for your compliance with all applicable laws, regulations, and network rules when saving a customer’s payment details. When rendering past payment methods to your end customer for future purchases, make sure you’re listing payment methods where you’ve collected consent from the customer to save the payment method details for this specific future use. To differentiate between payment methods attached to customers that can and can’t be presented to your end customer as a saved payment method for future purchases, use the allow_redisplay parameter.

When you’re ready to charge your customer off-session, use the Customer and PaymentMethod IDs to create a PaymentIntent. To find a payment method to charge, list the payment methods associated with your customer. This example lists cards but you can list any supported type.

Command Line
cURL
curl -G https://api.stripe.com/v1/payment_methods \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d customer=
{{CUSTOMER_ID}}
\ -d type=card

When you have the Customer and PaymentMethod IDs, create a PaymentIntent with the amount and currency of the payment. Set a few other parameters to make the off-session payment:

  • Set off_session to true to indicate that the customer isn’t in your checkout flow during a payment attempt and can’t fulfill an authentication request made by a partner, such as a card issuer, bank, or other payment institution. If, during your checkout flow, a partner requests authentication, Stripe requests exemptions using customer information from a previous on-session transaction. If the conditions for exemption aren’t met, the PaymentIntent might throw an error.
  • Set the value of the PaymentIntent’s confirm property to true, which causes confirmation to occur immediately when the PaymentIntent is created.
  • Set payment_method to the ID of the PaymentMethod and customer to the ID of the Customer.
Command Line
curl
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -d amount=1099 \ -d currency=usd \ # In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. -d "automatic_payment_methods[enabled]"=true \ -d customer="{{CUSTOMER_ID}}" \ -d payment_method="{{PAYMENT_METHOD_ID}}" \ -d return_url="https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true

When a payment attempt fails, the request also fails with a 402 HTTP status code and the status of the PaymentIntent is requires_payment_method. You must notify your customer to return to your application to complete the payment (for example, by sending an email or in-app notification).

Check the code of the error raised by the Stripe API library. If the payment failed due to an authentication_required decline code, use the declined PaymentIntent’s client secret with confirmPayment to allow the customer to authenticate the payment.

checkout.js
const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); const {error} = await stripe.confirmPayment({ // The client secret of the PaymentIntent clientSecret, confirmParams: { return_url: 'https://example.com/order/123/complete', }, }); if (error) { // This point will only be reached if there is an immediate error when // confirming the payment. Show error to your customer (for example, payment // details incomplete) const messageContainer = document.querySelector('#error-message'); messageContainer.textContent = error.message; } else { // Your customer will be redirected to your `return_url`. For some payment // methods like iDEAL, your customer will be redirected to an intermediate // site first to authorize the payment, then redirected to the `return_url`. } });

Note

stripe.confirmPayment can take several seconds to complete. During that time, disable your form from being resubmitted and show a waiting indicator like a spinner. If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator. If the customer must perform additional steps to complete the payment, such as authentication, Stripe.js walks them through that process.

If the payment failed for other reasons, such as insufficient funds, send your customer to a payment page to enter a new payment method. You can reuse the existing PaymentIntent to attempt the payment again with the new payment details.

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