All topics
Frontend · Learning hub

Stripe notes for developers

Master Stripe with a curated set of 1 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore Frontend notes
Stripe

Stripe Integration

Stripe Integration Checkout & Payments import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-06-20', }); //

Stripe Integration

Checkout & Payments

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-06-20',
});

// Create Checkout Session (hosted page)
// app/api/checkout/route.ts
export async function POST(req: Request) {
  const { priceId, userId } = await req.json();

  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',         // or 'payment' for one-time
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
    metadata: { userId },          // pass your own data through
    customer_email: user.email,    // pre-fill email
    allow_promotion_codes: true,
  });

  return Response.json({ url: session.url });
}

// Client — redirect to Stripe
const { url } = await fetch('/api/checkout', {
  method: 'POST',
  body: JSON.stringify({ priceId: 'price_123' }),
}).then(r => r.json());
window.location.href = url;

// Payment Intent (custom UI with Stripe Elements)
const paymentIntent = await stripe.paymentIntents.create({
  amount: 2000,         // in smallest currency unit (cents for USD)
  currency: 'usd',
  automatic_payment_methods: { enabled: true },
  metadata: { orderId: '123' },
});
// Return client_secret to frontend to complete payment

Webhooks

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';

export async function POST(req: Request) {
  const body = await req.text();
  const sig = headers().get('stripe-signature')!;

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    return new Response(`Webhook Error: ${err.message}`, { status: 400 });
  }

  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object as Stripe.Checkout.Session;
      const userId = session.metadata?.userId;
      const subscriptionId = session.subscription as string;
      await db.subscription.create({
        data: { userId, stripeSubscriptionId: subscriptionId, status: 'active' },
      });
      break;
    }

    case 'customer.subscription.updated': {
      const sub = event.data.object as Stripe.Subscription;
      await db.subscription.update({
        where: { stripeSubscriptionId: sub.id },
        data: { status: sub.status },
      });
      break;
    }

    case 'customer.subscription.deleted': {
      const sub = event.data.object as Stripe.Subscription;
      await db.subscription.update({
        where: { stripeSubscriptionId: sub.id },
        data: { status: 'canceled' },
      });
      break;
    }

    case 'invoice.payment_failed': {
      const invoice = event.data.object as Stripe.Invoice;
      // notify user, retry logic
      break;
    }
  }

  return new Response(null, { status: 200 });
}

// Test webhooks locally
// stripe listen --forward-to localhost:3000/api/webhooks/stripe

Customer Portal & Subscriptions

// Customer portal — let users manage subscription themselves
const portalSession = await stripe.billingPortal.sessions.create({
  customer: user.stripeCustomerId,
  return_url: `${process.env.NEXT_PUBLIC_URL}/settings`,
});
return Response.json({ url: portalSession.url });

// Create/retrieve customer
const customer = await stripe.customers.create({
  email: user.email,
  name: user.name,
  metadata: { userId: user.id },
});

// Retrieve subscription with items
const subscription = await stripe.subscriptions.retrieve(
  subscriptionId,
  { expand: ['items.data.price.product'] }
);
const plan = (subscription.items.data[0].price.product as Stripe.Product).name;

// Prices & Products (retrieve from dashboard or API)
const prices = await stripe.prices.list({
  product: 'prod_123',
  active: true,
  expand: ['data.product'],
});

// Stripe CLI shortcuts
// stripe login
// stripe listen --forward-to localhost:3000/api/webhooks/stripe
// stripe trigger checkout.session.completed
// stripe customers list
// stripe subscriptions list

Keep your Stripe knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever