Seamless Payments with Stripe: A Developer’s Guide to Easy Integration



This content originally appeared on DEV Community and was authored by Antwi Isaac

Stripe has firmly established itself as a leader in payment processing, simplifying the payment acceptance process for developers. In this guide, we’ll walk through setting up a Node.js Express service integrated with Stripe using Checkout Sessions. We’ll focus solely on Checkout Sessions for a hands-on introduction. For other integration options, view the docs here: Stripe Payment Options

High Level Design & Flow

Before diving into the code, here’s a high-level overview of the integration process.

Gotchas & Things to Note

  • Handle Currency and Amount Formatting: Stripe expects amounts in minor units (e.g., $10.00 should be passed as 1000 cents).

  • Use Webhooks for Reliable Payment Confirmation: Don’t rely on the client-side redirect for payment status.

  • Ensure Metadata is Passed Correctly: To track payments efficiently

  • Enable Webhook Signature Verification: Always verify Stripe webhook signatures to prevent unauthorized or spoofed events.

image

Let’s Code 🚀

1.Initialise a package.json file
yarn init -y

2.Install dependencies
yarn add express stripe dotenv

3.Install dev dependencies.
yarn add -D nodemon typescript @types/express @types/node

4.Initialise Typescript
This would create a new tsconfig.json file that tells typescript how to transpile our app

npx tsc --init

5.Update your scripts in package.json

"scripts": {
    "build": "yarn install && tsc",
    "dev": "nodemon src/server.ts",
    "start": "node dist/server.js"
  }

Your package.json should look like this

package.json

6.Create your src folder and create a server.ts file in it

import express, { Request, Response } from "express";
import dotenv from "dotenv";
import Stripe from "stripe";

// Environment config
dotenv.config();

// Stripe initialization
let _stripe: Stripe | null = null;
export const getStripe = (): Stripe => {
  if (!_stripe) {
    _stripe = new Stripe(process.env.STRIPE_TEST_KEY as string);
  }
  return _stripe;
};

// create an express app
const app = express();

// Express Middleware setup
app.use(express.json());

const port = process.env.PORT || 3001;

// Default Route
app.get("/", (_, res) => {
  res.status(200).json({
    success: true,
    message: "Our express app is live!",
  });
});

// Start Server
app.listen(port, () => {
  console.log(`🚀 Server up and running on port: ${port}`);
});

7.Create an .env file in the root of your project

PORT=8080
STRIPE_TEST_KEY=your_stripe_test_key
STRIPE_WEBHOOK_SECRET=stripe_webhook_secret

Follow this guide to obtain your Stripe Test Key: https://docs.stripe.com/keys

8.Our app is ready… well almost. Run yarn dev to start the server. You should get this feedback.

app ready

Lets Handle Payment Requests

Now that our server is running. We’re going to do two things.

  • Accepting incoming payment requests from the client.
  • Listen for Stripe webhook events and process them

1.Accepting Incoming Payment Requests

When a payment request arrives, calculate the total amount to be paid. For example, in an e-commerce app, send the product_id and quantity, then retrieve the product price and compute the final amount. (Calculate the total amount in the backend, not the frontend, to ensure security and prevent tampering.)

When creating a Checkout Session with Stripe:

  • Specify the currency in lowercase (e.g. “usd”).

  • Convert the price to cents (e.g. $10.00 → 1000).

  • Include relevant metadata (e.g. order_id, productName, productId).

  • Set success and cancel URLs for proper redirection after payment.

Learn more about Checkout Sessions here: Stripe Checkout Sessions

// POST  /checkout to process incoming payment requests from client
app.post("/checkout", async (req: Request, res: Response) => {
  /*  
    Retreive cart from request, calculate final amount to be paid.
    In this example we're purchasing only one product with quantity 1
  */

  const product: { id: number; name: string; priceInCents: number } = {
    id: 1,
    name: "Sunglasses",
    priceInCents: 4500,
  };

  // Create an order, store it on your database with paymentStatus "unpaid"
  // This article is focused on stripe so I'm not doing that fully.
  const orderId = Math.floor(Math.random() * 1000);

  try {
    const session = await getStripe().checkout.sessions.create({
      currency: "usd",
      metadata: {
        productName: product?.name,
        productId: product?.id,
        orderId: orderId,
      },
      line_items: [
        {
          price_data: {
            currency: "usd",
            product_data: {
              name: product?.name,
              metadata: product,
            },
            unit_amount: product.priceInCents,
          },
          quantity: 1,
        },
      ],
      mode: "payment", // other modes are 'subscription' and 'setup'
      success_url: `http://localhost:3000/checkout?status=failed`,
      cancel_url: `http://localhost:3000/checkout?status=failed`,
    });

    res.status(200).json({ checkoutUrl: session.url });
  } catch (e) {
    console.log("e", e);
    res.status(500).json({ error: "Something went wrong" });
  }
});

2.Listen for events strom Stripe via Webhook

Add this code block before the app.use(express.json()) line. This is because, for Stripe to validate the signature of your webhook, it has to be raw without any json parsing.

// Stripe webhook to listen for events
app.post(
  "/stripe-webhook",
  express.raw({ type: "application/json" }),
  async (req: Request, res: Response) => {
    const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

    try {
      if (endpointSecret) {
        // Get the signature sent by Stripe
        const signature = req.headers["stripe-signature"];

        if (!signature) {
          throw new Error("Missing Stripe signature");
        }

        const event = getStripe().webhooks.constructEvent(
          req.body,
          signature,
          endpointSecret
        );

        switch (event.type) {
          case "checkout.session.completed": {
            const checkoutSession = event.data.object;

            const {
              currency,
              subscription,
              amount_total,
              status,
              payment_status,
              metadata,
              created,
            } = checkoutSession;

            // Get order ID from metadata since it was passed
            const orderId = metadata?.orderId;

            /*
             Mark order as complete based on the payment_status
             You have access ti other information from the checkout session
             including the amount_total, date created, currency and so much more
             checkoout the Checkout session object in stripe docs: https://docs.stripe.com/api/checkout/sessions/object
            */

            // handle your database updates and any other logic
            if (payment_status == "paid") {
              //
            }

            break;
          }

          default:
            console.log(`⚡ Unhandled Stripe Webhook ->  ${event.type}`);
        }

        res.json({ received: true });
      } else {
        res.status(500).json({ message: "Something went wrong" });
      }
    } catch (error: any) {
      console.error(`Webhook error: ${error?.message}`);
      res.status(400).send(`Webhook error: ${error?.message}`);
    }
  }
);

Forwarding Stripe Events to your local machine

Add your local event listener in the Stripe dashboard. Ensure that your webhook endpoint has been configured with the “checkout.session.completed” event included.

View the docs here: Webhooks

Install the Stripe CLI, login and run this command. This would listen to webhook events and forward them to the endpoint we specified.

stripe listen --forward-to localhost:8080/stripe-webhook

Now we’re ready to test our setup.

Interactive Testing With Stripe Test Keys

When testing interactively, use a card number, such as 4242 4242 4242 4242 to simulate a successful payment.

  • Use a valid future date, such as 12/34.
  • Use any three-digit CVC (four digits for American Express cards).
  • Use any value you like for other form fields.

Read More About Testing Cards: Stripe Testing

Full Code for this tutorial is available on Github

You can view the full code on Github: https://github.com/okraks/stripe-integration-guide

Related Articles & Resources


This content originally appeared on DEV Community and was authored by Antwi Isaac