# Build an integration with an embedded form The following guide describes how to use the embedded form with *Checkout Sessions* (A Checkout Session represents your customer's session as they pay for one-time purchases or subscriptions through Checkout. After a successful payment, the Checkout Session contains a reference to the Customer, and either the successful PaymentIntent or an active Subscription). ## Set up Stripe [Server-side] First, [create a Stripe account](https://dashboard.stripe.com/register) or [sign in](https://dashboard.stripe.com/login). Coding agents should install the Stripe CLI (`npm i -g @stripe/cli`) and run the command `stripe sandbox create --help` to provision an anonymous Stripe sandbox with working API keys. No account registration required. Use our official libraries to access the Stripe API from your application: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Enable payment methods By default, Stripe uses your [payment methods settings](https://dashboard.stripe.com/settings/payment_methods) to determine which payment methods the embedded form presents. You can also configure specific payment methods on your Checkout Session using the [payment_method_types](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-payment_method_types) attribute. ## Create a Checkout Session [Server-side] Create a Checkout Session on your server to control the payment flow. The Checkout Session defines your line items, shipping options, and other settings for the payment. ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d "line_items[0][price]={{PRICE_ID}}" \ -d "line_items[0][quantity]=1" \ -d mode=payment \ -d ui_mode=form \ -d return_url={{RETURN_URL}} ``` Set `ui_mode` to `form` to integrate with the embedded form. The returned `CheckoutSession` object includes a client secret, which the client uses to securely display the checkout interface. You can also configure the following options on the [CheckoutSession](https://docs.stripe.com/api/checkout/sessions/create.md): - [automatic_tax](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-automatic_tax): Enable automatic tax calculation - [billing_address_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-billing_address_collection): Collect billing addresses - [customer_email](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-customer_email): Prefill the customer’s email address - [customer](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-customer): Prefill customer data from an existing `Customer` object - [name_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-name_collection): Collect your customers’ business name, individual name, or both - [shipping_address_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_address_collection): Collect shipping addresses - [shipping_options](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_options): Provide shipping rate options - [submit_type](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-submit_type): Specify the type of transaction being performed - [phone_number_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-phone_number_collection): Collect your customer’s phone number - [tax_id_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-tax_id_collection): Collect tax IDs > #### Unsupported CheckoutSession parameters > > The embedded form doesn’t support `allow_promotion_codes` or `consent_collection`. ## Set up Stripe Elements [Client-side] #### React The embedded form is available as a feature of Stripe.js. Install [React Stripe.js](https://www.npmjs.com/package/@stripe/react-stripe-js) and the [Stripe.js loader](https://www.npmjs.com/package/@stripe/stripe-js) from the npm public registry. ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` Create `clientSecret` as a `Promise | string` containing the client secret returned by your server. Wrap your application with the [CheckoutFormProvider](https://docs.stripe.com/js/react_stripe_js/checkout_form/checkout_form_provider) component, passing in `clientSecret` and the `stripe` instance. ```jsx import React, {useMemo} from 'react'; import {CheckoutFormProvider, CheckoutForm} from '@stripe/react-stripe-js/checkout'; import {loadStripe} from '@stripe/stripe-js'; const stripePromise = loadStripe( '<>', {betas: ['custom_checkout_payment_form_1']} ); const App = () => { const clientSecret = useMemo(() => ( fetch('/create-checkout-session', {method: 'POST'}) .then((response) => response.json()) .then((json) => json.client_secret) ), []); return ( ); }; export default App; ``` #### HTML + JS The embedded form 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. ```html Checkout ``` Create an instance of Stripe on your checkout page: ```javascript const stripe = Stripe('<>', { betas: ['custom_checkout_payment_form_1'] }); ``` Fetch the client secret from your server and initialize the Checkout Form SDK: ```javascript const clientSecret = fetch('/create-checkout-session', {method: 'POST'}) .then((response) => response.json()) .then((json) => json.client_secret); ``` Create the Checkout Form SDK instance: ```javascript const checkout = stripe.initCheckoutFormSdk({ clientSecret }); ``` ## Create and mount [Client-side] The embedded form contains an iframe that securely sends the payment information to Stripe over an HTTPS connection. #### React The `CheckoutForm` component is already rendered inside `CheckoutFormProvider` in the previous step. No additional mounting is required for React. #### HTML + JS The embedded form needs a designated place on your payment page. Create an empty DOM node (container) with a unique ID in your payment form. ```html
``` When the Checkout Form SDK instance is ready, create the embedded form and mount it to the container DOM node: ```javascript const form = checkout.createForm(); form.mount('#checkout-form'); ``` You can specify the [layout](https://docs.stripe.com/js/custom_checkout/create_form#custom_checkout_create_form-options-layout) to render the embedded form as a single-step or multi-step embedded form. ## Prefill customer email You can prefill the customer’s email address using one of two approaches, depending on whether you want the email to be editable. ### Set the email on the Checkout Session (non-editable) Pass [customer_email](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-customer_email) when creating the Checkout Session on your server. The email is displayed in the embedded form but the customer can’t change it. You can also pass a [Customer](https://docs.stripe.com/api/customers.md) ID to the [customer](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-customer) field to prefill the email stored on the Customer. ### Set a default email on the client (editable) Pass `defaultValues.email` when initializing the SDK to prefill an editable email. The customer can modify this value in the embedded form. #### React ```jsx ``` #### HTML + JS ```javascript const checkout = stripe.initCheckoutFormSdk({ clientSecret, defaultValues: { email: 'customer@example.com', }, }); ``` ## Finalize payment [Client-side] The Checkout Session you created on the server automatically determines the line items, total amount, and available payment methods. The embedded form uses this information to display the appropriate interface. #### React ### Handle payment confirmation Handle the confirm event when your customer finalizes their payment: ```jsx import React, {useMemo} from 'react'; import {CheckoutFormProvider, CheckoutForm, useCheckoutForm} from '@stripe/react-stripe-js/checkout'; import {loadStripe} from '@stripe/stripe-js'; const stripePromise = loadStripe( '<>', {betas: ['custom_checkout_payment_form_1']} ); const CheckoutPage = () => { const checkoutState = useCheckoutForm(); if (checkoutState.type === 'error') { return
Error: {checkoutState.error.message}
; } const onConfirm = (event) => { if (checkoutState.type === 'success') { checkoutState.checkout.confirm({formConfirmEvent: event}); } }; return ; }; const App = () => { const clientSecret = useMemo(() => ( fetch('/create-checkout-session', {method: 'POST'}) .then((response) => response.json()) .then((json) => json.client_secret) ), []); return ( ); }; export default App; ``` ### Handle errors The embedded form automatically shows localized customer-facing error messages during client confirmation. If a problem prevents the confirm method from continuing, the confirm method can raise an exception. Catch and handle those exceptions. ```jsx const onConfirm = async (event) => { if (checkoutState.type === 'success') { try { await checkoutState.checkout.confirm({formConfirmEvent: event}); } catch (error) { console.error('Payment confirmation error:', error); } } }; ``` #### HTML + JS ### Handle payment confirmation Listen to the confirm event when your customer finalizes their payment: ```javascript const loadActionsResult = await checkout.loadActions(); if (loadActionsResult.type === 'success') { form.on('confirm', (event) => { loadActionsResult.actions.confirm({formConfirmEvent: event}); }); } ``` The embedded form automatically shows localized customer-facing error messages during client confirmation. If an immediate problem prevents the confirm method from continuing, the confirm method can raise an exception. Catch and handle those exceptions. ```javascript form.on('confirm', async (event) => { try { await checkout.confirm({formConfirmEvent: event}); } catch (error) { console.error('Payment confirmation error:', error); } }); ``` > #### Cutsomize redirect behavior > > By default, after a successful payment, the embedded form redirects your customer to the `return_url` that you specify when you create the Checkout Session. To prevent redirects for payment methods that don’t require a redirect, such as cards, set [redirect_on_completion](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-redirect_on_completion) to `if_required` on the Checkout Session. To learn more, see [Customize redirect behavior](https://docs.stripe.com/payments/checkout/custom-success-page.md?payment-ui=checkout-form). ## Handle post-payment events [Server-side] The Checkout Session defines your line items, shipping options, and other settings for the payment. After a customer confirms payment on the client, handle the result on your server rather than relying on client-side state. - **Webhooks** (Recommended): Listen for the [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed) event, which Stripe sends when the payment succeeds. This approach ensures you’re notified even if the customer closes their browser before the redirect. - **Return page**: [Retrieve the Checkout Session](https://docs.stripe.com/api/checkout/sessions/retrieve.md) using its ID to check the payment status and show the appropriate result to your customer on the page you set as the `return_url`. Learn how to [fulfill orders](https://docs.stripe.com/checkout/fulfillment.md?payment-ui=checkout-form) after receiving a payment. ## Test the integration Before you go live, [test](https://docs.stripe.com/testing.md) each payment method integration. Click **Pay** to complete the payment, which redirects you to the specified return page. If you see the return page, and the payment appears in the list of successful payments in the Dashboard, your integration is working. #### Cards Use Stripe’s [test card numbers](https://docs.stripe.com/testing.md#cards) to test card payments. For example: | Scenario | Card Number | | ----------------------------------- | ---------------- | | Payment succeeds | 4242424242424242 | | Payment requires 3DS authentication | 4000002500003155 | | Payment is declined | 4000000000009995 | #### One-click payment buttons Follow these steps to test one-click payment buttons: 1. Add a payment method to your browser. For example, you can add a card to your Google Pay account or to your Wallet for Safari. 2. Serve your application over HTTPS. This is required in development and in production. You can use a service such as [ngrok](https://ngrok.com/). 3. [Register your domain](https://dashboard.stripe.com/settings/payment_method_domains) in both a *sandbox* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes) and live mode. 4. Follow the instructions in the [Express Checkout Element testing guide](https://docs.stripe.com/elements/express-checkout-element/accept-a-payment.md?payment-ui=embedded-components#test-integration). ## Customize the layout and appearance You can customize the embedded form by setting its options or by using the Appearance API. ### Embedded form options You can configure the overall layout of the embedded form and style the appearance of each express payment method button using options. #### React ```jsx const checkoutFormOptions = { layout: 'expanded', expressCheckout: { buttonTheme: { applePay: 'white-outline' }, paymentMethods: { applePay: 'always' } } }; ``` #### HTML + JS ```javascript const form = checkout.createForm({ layout: 'expanded', expressCheckout: { buttonTheme: { applePay: 'white-outline' }, paymentMethods: { applePay: 'always' } } }); form.mount('#checkout-form'); ``` The embedded form supports the following options: | | | | | `layout` | Layout for the embedded form. Options include `expanded` and `compact`. When you leave this undefined, Stripe renders the layout it determines has the best conversion. | | `expressCheckout.buttonTheme` | Specify a theme per one-click payment button. See [buttonTheme](https://docs.stripe.com/js/custom_checkout/create_form#custom_checkout_create_form-options-expressCheckout-buttonTheme). | | `expressCheckout.paymentMethods` | Specify which one-click payment buttons show. See [paymentMethods](https://docs.stripe.com/js/custom_checkout/create_form#custom_checkout_create_form-options-expressCheckout-paymentMethods). | | `contacts` | An array of objects representing saved addresses, each containing `name`, `address`, and `phone` properties. See [contacts](https://docs.stripe.com/js/custom_checkout/create_form#custom_checkout_create_form-options-contacts). | ### Appearance API You can use the [Appearance API](https://docs.stripe.com/elements/appearance-api.md) to control the style of the embedded form by applying a theme or updating specific details. However, the embedded form doesn’t support the `rules` option. For instance, choose the “flat” theme and override the primary text color. #### React ```jsx const appearance = { theme: 'flat', variables: { colorPrimaryText: '#262626' } }; ``` #### HTML + JS ```javascript const appearance = { theme: 'flat', variables: { colorPrimaryText: '#262626' } }; const checkout = stripe.initCheckoutFormSdk({ clientSecret, // In a working integration, your back end passes this hash containing details such as the amount of a payment. See the full sample for details. appearance }); ``` ## Disclose Stripe to your customers Stripe collects information on customer interactions with Elements to provide services to you, prevent fraud, and improve its services. This includes using cookies and IP addresses to identify which Elements a customer saw during a single checkout session. You’re responsible for disclosing and obtaining all rights and consents necessary for Stripe to use data in these ways. For more information, visit our [privacy center](https://stripe.com/legal/privacy-center#as-a-business-user-what-notice-do-i-provide-to-my-end-customers-about-stripe). ## See also - [Save a customer’s payment method when they use it for a payment](https://docs.stripe.com/payments/save-during-payment.md?payment-ui=embedded-components)