# How to use Stripe API with Node.js

- Tool: Stripe
- Difficulty: Beginner
- Time required: 15 minutes
- Compatibility: Stripe Node.js SDK v14+, Node.js 18+, Express.js 4+
- Last updated: March 2026

## TL;DR

This guide covers setting up Stripe with Node.js from scratch: installing the SDK, configuring API keys, creating customers, processing payments with PaymentIntents, and handling errors. Everything runs in test mode with the 4242424242424242 test card. By the end you will have a working Express server that creates customers, accepts payments, and handles webhooks.

## Getting Started with the Stripe API in Node.js

The Stripe Node.js SDK is the official library for integrating Stripe into backend applications. It wraps the REST API with typed methods for every Stripe resource — customers, payments, subscriptions, and more. The SDK handles authentication, retries, and error formatting. This guide takes you from npm install to a working payment server.

## Before you start

- Node.js 18 or later installed
- A Stripe account (free to create at stripe.com)
- Your test API keys from Dashboard → Developers → API keys
- Basic familiarity with JavaScript and Express.js

## Step-by-step guide

### 1. Install the Stripe SDK and Express

Initialize a Node.js project and install the required packages.

```
# Create project and install dependencies
mkdir stripe-node-app && cd stripe-node-app
npm init -y
npm install stripe express dotenv
```

**Expected result:** A Node.js project is created with stripe, express, and dotenv installed.

### 2. Configure Stripe with environment variables

Create a .env file with your test API keys and initialize the Stripe client.

```
// .env
// STRIPE_SECRET_KEY=sk_test_your_key_here
// STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here
// STRIPE_WEBHOOK_SECRET=whsec_your_secret_here

// server.js
require('dotenv').config();

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2024-12-18.acacia',
  maxNetworkRetries: 2,
});

const express = require('express');
const app = express();

console.log('Stripe initialized. API version:', stripe.getApiField('version'));
```

**Expected result:** The Stripe client is initialized with your test secret key and a pinned API version.

### 3. Create a customer

Customers are the foundation for payments, subscriptions, and saved cards. Create one with an email and metadata.

```
app.use(express.json());

app.post('/api/customers', async (req, res) => {
  try {
    const customer = await stripe.customers.create({
      email: req.body.email,
      name: req.body.name,
      metadata: {
        app_user_id: req.body.userId,
      },
    });
    res.json({ customerId: customer.id, email: customer.email });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
```

**Expected result:** A Stripe customer is created and the customer ID is returned.

### 4. Create a PaymentIntent

PaymentIntents are the recommended way to accept payments. Create one on the server and send the client_secret to the frontend.

```
app.post('/api/create-payment-intent', async (req, res) => {
  try {
    const { amount, customerId } = req.body;

    const paymentIntent = await stripe.paymentIntents.create({
      amount: amount, // Amount in cents (e.g., 2500 = $25.00)
      currency: 'usd',
      customer: customerId,
      automatic_payment_methods: { enabled: true },
      metadata: {
        order_id: req.body.orderId,
      },
    });

    res.json({
      clientSecret: paymentIntent.client_secret,
      paymentIntentId: paymentIntent.id,
    });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

// Test with: curl -X POST http://localhost:3000/api/create-payment-intent \
//   -H 'Content-Type: application/json' \
//   -d '{"amount": 2500, "orderId": "order_123"}'
```

**Expected result:** A PaymentIntent is created and the client_secret is returned for frontend confirmation.

### 5. Handle Stripe API errors

Stripe throws specific error types. Handle them to return useful error messages.

```
async function handleStripeError(err, res) {
  switch (err.type) {
    case 'StripeCardError':
      // Card was declined
      res.status(402).json({
        error: err.message,
        code: err.code, // e.g., 'card_declined', 'expired_card'
        decline_code: err.decline_code,
      });
      break;
    case 'StripeRateLimitError':
      res.status(429).json({ error: 'Too many requests. Please retry.' });
      break;
    case 'StripeInvalidRequestError':
      res.status(400).json({ error: err.message });
      break;
    case 'StripeAuthenticationError':
      console.error('Invalid API key!');
      res.status(500).json({ error: 'Payment service configuration error.' });
      break;
    case 'StripeAPIError':
      res.status(500).json({ error: 'Payment service temporarily unavailable.' });
      break;
    default:
      res.status(500).json({ error: 'An unexpected error occurred.' });
  }
}
```

**Expected result:** Each Stripe error type is handled with an appropriate HTTP status code and user-friendly message.

### 6. Add a webhook endpoint

