Payment with Stripe in React Storefront

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

TipFor other types of frameworks or tech stacks, the steps are similar. 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#

You can now create a Stripe component that renders the Stripe UI to accept payment.

For example, you can create a file holding the following Stripe component:

Tip
Code
1"use client"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"12import { sdk } from "@/lib/sdk"13
14const stripe = loadStripe(15  process.env.NEXT_PUBLIC_STRIPE_PK || "temp"16)17
18export default function StripePayment() {19  const { cart } = useCart()20  const clientSecret = cart?.payment_collection?.21    payment_sessions?.[0].data.client_secret as string22
23  return (24    <div>25      <Elements stripe={stripe} options={{26          clientSecret,27        }}>28        <StripeForm clientSecret={clientSecret} />29      </Elements>30    </div>31  )32}33
34const StripeForm = ({ 35  clientSecret,36}: {37  clientSecret: string | undefined38}) => {39  const { cart, refreshCart } = useCart()40  const [loading, setLoading] = useState(false)41
42  const stripe = useStripe()43  const elements = useElements()44
45  async function handlePayment(46    e: React.MouseEvent<HTMLButtonElement, MouseEvent>47  ) {48    e.preventDefault()49    const card = elements?.getElement(CardElement)50
51    if (52      !stripe || 53      !elements ||54      !card ||55      !cart ||56      !clientSecret57    ) {58      return59    }60
61    setLoading(true)62    stripe?.confirmCardPayment(clientSecret, {63      payment_method: {64        card,65        billing_details: {66          name: cart.billing_address?.first_name,67          email: cart.email,68          phone: cart.billing_address?.phone,69          address: {70            city: cart.billing_address?.city,71            country: cart.billing_address?.country_code,72            line1: cart.billing_address?.address_1,73            line2: cart.billing_address?.address_2,74            postal_code: cart.billing_address?.postal_code,75          },76        },77      },78    })79    .then(({ error }) => {80      if (error) {81        // TODO handle errors82        console.error(error)83        return84      }85
86      sdk.store.cart.complete(cart.id)87      .then((data) => {88        if (data.type === "cart" && data.cart) {89          // an error occured90          console.error(data.error)91        } else if (data.type === "order" && data.order) {92          // TODO redirect to order success page93          alert("Order placed.")94          console.log(data.order)95          refreshCart()96        }97      })98    })99    .finally(() => setLoading(false))100  }101
102  return (103    <form>104      <CardElement />105      <button106        onClick={handlePayment}107        disabled={loading}108      >109        Place Order110      </button>111    </form>112  )113}

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 after you initialize the payment session using the Initialize Payment Sessions API route.
  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. The redirection logic depends on the framework you're using.

4. Use the Stripe Component#

Finally, 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?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break