How to integrate Stripe with React
Handling payments can be quite a complex task in applications and is considered very safety-critical. For this purpose, we'll be making use of an easy-to-integrate platform called Stripe to aid our payments in web applications.
Stripe
Stripe is a payment processing platform to help applications securely accept and manage online payments. Its developer-friendly API makes it one of the top choices in websites, and therefore, its integration with ReactJS is in high demand.
Setting up a React project
Prior to using Stripe in a React project, we'll have to set up a project first. This can be done by following the steps given here.
Alternatively, these two commands can be run to set up the project quickly.
npx create-react-app react-proj
cd react-proj
Styling with Tailwind
For adding styles to our code, we will be making use of Tailwind CSS, a highly customizable library that provides class names to add different styles.
You can learn how to set it up here.
Project structure
After the setup, the project structure should be similar to the given image.
Setting up Stripe
Now that we're ready to integrate Stripe into our React project, let's start by downloading the necessary dependencies.
Installing the Stripe package
Stripe provides a library specifically designed for React integration. To install it, we can run the following command in our main directory.
Imports for our client-side code
npm install --save @stripe/react-stripe-js @stripe/stripe-js
Imports for our server-side code
npm install stripe
Importing the Stripe package
Once the package is installed, we can import the required modules from the Stripe package into our React code. Depending on the requirements, the imports can differ.
import { loadStripe } from "@stripe/stripe-js";
Obtaining Stripe's API key
To establish a connection with Stripe's services, we will need an API key. Simply put, we'll make our application communicate with Stripe and allow it to handle the payments safely.
You can obtain an API key by creating an account on Stripe. In this Answer, we'll be using the test keys from the developer mode. Stripe provides us with two types of keys in this mode:
A publishable key (used in client-side code).
A secret key (used in server-side code).
Make sure to keep your API key secure and treat it as a sensitive piece of information.
Note: It's a good practice to keep the API keys in a .env file. Make sure to replace both the client and server side codes with your keys.
Code sample
We will be creating a full-stack application with ReactJS for the client-side code and Express for the server-side code. Our application will be able to render a simple payment form that is handled by Stripe's own logic, connected through our server.
Note: You will have to run the front-end code on one port and the back-end code on another. Make sure to replace the server endpoint and the API keys accordingly.
Client code
Our client code displays the payment form by integrating both React and Stripe. Let's take a lot at the files included in the code.
CheckoutForm.jsx file
import React, { useEffect, useState } from "react";import { PaymentElement, useStripe, useElements } from "@stripe/react-stripe-js";export default function CheckoutForm() {const stripe = useStripe();const elements = useElements();const [message, setMessage] = useState(null);const [isLoading, setIsLoading] = useState(false);useEffect(() => {if (!stripe) {return;}const clientSecret = new URLSearchParams(window.location.search).get("payment_intent_client_secret");if (!clientSecret) {return;}stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {setMessage(paymentIntent.status === "succeeded" ? "Your payment succeeded" : "Unexpected error occurred");});}, [stripe]);const handleSubmit = async (e) => {e.preventDefault();if (!stripe || !elements) {return;}setIsLoading(true);const { error } = await stripe.confirmPayment({elements,confirmParams: {return_url: "http://localhost:3000",}});if (error && (error.type === "card_error" || error.type === "validation_error")) {setMessage(error.message);}setIsLoading(false);};return (<form onSubmit={handleSubmit}><p className="text-black mb-4">Complete your payment here!</p><PaymentElement /><button className='bg-black rounded-xl text-white p-2 mt-6 mb-2' disabled={isLoading || !stripe || !elements}>{isLoading ? "Loading..." : "Pay now"}</button>{message && <div>{message}</div>}</form>);}
Let's first import the necessary modules, including
React,useEffect,useState,PaymentElement,useStripe, anduseElements, to handle the payment functionality in our code.We define the
CheckoutFormfunction component that handles the payment form. It utilizes thestripeandelementsobjects fromuseStripeanduseElementshooks respectively. The component includes elements for payment details, a submit handler, and conditional rendering of messages.In the
useEffecthook, we retrievepayment_intent_client_secretand usestripe.retrievePaymentIntent()to get the payment intent's status. We can set themessagethrough this.The
handleSubmitis called when the form is submitted. It confirms the payment usingstripe.confirmPayment()and sets the error, if any.Our return code includes the components for the UI interface.
stripePayment.js file
import React, { useState, useEffect } from "react";import { loadStripe } from "@stripe/stripe-js";import { Elements } from "@stripe/react-stripe-js";import CheckoutForm from "./CheckoutForm";const stripePromise = loadStripe(process.env.CLIENT_KEY);export default function StripeCheckout() {const [clientSecret, setClientSecret] = useState("");useEffect(() => {// replace this with your own server endpointfetch("https://ed-5374963090194432.educative.run/create-payment-intent", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({ items: [{}] }),}).then((res) => {if (!res.ok) {throw new Error("Network response was not ok");}return res.json();}).then((data) => setClientSecret(data.clientSecret)).catch((error) => {console.log(error);});}, []);const options = {clientSecret,};return (<div>{clientSecret && (<Elements options={options} stripe={stripePromise}><CheckoutForm /></Elements>)}</div>);}
We import the necessary modules for the code.
We import the
CheckoutFormcomponent here.We load the Stripe API by calling
loadStripeand passing the Stripe client key (replace this with your own key).Next, the
StripeCheckoutfunction component is defined, which manages theclientSecretstate.In the
useEffecthook, we make a POST request to the server endpoint/create-payment-intentto get the client secret. Theitemsparameter can be replaced with the actual order item/s.We define the
optionsobject that contains theclientSecret.Finally, we render the
CheckoutFormcomponent which is to be wrapped in Stripes'Elementscomponent. TheoptionsandstripePromiseare then passed as props. TheCheckoutFormis only rendered whenclientSecretis available.
Checkout.jsx file
import React from "react";import StripeCheckout from "./stripePayment";const Checkout = () => {return (<div className="font-mono text-white text-opacity-70 font-[700] text-opacity-90 h-screen flex justify-center items-center"><div className='bg-white rounded-md p-12 bg-opacity-70'><StripeCheckout/></div></div>);};export default Checkout;
We can then render our component in the Checkout.jsx file, which is called in App.js.
Server code
Now that our front-end code is complete, we simply have to define an endpoint to communicate with Stripe. Let's build our back-end in Express.
const app = express();const stripe = require("stripe")(process.env.SERVER_KEY);app.use(cors());app.use(bodyParser.json());const calculateOrderAmount = (items) => {return 2000;};app.post("/create-payment-intent", async (req, res) => {const { items } = req.body;const paymentIntent = await stripe.paymentIntents.create({amount: calculateOrderAmount(items),currency: "usd",automatic_payment_methods: {enabled: true,},});res.send({clientSecret: paymentIntent.client_secret,});});
The following code calculates the amount of
itemssent to it (you can replace it with your own logic) and returns theclientSecretto the client-side code.
Complete code
Hurray! Our full-stack application is now complete.
module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
};Payment demonstration
Our application looks like this at first glance. The payment component is provided by Stripe and can be customized according to our needs.
Note: We can test our application using test cards from Stripe's official documentation
. here https://docs.stripe.com/testing?testing-method=card-numbers
Let's simply click the Pay now button without entering the required information. The component shows the appropriate errors.
We will now enter valid entries from Stripe's test data and see what the result is in the next step.
After submitting the information, our payment has now succeeded.
Note: You can check out our course on "Integration With Stripe API" here.
Why do we use the .env file to store our secret keys?
Free Resources