Sign in
An image of the Stripe logo
Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
No-code
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Overview
Online payments
    Overview
    How cards work
    Quickstart
    Accept a payment
    Add funds to your balance
    Faster checkout with Link
      Accept a payment
      Alternative ways to integrate with Link
      Set up future Link payments
      Link in the Card Element
    More payment scenarios
    US and Canadian cards
Products and prices
Invoicing
Subscriptions
Quotes
In-person payments
Multiparty payments
After the payment
Add payment methods
Payment Links
Stripe Checkout
Stripe Elements
About the APIs
Regulation support
Implementation guides
Testing
Payments
·
HomePaymentsOnline paymentsFaster checkout with Link

Accept a payment with Link

Add the Link payment method to your custom payment form.

Country availability

See the list of countries and currencies that support Link.

This guide describes how to accept payments with Link using information your customer stores in the Link app. When you receive a payment from a customer using Link in the Payment Element, the payment_method.type listed for the payment is link.

To add Link to your Elements integration, you need to collect a customer’s email address so they can authenticate to Link. You can use the Link Authentication Element to collect email, which is recommended because it allows you to use a single email field for customer email collection and Link Authentication during payment.

If you previously collected email ahead of payment (in a previous account creation step for example), or if you prefer to use your own email input field, then you can pass that email into the Payment Element. In this scenario, a customer authenticates to Link directly in the payment form (versus in a separate UI component).

In addition to email, if you’ve previously collected other information from a customer, such as name or phone number, you can optionally pre-fill parts of the payment form to further streamline the checkout process and reduce manual data entry.

Payment form that includes the Link Authentication Element field alongside the Address Element, and Payment Element.

Use Elements to compose your checkout experience.

As Link expands to new countries, we’ll increase the number of funding sources customers can use to match local preferences.

Set up Stripe
Server-side

First, register for a Stripe account and then use the Dashboard to create an account name. You can always edit your account name later.

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

Command Line
# For detailed setup, see our quickstarts at https://stripe.com/docs/development/quickstart bundle add stripe

Create a PaymentIntent
Server-side

Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

An overview diagram of the entire payment flow

Create a PaymentIntent on your server with an amount and currency, and payment_method_types set to link and any other payment methods you want to accept. Always decide how much to charge on the server side, a trusted environment, instead of the client. This prevents malicious customers from choosing their own prices.

Command Line
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_4eC39HqLyjWDarjtT1zdp7dc
: \ -d amount=1099 \ -d currency=usd \ -d "payment_method_types[]"=link \ -d "payment_method_types[]"=card

Included in the returned PaymentIntent is a client secret, which the client side uses to securely complete the payment process instead of passing the entire PaymentIntent object. You can use different approaches to pass the client secret to the client side.

You can retrieve the client secret from an endpoint on your server using the browser’s fetch function on the client side. This approach is generally most suitable when your client side is a single-page application, particularly one built with a modern frontend framework such as React. The following example shows how to create the server endpoint that serves the client secret:

main.rb
get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end

This example demonstrates how to fetch the client secret with JavaScript on the client side:

(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Render the form using the clientSecret })();

Collect customer email

Link authenticates customer accounts using their email address. Use the Link Authentication Element, an email input Element, to both collect a customer’s email during the payment process and enable your customer to authenticate to Link.

If you previously collected email ahead of the payment process or prefer to use your own email input field, you can pass a customer’s email into the Payment Element. In this scenario, you only integrate the Payment Element, and a customer authenticates to Link directly in the payment form.

If any of the following apply to you:

  • You want a single, optimized component for email collection and Link authentication
  • You need to collect a shipping address from your customer

Then, use the integration flow that implements these elements: the Link Authentication Element, Payment Element and optional Address Element on your checkout page. A Link-enabled checkout page has the Link Authentication Element at the beginning, followed by the optional Address Element, and the Payment Element at the end. You can also display the Link Authentication Element on separate pages, in this same order, for multi-page checkout flows.

Preview of a checkout page that includes the Link Authentication Element, Address Element, and Payment Element.

Use combinations of Elements to compose your checkout page

The integration works as follows:

