# Migrate to the express checkout element

Migrate your existing integration with the payment request button element to the express checkout element.

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.

| If your existing integration uses | Do the following |
| --- | --- |
| [Payment Intents](https://docs.stripe.com/api/payment_intents.md) API to create and track payments or save card details during a payment | Follow this guide to keep using the Payment Intents API. We recommend using this opportunity to migrate to the [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md). To do so, see [Migrate to the Express Checkout Element with Checkout Sessions](https://docs.stripe.com/payments/checkout/express-checkout-element/migration.md). |
| [Charges](https://docs.stripe.com/api/charges.md) API with tokens | Migrate to the [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md) or [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) 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 the [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.

## Update Elements instance [Client-side]

Next, update your client-side code to pass the mode (payment), amount, and currency. These values determine which payment methods to show to your customers.

For example, if you pass the `eur` currency on the `PaymentIntent` and enable OXXO in the Dashboard, your customer won’t see OXXO because OXXO doesn’t support `eur` payments.

Stripe evaluates the currency, payment method restrictions, and other parameters to determine the list of supported payment methods. We prioritize payment methods that increase conversion and are most relevant to the currency and customer location.

#### HTML + JS

### Before

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

### After

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');
const options = {
  mode: 'payment',
  amount: 1099,
  currency: 'usd',
};
const elements = stripe.elements(options);
```

#### React

### Before

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

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

### After

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

const options = {
  mode: 'payment',
  amount: 1099,
  currency: 'usd',
};

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

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

If you use [React Stripe.js](https://github.com/stripe/react-stripe-js), update to the latest package to use the express checkout element.

Replace the payment request button element with the express checkout element. The examples below demonstrate how to replace `PaymentRequestButtonElement` with `ExpressCheckoutElement`.

You no longer need to create a `paymentRequest` object. Instead, pass the options when creating the `ExpressCheckoutElement`.

#### 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,
});
paymentRequest.canMakePayment().then(function(result) {
  if (result) {
    paymentRequestButton.mount('#payment-request-button');
  } else {
    document.getElementById('payment-request-button').style.display = 'none';
  }
});
```

### After

```javascript
const expressCheckoutElement = elements.create('expressCheckout', {
  // Express checkout element options
});
expressCheckoutElement.mount("#express-checkout-element");
```

#### React

### Before

```jsx
import React, {useState, useEffect} 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 React from 'react';
import {ExpressCheckoutElement} from '@stripe/react-stripe-js';

const CheckoutPage = () => {
  const options = {
    // Express checkout element options
  };

  return (
    <div id="checkout-page">
      <ExpressCheckoutElement
        options={options}
        onConfirm={onConfirm}
      />
    </div>
  );
};
```

## Optional: Save payment details during a payment [Client-side] [Server-side]

If your existing integration saves card details during payment, use the `setup_future_usage` option when creating the Elements instance, instead of passing it at the confirm payment stage with `stripe.confirmCardPayment`. Set the same value when you create the PaymentIntent.

You can’t save certain payment methods during payment. You can still enable these [payment methods](https://docs.stripe.com/payments/payment-methods/integration-options.md) for other use cases, but customers won’t see them when they set up future payments.

#### HTML + JS

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');
const options = {
  mode: 'payment',
  amount: 1099,
  currency: 'usd',
  setup_future_usage: 'off_session',
};
const elements = stripe.elements(options);
```

#### React

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

const options = {
  mode: 'payment',
  amount: 1099,
  currency: 'usd',
  setup_future_usage: 'off_session',
};

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

When you create the PaymentIntent on your server, set `setup_future_usage` to the same value that you passed to Elements:

```javascript
const paymentIntent = await stripe.paymentIntents.create({
  amount: 1099,
  currency: 'usd',
  setup_future_usage: 'off_session',
});
```

## Optional: Style the express checkout element

You can [style](https://docs.stripe.com/js/elements_object/create_express_checkout_element#express_checkout_element_create-options-buttonTheme) each payment method button—for example, [Google Pay](https://developers.google.com/pay/api/web/guides/resources/customize) or [Apple Pay](https://developer.apple.com/design/human-interface-guidelines/technologies/apple-pay/buttons-and-marks/)—to have different themes and types.

You can also use the `buttonExpressCheckoutBorderRadius` variable in the [Appearance](https://docs.stripe.com/elements/appearance-api.md?platform=web#commonly-used-variables) API.

#### HTML + JS

### Before

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

### After

```javascript
const appearance = {
  variables: {
    // This controls the border-radius of the rendered express checkout element.
    buttonExpressCheckoutBorderRadius: '4px',
  }
};
const options = {
  mode: 'payment',
  amount: 1099,
  currency: 'usd',
  appearance,
};
// Pass the appearance object to the Elements instance.
const elements = stripe.elements(options);
elements.create('expressCheckout', {
  buttonType: {
    googlePay: 'book',
    applePay: 'book',
    paypal: 'buynow',
  },
  buttonTheme: {
    applePay: 'black'
  },
  buttonHeight: 55
});
```

#### React

### Before

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

const CheckoutForm = () => {
  const options = {
    paymentRequest,
    style: {
      paymentRequestButton: {
        type: 'book',
        theme: 'dark',
        height: '55px',
      },
    }
  };

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

### After

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

const CheckoutPage = () => {
  const options = {
    buttonType: {
      googlePay: 'book',
      applePay: 'book',
      paypal: 'buynow',
    },
    buttonTheme: {
      applePay: 'black'
    },
    buttonHeight: 55
  }

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

## Optional: Listen to the availablepaymentmethodschange event

After mounting, the express checkout element doesn’t show any buttons for a brief period. You might want to animate in the element when buttons appear. To do so, listen to the [availablepaymentmethodschange event](https://docs.stripe.com/js/elements_object/express_checkout_element_availablepaymentmethodschange_event) and inspect the value of `paymentMethods` to determine which buttons, if any, show up in the express checkout element.

#### HTML + JS

```javascript
// Optional: If you're doing custom animations, hide the element
const expressCheckoutDiv = document.getElementById('express-checkout-element');
expressCheckoutDiv.style.visibility = 'hidden';

expressCheckoutElement.on('availablepaymentmethodschange', ({paymentMethods}) => {
  if (!paymentMethods) {
    // No buttons will show
  } else {
    // Optional: Animate in the element
    expressCheckoutDiv.style.visibility = 'initial';
  }
});
```

#### React

```jsx
import React, {useState} from 'react';
import {ExpressCheckoutElement} from '@stripe/react-stripe-js';
import {onConfirm} from './confirmHandler';

const CheckoutPage = () => {
  // Optional: If you're doing custom animations, hide the element
  const [visibility, setVisibility] = useState('hidden');

  const onAvailablePaymentMethodsChange = ({paymentMethods}) => {
    if (!paymentMethods) {
      // No buttons will show
    } else {
      // Optional: Animate in the element
      setVisibility('initial');
    }
  };

  return (
    <div id="checkout-page">
      <div id="express-checkout-element" style={{visibility}}>
        <ExpressCheckoutElement
          onConfirm={onConfirm}
          onAvailablePaymentMethodsChange={onAvailablePaymentMethodsChange}
        />
      </div>
    </div>
  );
};
```

## Optional: Request an Apple Pay merchant token (MPAN)

The express checkout element supports Apple Pay merchant tokens, which we recommend over device tokens to enable merchant-initiated transactions (MIT), such as recurring and deferred payments and automatic reloads. Merchant tokens (MPANs) connect your business with your customer’s Apple Wallet payment method, so they work across multiple devices and keep payment information active on a new device even when it’s removed from a lost or stolen device. See [Apple Pay merchant tokens](https://docs.stripe.com/apple-pay/merchant-tokens.md?pay-element=ece) for integration details.

## Update the confirm payment method [Client-side]

Listen to the [confirm](https://docs.stripe.com/js/elements_object/express_checkout_element_confirm_event) event to handle confirmation. To collect and submit payment information to Stripe, use [stripe.confirmPayment](https://docs.stripe.com/js/payment_intents/confirm_payment) instead of individual confirmation methods like `stripe.confirmCardPayment`.

Instead of a PaymentMethod ID, `stripe.confirmPayment` uses the Elements instance from the express checkout element and the client secret from the created `PaymentIntent`.

When called, `stripe.confirmPayment` attempts to complete any required actions, such as authenticating your customers by displaying a 3DS dialog or redirecting them to a bank authorization page. After confirmation completes, users are directed to the [return_url](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-return_url) that you configured, which corresponds to a page on your website that provides the payment status.

If you want the checkout flow for card payments to redirect only for payment methods that require it, you can set [redirect](https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-redirect) to `if_required`. When you use `if_required`, handle successful confirmations for redirect-based and non-redirect-based payment methods separately.

The example below replaces `stripe.confirmCardPayment` with `stripe.confirmPayment`.

#### HTML + JS

### Before

```javascript
paymentRequest.on('paymentmethod', function(ev) {
  stripe.confirmCardPayment(
    '{{CLIENT_SECRET}}',
    {payment_method: ev.paymentMethod.id},
    {handleActions: false}
  ).then(function(confirmResult) {
    if (confirmResult.error) {
      ev.complete('fail');
    } else {
      ev.complete('success');
      if (confirmResult.paymentIntent.status === "requires_action") {
        stripe.confirmCardPayment(clientSecret).then(
          function(result) {
            if (result.error) {
              // The payment failed -- ask your customer for a new payment method.
            } else {
              // The payment succeeded.
            }
          }
        );
      } else {
        // The payment succeeded.
      }
    }
  });
});
```

### After

```javascript
expressCheckoutElement.on('confirm', async (event) => {
  const {error: submitError} = await elements.submit();
  if (submitError) {
    // Show submitError.message to your customer.
    return;
  }

  const response = await fetch('/create-intent', {
    method: 'POST',
  });
  const {client_secret: clientSecret} = await response.json();

  const {error} = await stripe.confirmPayment({
    // `Elements` instance that's used to create the express checkout element.
    elements,
    // `clientSecret` from the created PaymentIntent
    clientSecret,
    confirmParams: {
      return_url: 'https://example.com/order/123/complete',
    },
    // To redirect only for redirect-based payment methods, uncomment this line
    // and handle successful non-redirect payments separately.
    // redirect: 'if_required',
  });

  if (error) {
    // This point is reached only if there's an immediate error when confirming the payment. Show the error to your customer (for example, payment details incomplete).
  } else {
    // Your customer will be redirected to your `return_url`.
  }
});
```

#### React

### Before

```javascript
paymentRequest.on('paymentmethod', function(ev) {
  stripe.confirmCardPayment(
    '{{CLIENT_SECRET}}',
    {payment_method: ev.paymentMethod.id},
    {handleActions: false}
  ).then(function(confirmResult) {
    if (confirmResult.error) {
      ev.complete('fail');
    } else {
      ev.complete('success');
      if (confirmResult.paymentIntent.status === "requires_action") {
        stripe.confirmCardPayment(clientSecret).then(
          function(result) {
            if (result.error) {
              // The payment failed -- ask your customer for a new payment method.
            } else {
              // The payment succeeded.
            }
          }
        );
      } else {
        // The payment succeeded.
      }
    }
  });
});
```

### After

```jsx
import React from 'react';
import {
  ExpressCheckoutElement,
  useElements,
  useStripe,
} from '@stripe/react-stripe-js';

const CheckoutPage = () => {
  const stripe = useStripe();
  const elements = useElements();

  const onConfirm = async () => {
    if (!stripe || !elements) {
      return;
    }

    const {error: submitError} = await elements.submit();
    if (submitError) {
      // Show submitError.message to your customer.
      return;
    }

    const response = await fetch('/create-intent', {
      method: 'POST',
    });
    const {client_secret: clientSecret} = await response.json();

    const {error} = await stripe.confirmPayment({
      // `Elements` instance that's used to create the express checkout element.
      elements,
      // `clientSecret` from the created PaymentIntent
      clientSecret,
      confirmParams: {
        return_url: 'https://example.com/order/123/complete',
      },
      // To redirect only for redirect-based payment methods, uncomment this line
      // and handle successful non-redirect payments separately.
      // redirect: 'if_required',
    });

    if (error) {
      // This point is reached only if there's an immediate error when confirming the payment. Show the error to your customer (for example, payment details incomplete).
    } else {
      // Your customer will be redirected to your `return_url`.
    }
  };

  return (
    <div id="checkout-page">
      <ExpressCheckoutElement
        onConfirm={onConfirm}
      />
    </div>
  );
};
```

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

You don’t need to change your post-payment event handling when you migrate to the express checkout element. Continue listening for payment events on your server instead of relying on a client-side callback. To fulfill orders, send confirmations, and handle delayed or failed payments, see [Handle payment events with webhooks](https://docs.stripe.com/events/set-up-events.md).
