Get Stripe card payments working in your React Application
Hey there, fellow devs! Today, we'll be adding a crucial feature to your future applications - integrating payment functionality using the leading payment processor, Stripe. Let me quickly guide you on incorporating a basic card payment feature with Stripe Elements.
Step 1 - Stripe Account
Firstly, you'll need a Stripe account. Visit Stripe and set one up; it's a straightforward process. Gather two essential pieces of information:
Head to the developers' section in your account. Start with the test mode. Gather the necessary keys and save them as environment variables.
Step 2 - Setup
After your initial react app is created install the required packages
npm i @stripe/react-stripe-js" stripe
Its important to note, that to test this locally you will need to use Netlifys CLI as this will give you access to its serverless functions. These are necessary when using stripe payments as these cannot be handled on the client side.
In order to run your React app just run netlify dev and it will spin up a development server for you giving you access to these handy features.
Step 3 - Stripe Configuration
Now, let's set up Stripe in our React app with some quick configuration
Create a config folder in your application and load in Stripe.
This stripe promise will be linked to your dev account so add the publishable key. Here I have VITE_STRIPE_PUBLISHABLE_KEY as I am using Vite to build my React App.
import { loadStripe } from "@stripe/stripe-js"; export const stripePromise = loadStripe(process.env.VITE_STRIPE_PUBLISHABLE_KEY);
Wrap your application with the Elements component and add stripePromise as a prop:
import { Elements } from "@stripe/react-stripe-js"; import { stripePromise } from "./config/stripe/stripe.utils.ts"; ReactDOM.createRoot(document.getElementById("root")!).render( <Elements stripe={stripePromise}> <App /> </Elements> );
Step 4 - Create a basic payment form
Set up the most basic of payment forms. Import the required hooks:
const stripe = useStripe(); const elements = useElements();
Next we need the CardElement within a form component. A simple JSX element return will look something like this.
<form className="h-[100px] w-full max-w-[500px]" onSubmit={paymentHandler} > <h2>Credit Card Payment: </h2> <CardElement /> <button type="submit" className="mt-4 w-full rounded-lg bg-blue-500 px-4 py-2 text-white hover:bg-blue-700 focus:bg-blue-700" disabled={isProcessingPayment} > PAY NOW </button> </form>
The "Pay Now" button will submit the form, triggering the paymentHandler. Construct this payment handler with the following five steps:
1. Check to see if the stripe and elements instances exist and prevent the default form submission behaviour.
const paymentHandler = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!stripe || !elements) { return; }
**2. Create a post request to the payment intent handler function written above. We need to include the Content-Type in the headers and stringify that data. Here I just have a cart total * 100 as stripe charges in cents. **
To keep this walkthrough concise, I have included the full serverless function. Essentially, a serverless function opens up a world of possibilities for running on-demand, server-side code without the need for a dedicated server. We require our Stripe code to run server-side, and we'll leverage this capability.
Enjoy the basic code. Try break it down. Install dotenv to access environment variables
npm i dotenv
To make this work, save the payment intent in a folder in your React project called netlify/functions and name the JavaScript file create-payment-intent.js
Create payment intent
require("dotenv").config(); const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); exports.handler = async (event) => { try { const { amount } = JSON.parse(event.body); const paymentIntent = await stripe.paymentIntents.create({ amount: amount, currency: "usd", payment_method_types: ["card"], }); return { statusCode: 200, body: JSON.stringify({ paymentIntent }), }; } catch (error) { console.log({ error }); return { status: 400, body: JSON.stringify({ error }), }; } };
Don't worry if this confuses you. All it is doing is getting Netlify to run a function on the server that creates the payment intent for us. We need this intent to then charge the card later.
const response = await fetch("./.netlify/functions/create-payment-intent", { method: "post", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ amount: cartTotal * 100 }), }).then((res) => res.json());
3. Next, we need the client secret attached to the payment intent response. This is used to link with the card element when confirming payment. So, grab both of these and add a quick if check.
const clientSecret = response.paymentIntent.client_secret; const cardElement = elements.getElement(CardElement); if (!cardElement) { return "There was an error retrieving the card element"; }
4. Next we need to add the client secret to the card element and confirm the card payment. Run the inbuilt confirmCardPayment function adding the card as the card element and whichever billing details you wish
const paymentResult = await stripe.confirmCardPayment(clientSecret, { payment_method: { card: cardElement, billing_details: { // Whatever you want // }, }, });
5. Depending on the outcome of this billing we can either save the order to our database or alert the user to the error received. Just check the payment intent status
if (paymentResult.error) { alert(paymentResult.error.message); } else { if (paymentResult.paymentIntent.status === "succeeded") { // Save order to your DB alert("PAYMENT SUCCESSFUL"); } }
That's really all there is to it. This is the most simple and straightforward method I have found for using Stripe-authenticated card payment processing in your React application.
There is a ton of extra features/elements you can add and a host of other elements that Stripe offers.
Just read up the react js stripe docs on ways to add even more complexity to this basic setup.