Set up your payment form
Client-side

Now you can set up your custom payment form with the Elements prebuilt UI components.

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

Set up Stripe Elements

Install React Stripe.js and the Stripe.js loader from the npm public registry.

Command Line
npm install --save @stripe/react-stripe-js @stripe/stripe-js

On your payment page, wrap your payment form with the Elements component, passing the client secret from the previous step. ​​If you already collect the customer’s email in another part of your form, replace your existing input with the linkAuthenticationElement​.

If you don’t collect email, add the linkAuthenticationElement​ to your checkout flow. You must place the linkAuthenticationElement before the ShippingAddressElement (optional if you collect shipping addresses) and the PaymentElement for Link to autofill Link-saved details for your customer in the ShippingAddressElement and PaymentElement.

You can also pass in the appearance option, customizing the Elements to match the design of your site.

If you have the customer’s email, pass it to the defaultValues option of the linkAuthenticationElement. This pre-fills their email address and initiates the Link authentication process.

If you have other customer information, pass it to the defaultValues.billingDetails object for the PaymentElement. Pre-filling as much information as possible simplifies Link account creation and reuse for your customers.

Then, render the linkAuthenticationElement and PaymentElement components in your payment form.

Checkout.js
import {loadStripe} from "@stripe/stripe-js"; import { Elements, linkAuthenticationElement, PaymentElement, } from "@stripe/react-stripe-js"; const stripe = loadStripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
); // Customize the appearance of Elements using the Appearance API. const appearance = {/* ... */}; // Enable the skeleton loader UI for the optimal loading experience. const loader = 'auto';

linkAuthenticationElement renders an email address input. When Link matches a customer email with an existing Link account, it sends the customer a secure, one-time code to their phone to authenticate. If the customer successfully authenticates, Stripe displays their Link-saved addresses and payment methods automatically for them to use.

The PaymentElement component renders a dynamic form that allows your customer to pick a payment method type. The form automatically collects all necessary payments details for the payment method type selected by the customer. The PaymentElement also handles the display of Link-saved payment methods for authenticated customers.

The linkAuthenticationElement, PaymentElement, and ShippingAddressElement don’t need to be on the same page. If you have a process where customer contact info, shipping info, and payment details display to the customer in separate steps, you can display each Element in the appropriate step or page. Include the linkAuthenticationElement as the email input form in the contact info collection step to make sure the customer can take full advantage of the shipping and payment autofill provided by Link.

If you collect your customer’s email with the Link Authentication Element early in the checkout flow, you don’t need to show it again on the shipping or payment pages.

Retrieving email address

You can retrieve the email address details using the onChange prop on the linkAuthenticationElement component. The onChange handler fires whenever the user updates the email field, or when a saved customer email is auto-filled.

<linkAuthenticationElement onChange={(event) => { setEmail(event.value.email); }} />

Pre-fill a customer email address

The Link Authentication Element accepts an email address. Providing a customer’s email address triggers the Link authentication flow as soon as the customer lands on the payment page using the defaultValues option.

<linkAuthenticationElement options={{defaultValues: {email: 'foo@bar.com'}}}/>

OptionalPre-fill additional customer data
Client-side

OptionalCollect shipping addresses
Client-side

OptionalCustomize the appearance
Client-side

Submit the payment to Stripe
Client-side

Use stripe.confirmPayment to complete the payment with details collected from your customer in the different Elements forms. Provide a return_url to this function to indicate where Stripe redirects the user after they complete the payment.

Your user might be first redirected to an intermediate site, like a bank authorization page, before Stripe redirects them to the return_url.

By default, card and bank payments immediately redirect to the return_url when a payment is successful. If you don’t want to redirect to the return_url, you can use if_required to change the behavior.

