In today’s digital economy, integrating a reliable payment gateway is crucial for any online business. Stripe, one of the most popular payment processing platforms, offers a powerful solution for handling payments securely and efficiently. In this tutorial, we'll walk you through how to implement Stripe Payments using Payment Intents and Webhooks.
1. Starting the Project:
To kick things off, we’ll begin by setting up a basic Node.js project. I’ve already created the project and initialized it in Git, so we won’t need to go through the setup steps from scratch. Let’s jump straight into the implementation of Stripe payment integration.
"https://github.com/vimuths123/stripeapp.git" this repos has a branch called initial_project. This has a simple node project with this structure
stripe-payment/
│── public/
│ └── index.html (Frontend)
│── .env (Environment variables)
│── package.json (Project metadata)
│── server.js (Backend server)
And it has installed all packages needed like "body-parser", "cors", "dotenv", "express", "stripe", "nodemon"
2. Implementing Stripe Payment Intents
In this section, we will create a Payment Intent on the server-side to handle the payment process securely. The Payment Intent represents your intent to collect a payment and manages the complexities of authentication and authorization, like 3D Secure.
1. Set up the Environment
Before we create the Payment Intent, make sure you have added your Stripe API keys in the .env file. You can find your keys in the Stripe Dashboard.
In the .env file, add the following:
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
2. Create the Payment Intent Route
In your server.js file, we’ll create an endpoint that will handle creating the Payment Intent. When a client sends a payment request, this endpoint will interact with Stripe to create a Payment Intent and return the client secret to the frontend.
Open the server.js file and add the following code to create the Payment Intent:
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const app = express();
// Middleware
app.use(cors());
app.use(bodyParser.json());
// Create Payment Intent endpoint
app.post("/create-payment-intent", async (req, res) => {
try {
const { amount, currency } = req.body;
// Create Payment Intent
const paymentIntent = await stripe.paymentIntents.create({
amount: amount * 100, // Convert to cents
currency: currency,
});
// Send the client secret to the client
res.send({
clientSecret: paymentIntent.client_secret,
});
} catch (error) {
res.status(500).send({
error: error.message,
});
}
});
// Start the server
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Here you can see we take stripe secret from .env and create stripe object to get the payment intent
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
And
const paymentIntent = await stripe.paymentIntents.create({
amount: amount * 100, // Convert to cents
currency: currency,
});
Now, you can test the server by running:
npm run dev
You can se the code until this with "backend_payment_intent" branch or previous repo.
3. Frontend Implementation: Handling Stripe Payment
In this section, we will build the frontend to collect payment information from the user and complete the payment using Stripe's Payment Intent.
I have added a page called public/index.html in root. But to populate this we need to do a small change in server.js
const path = require('path');
// Serve static files from the 'public' folder
app.use(express.static(path.join(__dirname, 'public')));
This line of code is used to serve static files (such as HTML, CSS, JavaScript, images, etc.) from a specific folder, in this case, the public folder.
Now if we run 'http://localhost:5000/' this index.html file will be shown in browser.
1. Add Stripe.js to Your HTML
First, we need to include Stripe.js in the
section of your index.html file. This script will provide the necessary functions to interact with Stripe on the client side. You can see this index.html in git repo.<script src="https://js.stripe.com/v3/"></script>
2. Get the payment intent and confirm payment
Upon clicking the button, we make a request to the previously created endpoint, passing the payment amount and currency. The server responds with the payment intent ID, which includes the Stripe secret.
let response = await fetch("http://localhost:5000/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount: amount, currency: "usd" }),
});
These lines retrieve the payment intent's client secret from the server response and then use it to confirm the payment with Stripe.
let { clientSecret } = await response.json();
let { paymentIntent, error } = await stripe.confirmCardPayment(clientSecret, {
payment_method: { card: cardElement },
});
Flow:
1. Backend creates a PaymentIntent: The server creates a PaymentIntent and returns a clientSecret.
2. Frontend confirms the payment: The frontend uses the clientSecret to confirm the payment by calling stripe.confirmCardPayment() with the user's card details.
3. Success or error: If the payment is successful, the paymentIntent is returned, which contains the payment details. If the payment fails, the error is returned, and you can handle it accordingly (e.g., showing an error message).
You can find changes upto this point with frontend_implementation git branch.
3. Save the data with stripe webhook.
Now we may need to save data to the db or add a record that payment happened. Now we can do it by sending ajax call from front end. But that have issues.
Users can change the payment amount and send or user may close the window before the axios call and also another axios call may slow the user experience. So best way is letting stripe call the webhook url.
let's add one more endpoint to server.js
app.use("/webhook", express.raw({ type: "application/json" })); // Webhook requires raw body
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["stripe-signature"];
let event;
try {
// Use req.body directly (raw Buffer) for signature verification
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
console.error("Webhook signature verification failed:", err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
console.log("Webhook received:", event.type);
res.json({ received: true });
});
Now we have two ways of adding stripe webhook. If we have a server then we can get this endpoint and add it here.
https://dashboard.stripe.com/test/webhooks
After that we can get 'STRIPE_WEBHOOK_SECRET' and add it to .env. But if you are using localhost you need to add Stripe CLI and run webhook from here.
4. Run with Stripe CLI
First you need to install the stripe CLI. Since I use windows I get it from here
https://docs.stripe.com/stripe-cli?install-method=windows
You just have to copy the extracted data to a folder and give environment variable
Then run this
stripe listen --forward-to http://localhost:5000/webhook
That is it. Happy coding.
Top comments (0)