Learn how to set up Stripe with React, from installing packages and creating payment forms to handling webhooks, error states, and going live—step-by-step tutorial.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Step 1: Install the necessary packages
First, you need to install the required packages for integrating Stripe with your React application.
npm install @stripe/stripe-js @stripe/react-stripe-js
If you're planning to use server-side functionality as well:
npm install stripe
Step 2: Set up your Stripe account
Before integrating Stripe into your React application, make sure you have:
Step 3: Initialize Stripe in your React application
Create a wrapper component or modify your main App component to initialize Stripe:
// src/App.js or src/index.js
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import PaymentForm from './components/PaymentForm';
// Load Stripe with your publishable key
const stripePromise = loadStripe('pk_test_your_publishable_key');
function App() {
return (
);
}
export default App;
Step 4: Create a payment form component
Create a new component for your payment form:
// src/components/PaymentForm.js
import React, { useState } from 'react';
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
const CARD_ELEMENT_OPTIONS = {
style: {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
}
};
const PaymentForm = () => {
const [error, setError] = useState(null);
const [processing, setProcessing] = useState(false);
const [succeeded, setSucceeded] = useState(false);
const stripe = useStripe();
const elements = useElements();
const handleChange = (event) => {
if (event.error) {
setError(event.error.message);
} else {
setError(null);
}
};
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) {
// Stripe.js has not loaded yet. Make sure to disable form submission until Stripe.js has loaded.
return;
}
setProcessing(true);
try {
// Call your backend to create the payment intent
const response = await fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 1000, // amount in cents
currency: 'usd',
}),
});
const data = await response.json();
// Confirm the payment with Stripe.js
const { paymentIntent, error } = await stripe.confirmCardPayment(
data.clientSecret,
{
payment\_method: {
card: elements.getElement(CardElement),
billing\_details: {
name: 'Customer Name', // You should collect this from the user
},
},
}
);
if (error) {
setError(`Payment failed: ${error.message}`);
setProcessing(false);
} else if (paymentIntent.status === 'succeeded') {
setError(null);
setSucceeded(true);
setProcessing(false);
// Handle successful payment here
console.log('Payment succeeded!');
}
} catch (err) {
setError(`Payment failed: ${err.message}`);
setProcessing(false);
}
};
return (
);
};
export default PaymentForm;
Step 5: Set up the backend for creating Payment Intents
Create a server file (e.g., server.js) to handle payment intents:
// server.js
const express = require('express');
const app = express();
const stripe = require('stripe')('sk_test_your_secret_key');
app.use(express.json());
app.post('/create-payment-intent', async (req, res) => {
try {
const { amount, currency } = req.body;
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
// Verify your integration in this guide by including this parameter
metadata: { integration_check: 'accept_a\_payment' },
});
res.json({ clientSecret: paymentIntent.client\_secret });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Step 6: Using Stripe Elements for specific payment methods
For more specific payment methods, you can use specialized Stripe Elements:
// Example of using specific Stripe Elements
import { CardNumberElement, CardExpiryElement, CardCvcElement } from '@stripe/react-stripe-js';
// In your component's render method:
Step 7: Adding styles for your payment form
Create a CSS file for styling your payment form:
/_ src/components/PaymentForm.css _/
.form-row {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.StripeElement {
height: 40px;
padding: 10px 12px;
width: 100%;
color: #32325d;
background-color: white;
border: 1px solid #ced4da;
border-radius: 4px;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.card-error {
color: #fa755a;
margin-top: 8px;
font-size: 14px;
}
.payment-success {
color: #28a745;
margin-top: 8px;
font-size: 14px;
}
button {
background: #5469d4;
color: #ffffff;
border-radius: 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
width: 100%;
transition: all 0.2s ease;
}
button:hover {
background-color: #4a5fc1;
}
button:disabled {
opacity: 0.5;
cursor: default;
}
Import this CSS file in your PaymentForm component:
import './PaymentForm.css';
Step 8: Setting up environment variables
For better security, use environment variables for your Stripe keys:
// .env.local (for React)
REACT_APP_STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
// .env (for Node.js backend)
STRIPE_SECRET_KEY=sk_test_your_secret_key
// Frontend
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY);
// Backend
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
Step 9: Implementing error handling and loading states
Enhance your payment form with better error handling and loading states:
// Enhanced version of the handleSubmit function
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) {
return;
}
setProcessing(true);
setError(null);
try {
// Create payment intent
const response = await fetch('/create-payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 1000,
currency: 'usd',
}),
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
// Confirm payment
const { paymentIntent, error: confirmError } = await stripe.confirmCardPayment(
data.clientSecret,
{
payment\_method: {
card: elements.getElement(CardElement),
billing\_details: {
name: 'Customer Name',
},
},
}
);
if (confirmError) {
throw new Error(confirmError.message);
}
if (paymentIntent.status === 'succeeded') {
setSucceeded(true);
// You can also redirect or show a success message
}
} catch (err) {
setError(`Payment failed: ${err.message}`);
} finally {
setProcessing(false);
}
};
Step 10: Adding webhook support for payment confirmations
For reliable payment processing, set up a webhook endpoint:
// In your server.js
const endpointSecret = 'whsec_your_webhook_signing_secret';
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment\_intent.succeeded':
const paymentIntent = event.data.object;
console.log('PaymentIntent was successful!');
// Update your database, fulfill the order, etc.
break;
case 'payment_intent.payment_failed':
const failedPayment = event.data.object;
console.log('Payment failed:', failedPayment.last_payment_error?.message);
// Notify the customer that payment failed
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
res.json({received: true});
});
Step 11: Testing your Stripe integration
For testing, Stripe provides test card numbers:
For testing different scenarios:
Step 12: Going to production
When you're ready to go live:
Update your environment variables for production:
// .env.production
REACT_APP_STRIPE_PUBLISHABLE_KEY=pk_live_your_live_publishable\_key
// Server environment
STRIPE_SECRET_KEY=sk_live_your_live_secret\_key
Conclusion
You've now set up a complete Stripe integration with your React application, including:
Remember that Stripe offers many more features like subscription billing, marketplace payments, and recurring payments that you can implement using similar patterns.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.