# Migrate to the Express Checkout Element with Checkout Sessions

Migrate your existing payment request button element integration to the Express Checkout Element and the Checkout Sessions API.

The [payment request button element](https://docs.stripe.com/stripe-js/elements/payment-request-button.md) lets you accept card payments through [Apple Pay](https://docs.stripe.com/apple-pay.md), [Google Pay](https://docs.stripe.com/google-pay.md), and [Link](https://docs.stripe.com/payments/link.md). When you migrate to the [express checkout element](https://docs.stripe.com/elements/express-checkout-element.md), you can also accept payments through [PayPal](https://docs.stripe.com/payments/paypal.md), [Amazon Pay](https://docs.stripe.com/payments/amazon-pay.md), and [Klarna](https://docs.stripe.com/payments/klarna.md), and display multiple payment buttons at the same time.

This guide shows how to migrate to the Express Checkout Element with the [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md). If your existing integration uses the [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) and you want to keep using it, see [Migrate to the Express Checkout Element](https://docs.stripe.com/elements/express-checkout-element/migration.md). If your existing integration uses the [Charges API](https://docs.stripe.com/api/charges.md) with tokens, migrate to the Checkout Sessions API before proceeding.

## Enable payment methods

Enable the payment methods you want to support in your [payment methods settings](https://dashboard.stripe.com/settings/payment_methods). You must enable at least one payment method.

By default, Stripe enables cards and other common payment methods. You can enable additional payment methods that are relevant for your business and customers. See [Payment method support](https://docs.stripe.com/payments/payment-methods/payment-method-support.md) for product and payment method support and our [pricing page](https://stripe.com/pricing/local-payment-methods) for fees.

## Create a Checkout Session [Server-side]

With the payment request button element, you create a `paymentRequest` object in your client-side code and provide the total, currency, and customer-detail requests.

With the Checkout Sessions API, create a Checkout Session on your server instead. The Checkout Session contains the amount, line items, eligible payment methods, and checkout configuration.

Create the Checkout Session with `ui_mode: 'elements'`, then send its `client_secret` to your client.

```javascript
const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  ui_mode: 'elements',
  line_items: [{
    price: 'price_123',
    quantity: 1,
  }],
  return_url: 'https://example.com/checkout/return',
});

res.json({clientSecret: session.client_secret});
```

With the payment request button element, your client-side `paymentRequest` object defines the payment amount, currency, requested customer details, and shipping options. With Checkout Sessions, configure these values when you create or update the Checkout Session on your server.

| Payment request button configuration | Checkout Sessions replacement |
| --- | --- |
| `total` | Configure your order with Checkout Session [line_items](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-line_items). |
| `currency` | Configure currency through `line_items.price` or `line_items.price_data.currency` in the Checkout Session. |
| `country` | Configure your business and payment method settings in the Stripe Dashboard. |
| `requestPayerName` | [name_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-name_collection): `{individual: {enabled: true}}` |
| `requestPayerEmail` | No additional configuration is required. The express checkout element collects the email address directly from the wallet. |
| `requestShipping` or `shippingOptions` | [shipping_address_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_address_collection) and [shipping_options](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_options) |
| `requestPayerPhone` | [phone_number_collection](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-phone_number_collection): `{enabled: true}` |
| `disableWallets` | Set the corresponding [paymentMethods](https://docs.stripe.com/js/custom_checkout/create_express_checkout_element#custom_checkout_create_express_checkout_element-options-paymentMethods) option for Apple Pay, Google Pay, or [Link](https://docs.stripe.com/payments/link.md) to `never`. The express checkout element doesn’t display browser-saved cards, so `browserCard` doesn’t require a replacement. |
| `paymentRequest.canMakePayment()` | Mount the express checkout element. It displays eligible express payment methods for the customer. |

## Initialize Checkout [Client-side]

After you create the Checkout Session, use its client secret to initialize Checkout on your client.

#### HTML + JS

### Before

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');
const elements = stripe.elements();
```

### After

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');

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

const checkout = stripe.initCheckoutElementsSdk({
  clientSecret,
});
```

#### React

### Before

```jsx
const stripePromise = loadStripe('<<YOUR_PUBLISHABLE_KEY>>');

function App() {
  return (
    <Elements stripe={stripePromise}>
      <CheckoutForm />
    </Elements>
  );
}
```

### After

```jsx
import {useMemo} from 'react';
import {loadStripe} from '@stripe/stripe-js';
import {CheckoutElementsProvider} from '@stripe/react-stripe-js/checkout';

const stripePromise = loadStripe('<<YOUR_PUBLISHABLE_KEY>>');

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

  return (
    <CheckoutElementsProvider stripe={stripePromise} options={{clientSecret}}>
      <CheckoutForm />
    </CheckoutElementsProvider>
  );
}
```

## Add the express checkout element [Client-side]

You no longer need to create a `paymentRequest` object or call `canMakePayment()`.

Instead, create the express checkout element from the Checkout instance. Stripe displays the payment methods that are available for the current customer and Checkout Session.

#### HTML + JS

### Before

```html
<div id="payment-request-button">
</div>
```

### After

```html
<div id="express-checkout-element">
  <!-- Mount the express checkout element here -->
</div>
```

### Before

```javascript
const paymentRequest = stripe.paymentRequest({
  country: 'US',
  currency: 'usd',
  total: {
    label: 'Demo total',
    amount: 1099,
  },
  requestPayerName: true,
  requestPayerEmail: true,
});
const paymentRequestButton = elements.create('paymentRequestButton', {
  paymentRequest,
});
paymentRequest.canMakePayment().then((result) => {
  if (result) {
    paymentRequestButton.mount('#payment-request-button');
  } else {
    document.getElementById('payment-request-button').style.display = 'none';
  }
});
```

### After

```javascript
const expressCheckoutElement = checkout.createExpressCheckoutElement();

expressCheckoutElement.mount('#express-checkout-element');
```

#### React

### Before

```jsx
import React, {useEffect, useState} from 'react';
import {PaymentRequestButtonElement, useStripe} from '@stripe/react-stripe-js';

const CheckoutForm = () => {
  const stripe = useStripe();
  const [paymentRequest, setPaymentRequest] = useState(null);

  useEffect(() => {
    if (stripe) {
      const pr = stripe.paymentRequest({
        country: 'US',
        currency: 'usd',
        total: {
          label: 'Demo total',
          amount: 1099,
        },
        requestPayerName: true,
        requestPayerEmail: true,
      });

      pr.canMakePayment().then((result) => {
        if (result) {
          setPaymentRequest(pr);
        }
      });
    }
  }, [stripe]);

  return (
    paymentRequest && <PaymentRequestButtonElement options={{paymentRequest}} />
  );
};
```

### After

```jsx
import {
  ExpressCheckoutElement,
  useCheckoutElements,
} from '@stripe/react-stripe-js/checkout';

const CheckoutForm = () => {
  const checkoutState = useCheckoutElements();

  if (checkoutState.type === 'loading') {
    return <p>Loading checkout...</p>;
  }

  if (checkoutState.type === 'error') {
    return <p role="alert">{checkoutState.error.message}</p>;
  }

  return (
    <ExpressCheckoutElement
      onConfirm={(event) => {
        // Add the confirmation logic in the "Confirm the Checkout Session" step.
      }}
    />
  );
};
```

## Optional: Save payment details during a payment

If your existing integration saves payment details during payment, configure the Checkout Session to save the payment method for future use.

When you set [setup_future_usage](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-payment_intent_data-setup_future_usage), Stripe saves the payment method to the Customer after a successful payment. To save a payment method, provide an existing Customer with `customer` or configure Checkout to create one with `customer_creation`.

Use `off_session` if you plan to charge the customer when they aren’t actively using your site. Use `on_session` if the customer will be present for future payments.

### Before

```javascript
paymentRequest.on('paymentmethod', (event) => {
  stripe.confirmCardPayment(
    clientSecret,
    {
      payment_method: event.paymentMethod.id,
      setup_future_usage: 'off_session',
    },
    {
      handleActions: false,
    }
  );
});
```

### After

```javascript
const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  ui_mode: 'elements',
  customer: 'cus_123',
  line_items: [
    {
      price: 'price_123',
      quantity: 1,
    },
  ],
  payment_intent_data: {
    setup_future_usage: 'off_session',
  },
  return_url: 'https://example.com/checkout/return',
});
```

If you don’t have an existing Customer, configure Checkout to create one:

```javascript
const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  ui_mode: 'elements',
  customer_creation: 'always',
  line_items: [
    {
      price: 'price_123',
      quantity: 1,
    },
  ],
  payment_intent_data: {
    setup_future_usage: 'off_session',
  },
  return_url: 'https://example.com/checkout/return',
});
```

## Optional: Style the express checkout element

You can [style](https://docs.stripe.com/js/custom_checkout/create_express_checkout_element#custom_checkout_create_express_checkout_element-options-buttonTheme) each payment method button (for example, Google Pay or Apple Pay) to have different themes and types.

You can also use the `buttonExpressCheckoutBorderRadius` variable in the [Appearance API](https://docs.stripe.com/payments/checkout/customization/appearance.md?ui=elements).

#### HTML + JS

### Before

```javascript
elements.create('paymentRequestButton', {
  paymentRequest,
  style: {
    paymentRequestButton: {
      type: 'book',
      theme: 'dark',
      height: '55px',
    },
  },
});
```

### After

```javascript
const checkout = stripe.initCheckoutElementsSdk({
  clientSecret,
  elementsOptions: {
    appearance: {
      variables: {
        // Controls the border radius of express checkout element buttons.
        buttonExpressCheckoutBorderRadius: '4px',
      },
    },
  },
});

const expressCheckoutElement = checkout.createExpressCheckoutElement({
  layout: {
    maxColumns: 2,
    maxRows: 1,
    overflow: 'auto',
  },
  buttonType: {
    applePay: 'book',
    googlePay: 'book',
    paypal: 'buynow',
  },
  buttonTheme: {
    applePay: 'black',
  },
  buttonHeight: 55,
});

expressCheckoutElement.mount('#express-checkout-element');
```

#### React

### Before

```jsx
const options = {
  paymentRequest,
  style: {
    paymentRequestButton: {
      type: 'book',
      theme: 'dark',
      height: '55px',
    },
  },
};

return <PaymentRequestButtonElement options={options} />;
```

### After

```jsx
import {CheckoutElementsProvider} from '@stripe/react-stripe-js/checkout';

const options = {
  clientSecret,
  elementsOptions: {
    appearance: {
      variables: {
        // Controls the border radius of express checkout element buttons.
        buttonExpressCheckoutBorderRadius: '4px',
      },
    },
  },
};

<CheckoutElementsProvider stripe={stripePromise} options={options}>
  <CheckoutForm />
</CheckoutElementsProvider>;
```

```jsx
import {ExpressCheckoutElement} from '@stripe/react-stripe-js/checkout';

const CheckoutForm = () => {
  const options = {
    layout: {
      maxColumns: 2,
      maxRows: 1,
      overflow: 'auto',
    },
    buttonType: {
      applePay: 'book',
      googlePay: 'book',
      paypal: 'buynow',
    },
    buttonTheme: {
      applePay: 'black',
    },
    buttonHeight: 55,
  };

  return <ExpressCheckoutElement options={options} onConfirm={handleConfirm} />;
};
```

| Payment request button configuration | Express checkout element replacement |
| --- | --- |
| `style.paymentRequestButton.type` | `buttonType` |
| `style.paymentRequestButton.theme` | `buttonTheme` |
| `style.paymentRequestButton.height` | `buttonHeight` |

## Optional: Adapt your UI to available payment methods

The payment request button element requires you to call `paymentRequest.canMakePayment()` before mounting the button.

The express checkout element renders the payment methods available for the customer and Checkout Session. To determine whether any express payment methods are available, listen for the [ready event](https://docs.stripe.com/js/custom_checkout/element_events/on_ready) and inspect `availablePaymentMethods`.

#### HTML + JS

### Before

```javascript
const paymentRequestButton = elements.create('paymentRequestButton', {
  paymentRequest,
});

paymentRequest.canMakePayment().then((result) => {
  if (result) {
    paymentRequestButton.mount('#payment-request-button');
  } else {
    document.getElementById('payment-request-container').style.display = 'none';
  }
});
```

```html
<div id="payment-request-container">
  <h2>Express checkout</h2>
  <div id="payment-request-button"></div>
</div>
```

### After

```javascript
const expressCheckoutElement = checkout.createExpressCheckoutElement();

expressCheckoutElement.on('ready', ({availablePaymentMethods}) => {
  const expressCheckoutContainer = document.getElementById(
    'express-checkout-container'
  );

  if (!availablePaymentMethods) {
    expressCheckoutContainer.style.display = 'none';
    return;
  }

  expressCheckoutContainer.style.display = 'block';
});

expressCheckoutElement.mount('#express-checkout-element');
```

```html
<div id="express-checkout-container">
  <h2>Express checkout</h2>
  <div id="express-checkout-element"></div>
</div>
```

#### React

### Before

```jsx
import {useEffect, useState} from 'react';
import {
  PaymentRequestButtonElement,
  useStripe,
} from '@stripe/react-stripe-js';

const CheckoutForm = () => {
  const stripe = useStripe();
  const [paymentRequest, setPaymentRequest] = useState(null);

  useEffect(() => {
    if (!stripe) {
      return;
    }

    const paymentRequest = stripe.paymentRequest({
      country: 'US',
      currency: 'usd',
      total: {
        label: 'Demo total',
        amount: 1099,
      },
    });

    paymentRequest.canMakePayment().then((result) => {
      if (result) {
        setPaymentRequest(paymentRequest);
      }
    });
  }, [stripe]);

  if (!paymentRequest) {
    return null;
  }

  return (
    <div>
      <h2>Express checkout</h2>
      <PaymentRequestButtonElement options={{paymentRequest}} />
    </div>
  );
};
```

### After

```jsx
import {useState} from 'react';
import {ExpressCheckoutElement} from '@stripe/react-stripe-js/checkout';

const CheckoutForm = () => {
  const [hasExpressPaymentMethods, setHasExpressPaymentMethods] = useState(false);

  return (
    <div hidden={!hasExpressPaymentMethods}>
      <h2>Express checkout</h2>
      <ExpressCheckoutElement
        onReady={({availablePaymentMethods}) => {
          setHasExpressPaymentMethods(Boolean(availablePaymentMethods));
        }}
        onConfirm={handleConfirm}
      />
    </div>
  );
};
```

| Payment request button configuration | Express checkout element replacement |
| --- | --- |
| `paymentRequest.canMakePayment()` | `ready` event |
| `canMakePayment()` result | `availablePaymentMethods` on the `ready` event |

## Confirm the Checkout Session [Client-side]

The payment request button element emits a `paymentmethod` event. In that handler, you typically confirm the PaymentIntent with `stripe.confirmCardPayment`, call `event.complete()`, and manually handle additional authentication.

The express checkout element emits a `confirm` event instead. Pass that event to Checkout to confirm the Checkout Session. Checkout handles required customer authentication and redirects for payment methods that require them.

#### HTML + JS

### Before

```javascript
paymentRequest.on('paymentmethod', (event) => {
  stripe.confirmCardPayment(
    clientSecret,
    {
      payment_method: event.paymentMethod.id,
    },
    {
      handleActions: false,
    }
  ).then((confirmResult) => {
    if (confirmResult.error) {
      event.complete('fail');
      return;
    }

    event.complete('success');

    if (confirmResult.paymentIntent.status === 'requires_action') {
      stripe.confirmCardPayment(clientSecret);
    }
  });
});
```

### After

```javascript
const loadActionsResult = await checkout.loadActions();

if (loadActionsResult.type === 'error') {
  // Show loadActionsResult.error.message to your customer.
  return;
}

const {actions} = loadActionsResult;

expressCheckoutElement.on('confirm', async (event) => {
  const result = await actions.confirm({
    expressCheckoutConfirmEvent: event,
  });

  if (result.type === 'error') {
    // Show result.error.message to your customer.
  }
});
```

#### React

### Before

```javascript
paymentRequest.on('paymentmethod', (event) => {
  stripe.confirmCardPayment(
    clientSecret,
    {payment_method: event.paymentMethod.id},
    {handleActions: false}
  ).then((confirmResult) => {
    if (confirmResult.error) {
      event.complete('fail');
      return;
    }

    event.complete('success');

    if (confirmResult.paymentIntent.status === 'requires_action') {
      stripe.confirmCardPayment(clientSecret);
    }
  });
});
```

### After

```jsx
import {
  ExpressCheckoutElement,
  useCheckoutElements,
} from '@stripe/react-stripe-js/checkout';

const CheckoutForm = () => {
  const checkoutState = useCheckoutElements();

  if (checkoutState.type === 'loading') {
    return <p>Loading checkout...</p>;
  }

  if (checkoutState.type === 'error') {
    return <p role="alert">{checkoutState.error.message}</p>;
  }

  const {checkout} = checkoutState;

  const handleConfirm = async (event) => {
    const result = await checkout.confirm({
      expressCheckoutConfirmEvent: event,
    });

    if (result.type === 'error') {
      // Show result.error.message to your customer.
      console.error(result.error.message);
    }
  };

  return <ExpressCheckoutElement onConfirm={handleConfirm} />;
};
```

## Handle post-payment events [Server-side]

When you migrate to the Checkout Sessions API, update your webhook endpoint to handle Checkout Session events. Fulfill the order after receiving `checkout.session.completed`. If you accept delayed payment methods, also handle `checkout.session.async_payment_succeeded` and `checkout.session.async_payment_failed`. Don’t rely on a client-side callback to fulfill orders. See [Fulfill orders](https://docs.stripe.com/checkout/fulfillment.md).
