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 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.
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
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.
After the setup, the project structure should be similar to the given image.
Now that we're ready to integrate Stripe into our React project, let's start by downloading the necessary dependencies.
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
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";
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.
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.
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.
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
, and useElements
, to handle the payment functionality in our code.
We define the CheckoutForm
function component that handles the payment form. It utilizes the stripe
and elements
objects from useStripe
and useElements
hooks respectively. The component includes elements for payment details, a submit handler, and conditional rendering of messages.
In the useEffect
hook, we retrieve payment_intent_client_secret
and use stripe.retrievePaymentIntent()
to get the payment intent's status. We can set the message
through this.
The handleSubmit
is called when the form is submitted. It confirms the payment using stripe.confirmPayment()
and sets the error, if any.
Our return code includes the components for the UI interface.
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 CheckoutForm
component here.
We load the Stripe API by calling loadStripe
and passing the Stripe client key (replace this with your own key).
Next, the StripeCheckout
function component is defined, which manages the clientSecret
state.
In the useEffect
hook, we make a POST request to the server endpoint /create-payment-intent
to get the client secret. The items
parameter can be replaced with the actual order item/s.
We define the options
object that contains the clientSecret
.
Finally, we render the CheckoutForm
component which is to be wrapped in Stripes' Elements
component. The options
and stripePromise
are then passed as props. The CheckoutForm
is only rendered when clientSecret
is available.
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.
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 items
sent to it (you can replace it with your own logic) and returns the clientSecret
to the client-side code.
Hurray! Our full-stack application is now complete.
module.exports = { content: ["./src/**/*.{html,js,jsx}"], theme: { extend: {}, }, plugins: [], };
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.
How well do you know Stripe’s integration with React JS?
Why do we use the .env file to store our secret keys?
Free Resources