How I Built a Dynamic Payment System in React with Stripe (Beginner-Friendly)

CodeGenitor
5 min readSep 5, 2024

--

Are you a developer looking to integrate payments into your web application? Look no further! In this blog, I’ll walk you through how to implement a secure and dynamic payment system using React, Stripe, and Node.js. Even if you’re a beginner, you’ll find this guide easy to follow as we break it down step by step.

Why Use Stripe for Payments?

Stripe is one of the most popular and powerful payment processing platforms out there. It handles everything from secure card processing to handling refunds and monitoring fraud. Best of all, you can get started quickly with minimal configuration.

What We’ll Cover:

source code on Github

  1. Setting up a basic React app
  2. Installing Stripe and integrating it into your project
  3. Creating dynamic payment fields (so users can choose how much they pay)
  4. Connecting a Node.js backend to handle payment intents
  5. Processing and confirming payments with a secure card input form

Let’s dive in!

Step 1: Setting Up a Basic React App

First things first — let’s create a basic React app if you don’t already have one.

Run the following command to get started:

npx create-react-app stripe-payment-app
cd stripe-payment-app
npm install

Next, you’ll need to install Stripe’s libraries to work with React:

npm install @stripe/react-stripe-js @stripe/stripe-js

These packages provide the tools we need to interact with Stripe and securely collect payment information.

Step 2: Setting Up Stripe Elements

Stripe Elements is a collection of pre-built UI components for collecting sensitive card information. Using Elements, you don’t need to worry about security, as all sensitive data is handled directly by Stripe.

In your App.js, we will set up Stripe by wrapping our form in the <Elements> provider. This will ensure that all payment-related components have access to Stripe’s context.

// src/App.tsx
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import PaymentForm from './PaymentForm';

// Load Stripe with your publishable key
const stripePromise = loadStripe('your-publishable-key-here');

const App: React.FC = () => {
return (
<Elements stripe={stripePromise}>
<PaymentForm />
</Elements>
);
};

export default App;

Step 3: Creating the Payment Form

Now that Stripe is initialized, let’s create the dynamic payment form where users can input their name, email, card information, and the amount they want to pay.

Here’s a simple payment form using React and Stripe’s CardElement component:

// src/PaymentForm.tsx
import React, { useState } from 'react';
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

const PaymentForm: React.FC = () => {
const stripe = useStripe();
const elements = useElements();
const [formData, setFormData] = useState({ name: '', email: '', amount: 0 });
const [paymentStatus, setPaymentStatus] = useState('');

const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: name === 'amount' ? parseInt(value) : value,
});
};

const handleSubmit = async (e) => {
e.preventDefault();
const cardElement = elements.getElement(CardElement);

try {
const amountInCents = formData.amount * 100;
const response = await fetch('/api/create-payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: amountInCents }),
});

const { clientSecret } = await response.json();
const result = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: formData.name,
email: formData.email,
},
},
});

if (result.error) {
setPaymentStatus(`Payment failed: ${result.error.message}`);
} else {
setPaymentStatus('Payment succeeded!');
setFormData({ name: '', email: '', amount: 0 });
cardElement.clear();
}
} catch (error) {
setPaymentStatus('Error processing payment.');
}
};

return (
<form onSubmit={handleSubmit}>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" required />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" required />
<input name="amount" value={formData.amount} onChange={handleChange} placeholder="Amount (USD)" type="number" required />
<CardElement />
<button type="submit" disabled={!stripe}>Pay ${formData.amount}</button>
{paymentStatus && <p>{paymentStatus}</p>}
</form>
);
};

export default PaymentForm;

This form is fully functional! It collects the user’s name, email, card details, and allows them to specify how much they want to pay. All of this is securely handled by Stripe Elements.

Step 4: Setting Up the Node.js Backend

Now, let’s create a backend that will handle the payment intent creation. This is where the real magic happens — Stripe requires a server to securely create and manage payment intents, ensuring that your transactions are secure.

First, create a simple Express server:

mkdir server
cd server
npm init -y
npm install express stripe cors

Now, in your server/index.js, add the following code to handle payment creation:

const express = require('express');
const Stripe = require('stripe');
const cors = require('cors');

const app = express();
const stripe = Stripe('your-secret-key-here');
app.use(cors());
app.use(express.json());

app.post('/api/create-payment-intent', async (req, res) => {
const { amount } = req.body;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency: 'usd',
});
res.send({ clientSecret: paymentIntent.client_secret });
} catch (error) {
res.status(500).send({ error: error.message });
}
});

app.listen(5000, () => console.log('Server running on port 5000'));

Step 5: Testing the Payment Flow

At this point, your frontend and backend are set up. You can now run both:

  1. Start the backend server by running:
node index.js

2. Start the React frontend:

npm start

Visit your app in the browser, fill out the form with test data, and use one of Stripe’s test card numbers, such as:

Card number: 4242 4242 4242 4242
Expiration: Any future date (e.g., 12/34)
CVC: Any 3 digits

Conclusion:

Congratulations! You’ve just built a simple, dynamic payment system using React and Stripe. This beginner-friendly setup allows users to input their own payment amounts and handles everything securely behind the scenes.

With Stripe, you can expand further, including saving customer details, handling subscriptions, or even processing refunds.

The source code is available on Github

Follow me on Github, give the project a like share to help someone that could benefit.

Happy coding, and good luck with your projects! 🎉

--

--

CodeGenitor

Software developer passionate about coding, innovation, and tech trends. Turning ideas into reality, one line of code at a time.