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

You can also build and preview your embedded Checkout form through [Checkout studio](https://docs.stripe.com/payments/checkout-studio.md), which provides a centralized Dashboard page for configuring and monitoring your Checkout integrations.

### Build an integration with an embedded form

We're developing an embeddable form that allows you to build a complete checkout UI with a single integration. If you want to join the preview, enter your email address below.

```bash
curl https://docs.stripe.com/preview/register \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Referer: https://docs.stripe.com/checkout/form/quickstart" \
  -d '{"email": "EMAIL", "preview": "elements_betas_preview"}'
```

## 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 "<<YOUR_SECRET_KEY>>:" \
  -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}} \
  -d integration_identifier=embedded_web_0001
```

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.

The Checkout Sessions API supports an optional `integration_identifier` parameter you can set when creating a session. Use it to label this form integration with a recognizable name and measure its conversion independently from other Checkout integrations you run.

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
- [automatic_tax[address_collection_precision]](https://docs.stripe.com/payments/checkout/taxes.md?payment-ui=checkout-form): Control billing address collection in embedded forms for tax calculation when automatic tax is enabled
- [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
- [allow_promotion_codes](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-allow_promotion_codes): Allow customer-redeemable promotion codes

> #### Unsupported CheckoutSession parameters
> 
> The embedded form doesn’t support `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> | 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(
  '<<YOUR_PUBLISHABLE_KEY>>',
  {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 (
    <CheckoutFormProvider
      stripe={stripePromise}
      options={{clientSecret}}
    >
      <CheckoutForm />
    </CheckoutFormProvider>
  );
};

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
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/dahlia/stripe.js"></script>
</head>
```

Create an instance of Stripe on your checkout page:

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>', {
  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
<div id="checkout-form">
  <!-- embedded form will be inserted here -->
</div>
```

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
<CheckoutFormProvider
  stripe={stripePromise}
  options={{
    clientSecret,
    defaultValues: {
      email: 'customer@example.com',
    },
  }}
>
  <CheckoutForm />
</CheckoutFormProvider>
```

#### 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(
  '<<YOUR_PUBLISHABLE_KEY>>',
  {betas: ['custom_checkout_payment_form_1']}
);

const CheckoutPage = () => {
  const checkoutState = useCheckoutForm();

  if (checkoutState.type === 'error') {
    return <div>Error: {checkoutState.error.message}</div>;
  }

  const onConfirm = (event) => {
    if (checkoutState.type === 'success') {
       checkoutState.checkout.confirm({formConfirmEvent: event});
    }
  };

  return <CheckoutForm onConfirm={onConfirm} />;
};

const App = () => {
  const clientSecret = useMemo(() => (
    fetch('/create-checkout-session', {method: 'POST'})
      .then((response) => response.json())
      .then((json) => json.client_secret)
  ), []);

  return (
    <CheckoutFormProvider
      stripe={stripePromise}
      options={{clientSecret}}
    >
      <CheckoutPage />
    </CheckoutFormProvider>
  );
};

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
const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
  form.on('confirm', async (event) => {
    try {
      await loadActionsResult.actions.confirm({formConfirmEvent: event});
    } catch (error) {
      console.error('Payment confirmation error:', error);
    }
  });
}
```

> #### Customize 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](https://docs.stripe.com/js/custom_checkout/confirm#custom_checkout_session_confirm-options-redirect) to `'if_required'` when calling [confirm](https://docs.stripe.com/js/custom_checkout/confirm). 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 Apple Wallet.
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'
    }
  }
};

<CheckoutForm
  options={checkoutFormOptions}
  onConfirm={onConfirm}
/>
```

#### 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' }
};

<CheckoutFormProvider
  stripe={stripePromise}
  options={{
    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
  }}
>
  <CheckoutForm />
</CheckoutFormProvider>
```

#### 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)