Handle payment confirmations and other events via webhooks. Remember to use express.raw() for the webhook route.

```
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,
        process.env.STRIPE_WEBHOOK_SECRET
      );
    } catch (err) {
      return res.status(400).send(`Webhook Error: ${err.message}`);
    }

    if (event.type === 'payment_intent.succeeded') {
      console.log('Payment confirmed:', event.data.object.id);
    }

    res.json({ received: true });
  }
);

app.listen(3000, () => console.log('Stripe server running on port 3000'));
```

**Expected result:** Webhook endpoint verifies signatures and handles payment_intent.succeeded events.

## Complete code example

File: `server.js`

```javascript
require('dotenv').config();

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2024-12-18.acacia',
  maxNetworkRetries: 2,
});

const express = require('express');
const app = express();

// Webhook route (must be before express.json())
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, process.env.STRIPE_WEBHOOK_SECRET
      );
    } catch (err) {
      return res.status(400).send(`Webhook Error: ${err.message}`);
    }
    console.log('Event:', event.type, event.id);
    if (event.type === 'payment_intent.succeeded') {
      console.log('Payment confirmed:', event.data.object.id);
    }
    res.json({ received: true });
  }
);

app.use(express.json());

app.get('/api/config', (req, res) => {
  res.json({ publishableKey: process.env.STRIPE_PUBLISHABLE_KEY });
});

app.post('/api/customers', async (req, res) => {
  try {
    const customer = await stripe.customers.create({
      email: req.body.email,
      name: req.body.name,
    });
    res.json({ customerId: customer.id });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

app.post('/api/create-payment-intent', async (req, res) => {
  try {
    const pi = await stripe.paymentIntents.create({
      amount: req.body.amount,
      currency: 'usd',
      customer: req.body.customerId,
      automatic_payment_methods: { enabled: true },
    });
    res.json({ clientSecret: pi.client_secret });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

app.get('/api/payments/:id', async (req, res) => {
  try {
    const pi = await stripe.paymentIntents.retrieve(req.params.id);
    res.json({ id: pi.id, status: pi.status, amount: pi.amount });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Stripe server on port ${PORT}`));
```

## Common mistakes

- **Not pinning the API version in the Stripe client** — undefined Fix: Always pass apiVersion when initializing: require('stripe')(key, { apiVersion: '2024-12-18.acacia' }). This prevents unexpected breaking changes.
- **Using the secret key (sk_) in frontend code or committing it to Git** — undefined Fix: Store sk_ in environment variables and .env (add to .gitignore). Only pk_ keys go to the frontend.
- **Placing express.json() before the webhook route** — undefined Fix: The webhook route needs express.raw(). Define it before any express.json() middleware.
- **Passing amount as dollars instead of cents** — undefined Fix: Stripe amounts are always in the smallest currency unit. $25.00 = 2500 cents.

## Best practices

- Pin the Stripe API version to avoid unexpected breaking changes
- Use environment variables for all API keys and webhook secrets
- Handle all Stripe error types with appropriate HTTP status codes
- Use maxNetworkRetries for automatic retry on transient network errors
- Place the webhook route before express.json() middleware
- Use metadata on every object to link Stripe resources to your internal records
- Test with card 4242424242424242 (any future expiry, any CVC) in test mode
- Use the Stripe CLI for local webhook testing: stripe listen --forward-to localhost:3000/webhook

## Frequently asked questions

### Which Node.js version does the Stripe SDK require?

The Stripe Node.js SDK v14+ requires Node.js 12 or later. We recommend Node.js 18+ for the best experience and long-term support.

### How do I use Stripe with TypeScript?

The Stripe SDK includes TypeScript types. Install normally with npm install stripe and import with: import Stripe from 'stripe'. All resources and methods are fully typed.

### What is the test card number for Stripe?

Use 4242424242424242 with any future expiration date and any 3-digit CVC. This card always succeeds in test mode. Use 4000000000000002 to simulate a declined card.

### Do I need Express.js to use Stripe with Node.js?

No. The Stripe SDK works with any Node.js framework (Fastify, Koa, Hapi, etc.) or even without a framework. Express is used in this guide because it is the most common choice.

### Can RapidDev help build my Stripe integration?

Yes. RapidDev specializes in building production-ready Stripe integrations including payments, subscriptions, Connect marketplaces, and webhook infrastructure.

---

Source: https://www.rapidevelopers.com/stripe-guide/how-to-use-stripe-api-with-node-js
© RapidDev — https://www.rapidevelopers.com/stripe-guide/how-to-use-stripe-api-with-node-js
