# How to create a Checkout Session with Stripe

- Tool: Stripe
- Difficulty: Beginner
- Time required: 15 minutes
- Compatibility: Stripe API v2024-12+, Node.js 18+, any frontend framework
- Last updated: March 2026

## TL;DR

Create a Stripe Checkout Session by calling stripe.checkout.sessions.create() on your server with line_items, mode, and success/cancel URLs. Stripe hosts the entire payment page so you never handle card data directly. Use test mode with card 4242 4242 4242 4242 to verify before going live.

## What Is Stripe Checkout and Why Use It?

Stripe Checkout is a prebuilt, hosted payment page that handles card collection, validation, 3D Secure, and compliance for you. Instead of building a custom payment form, you redirect users to a Stripe-hosted page. After payment, Stripe redirects them back to your success URL. This is the fastest way to accept payments — you only need a few lines of server-side code and zero frontend payment UI work.

## Before you start

- A Stripe account (free to create at dashboard.stripe.com)
- Node.js 18 or newer installed
- Your Stripe secret key (sk_test_...) from the Stripe Dashboard → Developers → API keys
- A basic Express server or any Node.js HTTP framework

## Step-by-step guide

### 1. Install the Stripe SDK

Install the official Stripe Node.js library. This SDK handles authentication, request signing, and API versioning for you.

```
npm install stripe express
```

**Expected result:** stripe and express packages appear in your node_modules and package.json dependencies.

### 2. Initialize Stripe with your secret key

Import Stripe and initialize it with your secret key. NEVER use your secret key (sk_) on the frontend — it must stay on your server. Use environment variables to keep it out of source code.

```
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
```

**Expected result:** The stripe object is ready to make API calls.

### 3. Create the Checkout Session endpoint

Create a POST endpoint that builds a Checkout Session. Specify line_items with price_data (or an existing Price ID), the mode ('payment' for one-time), and success/cancel URLs. Stripe returns a session URL you redirect the customer to.

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

app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: {
              name: 'Premium Widget',
              description: 'A high-quality widget',
            },
            unit_amount: 2000, // $20.00 in cents
          },
          quantity: 1,
        },
      ],
      success_url: 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
      cancel_url: 'https://yoursite.com/cancel',
    });

    res.json({ url: session.url });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});
```

**Expected result:** The endpoint returns a JSON object with a Stripe-hosted checkout URL.

### 4. Redirect the customer to Checkout

On your frontend, call your server endpoint and redirect the browser to the returned URL. This takes the customer to Stripe's hosted payment page.

```
// Frontend JavaScript
const response = await fetch('/create-checkout-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
});
const { url } = await response.json();
window.location.href = url;
```

**Expected result:** The browser redirects to a Stripe-hosted checkout page showing the product, price, and payment form.

### 5. Test with a Stripe test card

Use Stripe's test card number to simulate a successful payment. No real money is charged in test mode.

```
// Test card details:
// Number: 4242 4242 4242 4242
// Expiry: any future date (e.g., 12/34)
// CVC: any 3 digits (e.g., 123)
// ZIP: any 5 digits (e.g., 10001)
```

**Expected result:** After entering test card details and clicking Pay, Stripe redirects to your success_url with the session_id parameter appended.

## Complete code example

File: `server.js`

```javascript
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const app = express();
app.use(express.static('public'));
app.use(express.json());

// Create a Checkout Session
app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: {
              name: 'Premium Widget',
              description: 'A high-quality widget',
            },
            unit_amount: 2000, // $20.00 in cents
          },
          quantity: 1,
        },
      ],
      success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${req.headers.origin}/cancel`,
    });

    res.json({ url: session.url });
  } catch (err) {
    console.error('Checkout session error:', err.message);
    res.status(500).json({ error: err.message });
  }
});

// Verify session after payment (optional but recommended)
app.get('/session-status', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.retrieve(req.query.session_id);
    res.json({
      status: session.payment_status,
      customer_email: session.customer_details?.email,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

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

## Common mistakes

- **Using the secret key (sk_) on the frontend** — undefined Fix: Secret keys must only be used on your server. On the frontend, use the publishable key (pk_) for Stripe.js. Checkout Sessions are created server-side.
- **Passing amounts in dollars instead of cents** — undefined Fix: Stripe expects amounts in the smallest currency unit. For USD, pass 2000 for $20.00, not 20.
- **Hardcoding success_url without {CHECKOUT_SESSION_ID}** — undefined Fix: Include {CHECKOUT_SESSION_ID} in your success_url as a template variable so you can verify the session on your success page.
- **Not handling the cancel URL** — undefined Fix: Always provide a cancel_url. Users who click back or close the page land here. Show a friendly message, not a 404.

## Best practices

- Always use environment variables for your Stripe secret key — never commit it to source control
- Use test mode (sk_test_) during development and test with card 4242 4242 4242 4242
- Include {CHECKOUT_SESSION_ID} in your success_url to verify payment completion server-side
- Set up a webhook for checkout.session.completed instead of relying solely on the success redirect
- Specify amounts in the smallest currency unit (cents for USD, pence for GBP)
- Use price_data for dynamic pricing or pre-created Price IDs from the Dashboard for fixed catalog items
- Add metadata to your Checkout Session to link payments to your internal records

## Frequently asked questions

### Can I customize the look of Stripe Checkout?

Yes. In the Stripe Dashboard under Settings → Branding, you can set your logo, brand color, accent color, and icon. Checkout automatically uses these. For deeper customization, consider Stripe Elements instead.

### Do I need HTTPS for Stripe Checkout?

Stripe Checkout itself is always HTTPS since it's hosted by Stripe. Your success and cancel URLs should use HTTPS in production. In local development, localhost works fine.

### How do I know if the payment actually succeeded?

Do not rely solely on the success_url redirect — users can navigate there manually. Set up a webhook listener for the checkout.session.completed event to confirm payment server-side.

### Can I accept multiple items in one Checkout Session?

Yes. Add multiple objects to the line_items array. Each can have its own price_data, quantity, and product details. Stripe displays all items on the checkout page.

### What if I need help integrating Stripe into a larger application?

For complex payment flows — multi-vendor marketplaces, usage-based billing, or enterprise SaaS — the RapidDev team can help you architect and implement a production-ready Stripe integration.

### Does Stripe Checkout support Apple Pay and Google Pay?

Yes. Stripe Checkout automatically enables Apple Pay, Google Pay, and Link (Stripe's one-click checkout) when available. No extra configuration is needed.

---

Source: https://www.rapidevelopers.com/stripe-guide/how-to-create-a-checkout-session-with-stripe
© RapidDev — https://www.rapidevelopers.com/stripe-guide/how-to-create-a-checkout-session-with-stripe
