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.
Prerequisites
- 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
Install the Stripe SDK and Express
Install the Stripe SDK and Express
Initialize a Node.js project and install the required packages.
1# Create project and install dependencies2mkdir stripe-node-app && cd stripe-node-app3npm init -y4npm install stripe express dotenvExpected result: A Node.js project is created with stripe, express, and dotenv installed.
Configure Stripe with environment variables
Configure Stripe with environment variables
Create a .env file with your test API keys and initialize the Stripe client.
1// .env2// STRIPE_SECRET_KEY=sk_test_your_key_here3// STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here4// STRIPE_WEBHOOK_SECRET=whsec_your_secret_here56// server.js7require('dotenv').config();89const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, {10 apiVersion: '2024-12-18.acacia',11 maxNetworkRetries: 2,12});1314const express = require('express');15const app = express();1617console.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.
Create a customer
Create a customer
Customers are the foundation for payments, subscriptions, and saved cards. Create one with an email and metadata.
1app.use(express.json());23app.post('/api/customers', async (req, res) => {4 try {5 const customer = await stripe.customers.create({6 email: req.body.email,7 name: req.body.name,8 metadata: {9 app_user_id: req.body.userId,10 },11 });12 res.json({ customerId: customer.id, email: customer.email });13 } catch (err) {14 res.status(400).json({ error: err.message });15 }16});Expected result: A Stripe customer is created and the customer ID is returned.
Create a PaymentIntent
Create a PaymentIntent
PaymentIntents are the recommended way to accept payments. Create one on the server and send the client_secret to the frontend.
1app.post('/api/create-payment-intent', async (req, res) => {2 try {3 const { amount, customerId } = req.body;45 const paymentIntent = await stripe.paymentIntents.create({6 amount: amount, // Amount in cents (e.g., 2500 = $25.00)7 currency: 'usd',8 customer: customerId,9 automatic_payment_methods: { enabled: true },10 metadata: {11 order_id: req.body.orderId,12 },13 });1415 res.json({16 clientSecret: paymentIntent.client_secret,17 paymentIntentId: paymentIntent.id,18 });19 } catch (err) {20 res.status(400).json({ error: err.message });21 }22});2324// Test with: curl -X POST http://localhost:3000/api/create-payment-intent \25// -H 'Content-Type: application/json' \26// -d '{"amount": 2500, "orderId": "order_123"}'Expected result: A PaymentIntent is created and the client_secret is returned for frontend confirmation.
Handle Stripe API errors
Handle Stripe API errors
Stripe throws specific error types. Handle them to return useful error messages.
1async function handleStripeError(err, res) {2 switch (err.type) {3 case 'StripeCardError':4 // Card was declined5 res.status(402).json({6 error: err.message,7 code: err.code, // e.g., 'card_declined', 'expired_card'8 decline_code: err.decline_code,9 });10 break;11 case 'StripeRateLimitError':12 res.status(429).json({ error: 'Too many requests. Please retry.' });13 break;14 case 'StripeInvalidRequestError':15 res.status(400).json({ error: err.message });16 break;17 case 'StripeAuthenticationError':18 console.error('Invalid API key!');19 res.status(500).json({ error: 'Payment service configuration error.' });20 break;21 case 'StripeAPIError':22 res.status(500).json({ error: 'Payment service temporarily unavailable.' });23 break;24 default:25 res.status(500).json({ error: 'An unexpected error occurred.' });26 }27}Expected result: Each Stripe error type is handled with an appropriate HTTP status code and user-friendly message.
Add a webhook endpoint
Add a webhook endpoint
Handle payment confirmations and other events via webhooks. Remember to use express.raw() for the webhook route.
1app.post('/webhook',2 express.raw({ type: 'application/json' }),3 (req, res) => {4 const sig = req.headers['stripe-signature'];5 let event;6 try {7 event = stripe.webhooks.constructEvent(8 req.body,9 sig,10 process.env.STRIPE_WEBHOOK_SECRET11 );12 } catch (err) {13 return res.status(400).send(`Webhook Error: ${err.message}`);14 }1516 if (event.type === 'payment_intent.succeeded') {17 console.log('Payment confirmed:', event.data.object.id);18 }1920 res.json({ received: true });21 }22);2324app.listen(3000, () => console.log('Stripe server running on port 3000'));Expected result: Webhook endpoint verifies signatures and handles payment_intent.succeeded events.
Complete working example
1require('dotenv').config();23const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, {4 apiVersion: '2024-12-18.acacia',5 maxNetworkRetries: 2,6});78const express = require('express');9const app = express();1011// Webhook route (must be before express.json())12app.post('/webhook',13 express.raw({ type: 'application/json' }),14 (req, res) => {15 const sig = req.headers['stripe-signature'];16 let event;17 try {18 event = stripe.webhooks.constructEvent(19 req.body, sig, process.env.STRIPE_WEBHOOK_SECRET20 );21 } catch (err) {22 return res.status(400).send(`Webhook Error: ${err.message}`);23 }24 console.log('Event:', event.type, event.id);25 if (event.type === 'payment_intent.succeeded') {26 console.log('Payment confirmed:', event.data.object.id);27 }28 res.json({ received: true });29 }30);3132app.use(express.json());3334app.get('/api/config', (req, res) => {35 res.json({ publishableKey: process.env.STRIPE_PUBLISHABLE_KEY });36});3738app.post('/api/customers', async (req, res) => {39 try {40 const customer = await stripe.customers.create({41 email: req.body.email,42 name: req.body.name,43 });44 res.json({ customerId: customer.id });45 } catch (err) {46 res.status(400).json({ error: err.message });47 }48});4950app.post('/api/create-payment-intent', async (req, res) => {51 try {52 const pi = await stripe.paymentIntents.create({53 amount: req.body.amount,54 currency: 'usd',55 customer: req.body.customerId,56 automatic_payment_methods: { enabled: true },57 });58 res.json({ clientSecret: pi.client_secret });59 } catch (err) {60 res.status(400).json({ error: err.message });61 }62});6364app.get('/api/payments/:id', async (req, res) => {65 try {66 const pi = await stripe.paymentIntents.retrieve(req.params.id);67 res.json({ id: pi.id, status: pi.status, amount: pi.amount });68 } catch (err) {69 res.status(400).json({ error: err.message });70 }71});7273const PORT = process.env.PORT || 3000;74app.listen(PORT, () => console.log(`Stripe server on port ${PORT}`));Common mistakes when using Stripe API with Node.js
Why it's a problem: Not pinning the API version in the Stripe client
How to avoid: Always pass apiVersion when initializing: require('stripe')(key, { apiVersion: '2024-12-18.acacia' }). This prevents unexpected breaking changes.
Why it's a problem: Using the secret key (sk_) in frontend code or committing it to Git
How to avoid: Store sk_ in environment variables and .env (add to .gitignore). Only pk_ keys go to the frontend.
Why it's a problem: Placing express.json() before the webhook route
How to avoid: The webhook route needs express.raw(). Define it before any express.json() middleware.
Why it's a problem: Passing amount as dollars instead of cents
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
How do I set up the Stripe API with Node.js? I need to install the SDK, configure it with environment variables, create customers, process payments with PaymentIntents, and handle webhooks. Show me a complete Express.js server.
Build a complete Stripe payment server with Node.js and Express. I need endpoints for creating customers, creating PaymentIntents, retrieving payment status, and a webhook handler with signature verification. Include proper error handling.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation