If you're building a React project and want to add a payment feature, you're in the right place. In this post, I’ll show you the easiest way to integrate Stripe Checkout using:
- React for the frontend
- Node.js + Express for the backend
- No database — just dummy data
Perfect for beginners who want to learn fast or create a tutorial or demo app!
🛠 What We’re Building
You’ll create:
- A fake cart (no real database)
- A “Pay Now” button
- A Stripe payment flow
- A simple success/failure result screen
🧰 Tech Stack
- Frontend: React (using basic components)
- Backend: Node.js with Express
- Payment: Stripe (Checkout session)
📦 Backend Setup (Node.js + Express + Stripe)
Step 1: Create the backend project
mkdir backend
cd backend
npm init -y
npm install express stripe cors dotenv
Step 2: Add your .env
file
Create a .env
file in the backend folder and add your Stripe secret key:
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key_here
Step 3: Create index.js
import express from "express";
import dotenv from "dotenv";
import Stripe from "stripe";
import cors from "cors";
dotenv.config();
const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.use(cors());
app.use(express.json());
app.post("/api/checkout", async (req, res) => {
try {
const { items, email } = req.body;
const line_items = items.map((item) => ({
price_data: {
currency: "aud",
product_data: { name: item.name },
unit_amount: item.price * 100,
},
quantity: item.quantity,
}));
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items,
customer_email: email,
mode: "payment",
success_url: "http://localhost:5173/payment-result?success=true&session_id={CHECKOUT_SESSION_ID}",
cancel_url: "http://localhost:5173/payment-result?success=false&session_id={CHECKOUT_SESSION_ID}",
});
res.json({ url: session.url });
} catch (error) {
console.error(error);
res.status(500).json({ error: "Checkout session failed" });
}
});
app.listen(5000, () => console.log("Backend is running on port 5000"));
💻 Frontend Setup (React)
You can use Vite or Create React App. Let’s say you're inside App.jsx
.
Step 1: Dummy Cart UI
import React from "react";
import axios from "axios";
const dummyCart = [
{ name: "T-shirt", price: 30, quantity: 2 },
{ name: "Hat", price: 20, quantity: 1 },
];
const App = () => {
const total = dummyCart.reduce((acc, item) => acc + item.price * item.quantity, 0);
const handleCheckout = async () => {
try {
const res = await axios.post("http://localhost:5000/api/checkout", {
items: dummyCart,
email: "[email protected]", // Dummy email for now
});
window.location.href = res.data.url;
} catch (err) {
alert("Checkout failed");
console.error(err);
}
};
return (
<div style={{ maxWidth: 400, margin: "40px auto", textAlign: "center" }}>
<h2>🛒 Dummy Cart</h2>
{dummyCart.map((item, index) => (
<p key={index}>
{item.name} x {item.quantity} = ${item.price * item.quantity}
</p>
))}
<h3>Total: ${total}</h3>
<button onClick={handleCheckout} style={{ padding: "10px 20px" }}>
Pay Now
</button>
</div>
);
};
export default App;
🚀 Run and Test
- Start the backend:
node index.js
- Start the frontend (React app):
npm run dev
- Go to your app and click the "Pay Now" button — you’ll be taken to Stripe’s secure checkout page! Here's what to expect:
- ✅ Itemized summary of the products (name, price, quantity)
- 💳 Input fields for card details (number, expiry, CVC)
- 📧 Pre-filled customer email (if provided in session)
- 🔒 Secure and PCI-compliant design
This is a hosted page by Stripe — meaning you don't need to worry about building the form or storing card info. It’s fast, secure, and mobile-friendly.
💳 Stripe Test Card Details for Development
Stripe provides a range of test card numbers that work in test mode and simulate different payment scenarios.
✅ Commonly Used Card
Purpose | Card Number | Expiry | CVC | Result |
---|---|---|---|---|
Success Payment | 4242 4242 4242 4242 |
Any future date | Any 3 digits | ✅ Approved |
❌ Other Test Cards
Scenario | Card Number | Result |
---|---|---|
Declined card | 4000 0000 0000 0002 |
❌ Declined |
Insufficient funds | 4000 0000 0000 9995 |
❌ Declined |
Card requires authentication | 4000 0027 6000 3184 |
3D Secure required |
Incorrect CVC | 4000 0000 0000 0127 |
❌ Declined |
🧾 Usage Guide
-
Expiry Date: Use any future date like
12/34
-
CVC: Use any 3-digit number like
123
-
ZIP/Postal Code: You can enter any value, e.g.,
12345
✅ What Happens After Payment?
Notice this part in our backend:
success_url: "http://localhost:5173/payment-result?success=true&session_id={CHECKOUT_SESSION_ID}",
cancel_url: "http://localhost:5173/payment-result?success=false&session_id={CHECKOUT_SESSION_ID}",
Stripe will automatically replace {CHECKOUT_SESSION_ID}
with the actual session ID. That means you can verify the payment on the next page using that session.
🧠 Challenge For You!
Here’s a fun task to practice what you’ve learned:
✅ Use the
session_id
in the URL to verify the payment on your/payment-result
page.
Hint:
- Use
useSearchParams()
in React to get the session ID. - Create a new backend route:
stripe.checkout.sessions.retrieve(session_id)
- Check if
session.payment_status === "paid"
You can then show either a "Payment Successful 🎉" or "Payment Failed ❌" message based on the result.
🎉 That’s It!
You now have a working Stripe Checkout integration using dummy data. Feel free to expand this into a real cart later. Let me know if you’d like help verifying the session or deploying it online!
Top comments (2)
Nice, love when stuff just works and you don’t gotta fight with docs for hours.
Love how simple you made this! Any tips on turning the dummy cart into a real one with a database?