Checkout.js
import {loadStripe} from "@stripe/stripe-js"; import { useStripe, useElements, Elements, LinkAuthenticationElement, PaymentElement, // If collecting shipping AddressElement, } from "@stripe/react-stripe-js"; const stripe = loadStripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
); const appearance = {/* ... */}; // Enable the skeleton loader UI for the optimal loading experience. const loader = 'auto'; const CheckoutPage =({clientSecret}) => ( <Elements stripe={stripe} options={{clientSecret, appearance, loader}}> <CheckoutForm /> </Elements> ); export default function CheckoutForm() { const stripe = useStripe(); const elements = useElements(); const handleSubmit = async (event) => { event.preventDefault(); const {error} = await stripe.confirmPayment({ elements, confirmParams: { return_url: "https://example.com/order/123/complete", }, }); if (error) { // handle error } }; return ( <form onSubmit={handleSubmit}> <h3>Contact info</h3> <LinkAuthenticationElement /> {/* If collecting shipping */} <h3>Shipping</h3> <AddressElement options={{mode: 'shipping', allowedCountries: ['US']}} /> <h3>Payment</h3> <PaymentElement /> <button type="submit">Submit</button> </form> ); }

The return_url corresponds to a page on your website that provides the payment status of the PaymentIntent when you render the return page. When Stripe redirects the customer to the return_url, you can use the following URL query parameters to verify payment status. You can also append your own query parameters when providing the return_url. These query parameters persist through the redirect process.

ParameterDescription
payment_intentThe unique identifier for the PaymentIntent
payment_intent_client_secretThe client secret of the PaymentIntent object.

OptionalSeparate authorization and capture
Server-side

Handle post-payment events
Server-side

Stripe sends a payment_intent.succeeded event when the payment completes. Use a webhook to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

Configure your integration to listen for these events rather than waiting on a callback from the client. When you wait on a callback from the client, the customer can close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events enables you to accept different types of payment methods with a single integration.

In addition to handling the payment_intent.succeeded event, you can also handle two other important events when collecting payments with the Payment Element:

EventDescriptionAction
payment_intent.succeededSent from Stripe when a customer has successfully completed a payment.Send the customer an order confirmation and fulfill their order.
payment_intent.payment_failedSent from Stripe when a customer attempted a payment, but the payment didn’t succeed.If a payment transitioned from processing to payment_failed, offer the customer another attempt to pay.

Set a cookie to authenticate customers
Server-side

You can persist a Link session for your customers and automatically log them in to Link, without additional authentication. This works automatically for browsers that support persistent third-party local storage, such as Chrome and Firefox. For browsers that don’t, like Safari, you can enable persistent sessions in the three following steps:

  1. Retrieve a persistent_token from the PaymentIntent after an attempt to confirm it:
main.rb
# Your route for redirecting before showing the results of the payment get '/order/complete' do intent = Stripe::PaymentIntent.retrieve({ id: params[:payment_intent], expand: ['payment_method'], }) status = intent.status if status == 'succeeded' || status == 'processing' # If a valid Link session (created during the Link authentication flow # by the Payment Element code) is associated with the PaymentIntent, it # will be made available on the Payment Method object. link_persistent_token = intent.payment_method&.link&.persistent_token # If the payment was successful, show the customer a confirmation page
  1. To remember the customer across browser sessions, set a cookie with the persistent_token value with the HTTP response on the server side:
main.rb
LINK_PERSISTENT_TOKEN_COOKIE_NAME = 'stripe.link.persistent_token' get '/order/complete' do intent = Stripe::PaymentIntent.retrieve({ id: params[:payment_intent], expand: ['payment_method'], }) status = intent.status if status == 'succeeded' || status == 'processing' link_persistent_token = intent.payment_method&.link&.persistent_token if !link_persistent_token.nil? # Set the cookie from the value returned on the PaymentIntent. response.set_cookie(
  1. When you create the PaymentIntent, pass in the persistent_token value as an additional option:
main.rb
# Makes cookies accessible with cookies[:name]. require 'sinatra/cookies' LINK_PERSISTENT_TOKEN_COOKIE_NAME = 'stripe.link.persistent_token' get '/secret' do intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true} # The Link Persistent Token is attached to the PaymentIntent. payment_method_options: { link: { persistent_token: cookies[LINK_PERSISTENT_TOKEN_COOKIE_NAME], }, } }) {client_secret: intent.client_secret}.to_json end

