Payment with Stripe in React Storefront

In this document, you'll learn how to use Stripe for payment during checkout in a React-based storefront.

TipFor other types of storefronts, the steps are similar. However, refer to Stripe's documentation for available tools for your tech stack.

1. Install Stripe SDK#

In your storefront, use the following command to install Stripe's JS and React SDKs:


2. Add Stripe Environment Variables#

Next, add an environment variable holding your Stripe publishable API key.

For example:

Terminal
NEXT_PUBLIC_STRIPE_PK=pk_test_51Kj...
TipFor Next.js storefronts, the environment variable's name must be prefixed with NEXT_PUBLIC . If your storefront's framework requires a different prefix, make sure to change it.

3. Create Stripe Component#

Then, create a file holding the following Stripe component:

NoteThis snippet assumes you're using the provider from the Cart Context guide in your storefront.
Code
1"use client" // include with Next.js 13+2
3import { 4  CardElement, 5  Elements, 6  useElements, 7  useStripe,8} from "@stripe/react-stripe-js"9import { loadStripe } from "@stripe/stripe-js"10import { useCart } from "../../providers/cart"11import { useState } from "react"12
13const stripePromise = loadStripe(14  process.env.NEXT_PUBLIC_STRIPE_PK || "temp"15)16
17export default function StripePayment() {18  const { cart } = useCart()19  const clientSecret = cart?.payment_collection?.20    payment_sessions?.[0].data.client_secret as string21
22  return (23    <div>24      <Elements stripe={stripePromise} options={{25          clientSecret,26        }}>27        <StripeForm clientSecret={clientSecret} />28      </Elements>29    </div>30  )31}32
33const StripeForm = ({ 34  clientSecret,35}: {36  clientSecret: string | undefined37}) => {38  const { cart, refreshCart } = useCart()39  const [loading, setLoading] = useState(false)40
41  const stripe = useStripe()42  const elements = useElements()43
44  async function handlePayment(45    e: React.MouseEvent<HTMLButtonElement, MouseEvent>46  ) {47    e.preventDefault()48    const card = elements?.getElement(CardElement)49
50    if (51      !stripe || 52      !elements ||53      !card ||54      !cart ||55      !clientSecret56    ) {57      return58    }59
60    setLoading(true)61    stripe?.confirmCardPayment(clientSecret, {62      payment_method: {63        card,64        billing_details: {65          name: cart.billing_address?.first_name,66          email: cart.email,67          phone: cart.billing_address?.phone,68          address: {69            city: cart.billing_address?.city,70            country: cart.billing_address?.country_code,71            line1: cart.billing_address?.address_1,72            line2: cart.billing_address?.address_2,73            postal_code: cart.billing_address?.postal_code,74          },75        },76      },77    })78    .then(({ error }) => {79      if (error) {80        // TODO handle errors81        console.error(error)82        return83      }84
85      fetch(86        `http://localhost:9000/store/carts/${cart.id}/complete`,87        {88          credentials: "include",89          headers: {90            "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",91          },92          method: "POST",93        }94      )95      .then((res) => res.json())96      .then(({ type, cart, order, error }) => {97        if (type === "cart" && cart) {98          // an error occured99          console.error(error)100        } else if (type === "order" && order) {101          // TODO redirect to order success page102          alert("Order placed.")103          console.log(order)104          refreshCart()105        }106      })107    })108    .finally(() => setLoading(false))109  }110
111  return (112    <form>113      <CardElement />114      <button115        onClick={handlePayment}116        disabled={loading}117      >118        Place Order119      </button>120    </form>121  )122}

In the code snippet above, you:

  1. Create a StripePayment component that wraps the actual form with Stripe's Elements component.
    • In the StripePayment component, you obtain the client secret from the payment session's data field. This is set in the Medusa application.
  2. Create a StripeForm component that holds the actual form. In this component, you implement a handlePayment function that does the following:
    • Use Stripe's confirmCardPayment method to accept the card details from the customer.
    • Once the customer enters their card details and submit their order, the resolution function of the confirmCardPayment method is executed.
    • In the resolution function, you send a request to the Complete Cart API route to complete the cart and place the order.
    • In the received response of the request, if the type is cart, it means that the cart completion failed. The error is set in the error response field.
    • If the type is order, it means the card was completed and the order was placed successfully. You can access the order in the order response field.
    • When the order is placed, you refresh the cart. You can redirect the customer to an order success page at this point.

4. Use the Stripe Component#

You can now use the Stripe component in the checkout flow. You should render it after the customer chooses Stripe as a payment provider.

For example, you can use it in the getPaymentUi function defined in the Payment Checkout Step guide:

Code
1const getPaymentUi = useCallback(() => {2  const activePaymentSession = cart?.payment_collection?.3    payment_sessions?.[0]4  if (!activePaymentSession) {5    return6  }7
8  switch(true) {9    case activePaymentSession.provider_id.startsWith("pp_stripe_"):10      return <StripePayment />11    // ...12  }13} , [cart])

More Resources#

Refer to Stripe's documentation for more details on integrating it in your storefront.

Was this page helpful?
Edit this page