You might need to update the PaymentIntent with the persistent_token value explicitly if you create the PaymentIntent before involving the customer in confirming the payment later. You might want to use this type of configuration for recurring charges where you contact customers to confirm their payment.

Privacy implications

You should always check with your legal counsel to understand how to comply with applicable legal obligations regarding cookies.

Based on your integration choice for Link, Stripe either provides a randomly generated value for you to store in a cookie on your domain (for example, on your checkout flow domain) or for storage on a local cache on the Stripe domain (which is also considered a cookie from a legal perspective). Stripe uses these cookies to help remember the end user on that browser for Link.

Make sure that your privacy policy tells your end users about this type of data collection, and also make sure to update your cookie banner accordingly after reviewing the cookies placed on your website. If your privacy policy doesn’t include this information, consider adding the following paragraph (or something similar) to it:

We use Stripe for payment, analytics, and other business services. Stripe collects identifying information about the devices that connect to its services, including via cookies. Stripe uses this information to operate and improve the services it provides to us, including for fraud detection. You can learn more about Stripe and read its privacy policy at https://stripe.com/privacy.

Test the integration

Don’t store real user data in test mode Link accounts. Treat them as if they’re publicly available, because these test accounts are associated with your publishable key.

Currently Link only works with credit cards, debit cards, and US bank accounts (for qualifying purchases). To give your customers more payment options at checkout, Stripe is working to add additional funding sources to Link. Periodically test your checkout flow to confirm the look and feel of your payments page is what you expect.

You can create test mode accounts for Link using any valid email address. The following table shows the fixed one-time passcode values that Stripe accepts for authenticating test mode accounts:

ValueOutcome
any other 6 digits not listed belowSuccess
000001Error, code invalid
000002Error, code expired
000003Error, max attempts exceeded

For testing specific payment methods, refer to the Payment Element testing examples.

Multiple funding sources

Currently, Link only works with credit cards, debit cards, and US bank accounts for qualifying purchases. But as Stripe adds additional funding sources, Link automatically supports them with instant confirmation for your company’s payments, and the same transaction settlement timeline and guarantees as cards and bank account-funded payments. This adaptive support means that you don’t need to update your integration to let your customers save and reuse their preferred payment method on your domain.

To test that your integration can handle additional funding sources, provide an email in the form of {any_prefix}+multiple_funding_sources@{any_domain} when you’re testing your check out flow with Link.

Card authentication and 3D Secure

Link supports 3D Secure (3DS) authentication for card payments. 3DS requires customers to complete an additional verification step with the card issuer when paying. Payments that have been successfully authenticated using 3D Secure are covered by a liability shift.

To trigger 3DS authentication challenge flows with Link in test mode, use the following test card with any CVC, postal code, and future expiration date:

In test mode, the authentication process displays a mock authentication page. On that page, you can either authorize or cancel the payment. Authorizing the payment simulates successful authentication and redirects you to the specified return URL. Clicking the Failure button simulates an unsuccessful attempt at authentication.

For more details, refer to the 3D Secure authentication page.

See also

  • Save payment methods with SetupIntents
  • Migrate from Charges to PaymentIntents
  • Migrate to the Payment Element
Was this page helpful?
Need help? Contact Support.
Watch our developer tutorials.
Check out our product changelog.
Questions? Contact Sales.
Powered by Markdoc
You can unsubscribe at any time. Read our privacy policy.
On this page
Set up Stripe
Create a PaymentIntent
Collect customer email
Set up your payment form
Pre-fill additional customer data
Collect shipping addresses
Customize the appearance
Submit the payment to Stripe
Separate authorization and capture
Handle post-payment events
Set a cookie to authenticate customers
Test the integration
See also
Stripe Shell
Test mode
Welcome to the Stripe Shell! Stripe Shell is a browser-based shell with the Stripe CLI pre-installed. Login to your Stripe account and press Control + Backtick on your keyboard to start managing your Stripe resources in test mode. - View supported Stripe commands: - Find webhook events: - Listen for webhook events: - Call Stripe APIs: stripe [api resource] [operation] (e.g. )
The Stripe Shell is best experienced on desktop.
$