skillmake
← marketplace
creatorsconceptsha:518039b23eece6a9manual

course-landing-with-stripe

Use when shipping a course or digital-product landing page with email capture, Stripe Checkout, webhook fulfillment, and post-purchase drip emails — end-to-end in one pass with no SaaS subscription.

Tutorials · creator-attached
One-line install
curl --create-dirs -fsSL https://skillmake.xyz/i/course-landing-with-stripe -o ~/.claude/skills/course-landing-with-stripe/SKILL.md

The hash above pins this exact content. The file we serve at /api/marketplace/course-landing-with-stripe-518039b2/raw always matches sha:518039b23eece6a9.

3,902 chars · ~976 tokens
---
name: course-landing-with-stripe
description: Use when shipping a course or digital-product landing page with email capture, Stripe Checkout, webhook fulfillment, and post-purchase drip emails — end-to-end in one pass with no SaaS subscription.
source: https://docs.stripe.com/checkout/quickstart
generated: 2026-05-07T21:42:19.592Z
category: concept
audience: creators
---

## Tutorials

- https://skillmake.xyz/v/course-landing-with-stripe.mp4

## When to use

- Selling a course / template / mini-product without using Gumroad/Teachable/Podia
- Owning the customer email + analytics rather than handing them to a platform
- Wiring Stripe Checkout → webhook → grant access → drip onboarding emails
- Adding upsell/order-bump in a way that keeps the checkout single-page

## Key concepts

### Checkout vs Payment Intents

Stripe Checkout is the hosted page — fastest to ship, takes 30 minutes, handles taxes/cards/Apple Pay automatically. Payment Intents is for custom UI; only worth it if Checkout's brand fit blocks the sale. Default: Checkout.

### fulfillment via webhook

Stripe redirects on success but you can't trust client-side success. The webhook (checkout.session.completed) is the source of truth for fulfillment: grant access, send delivery email, log to analytics. Verify the signature with stripe.webhooks.constructEvent.

### drip onboarding

5-email sequence triggered on purchase: day 0 (delivery + welcome), day 1 (first lesson + community link), day 3 (second lesson + ask for feedback), day 7 (bonus + cross-sell), day 30 (testimonial ask). Resend + Inngest is the cleanest stack for this.

## API reference

```
Create Checkout Session
```

Server-side route the landing page POSTs to. Returns the hosted Checkout URL; client redirects.

```
// app/api/checkout/route.ts
import Stripe from 'stripe';
import { NextResponse } from 'next/server';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
  const { email } = await req.json();
  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    customer_email: email,
    line_items: [{ price: process.env.STRIPE_PRICE_ID!, quantity: 1 }],
    success_url: `${process.env.SITE_URL}/thanks?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.SITE_URL}/`,
    allow_promotion_codes: true,
  });
  return NextResponse.json({ url: session.url });
}
```

```
Webhook handler with signature verification
```

Verifies the Stripe signature, then fulfills the order. Anything not behind this verification is unsafe.

```
// app/api/stripe-webhook/route.ts
export const runtime = 'nodejs';
export async function POST(req: Request) {
  const sig = req.headers.get('stripe-signature')!;
  const body = await req.text();
  let event;
  try {
    event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response('bad sig', { status: 400 });
  }
  if (event.type === 'checkout.session.completed') {
    const s = event.data.object;
    await grantAccess(s.customer_email!, s.metadata?.product_id);
    await sendDeliveryEmail(s.customer_email!);
    await scheduleDrip(s.customer_email!);
  }
  return new Response('ok');
}
```

## Gotchas

- Never grant access from the success page — always wait for the webhook. Otherwise a bad actor can spoof the success URL.
- Set the webhook to deliver to your prod URL, not localhost. Use Stripe CLI's `stripe listen --forward-to` for local dev.
- Tax handling: enable automatic_tax in Checkout if you sell internationally — manual tax math gets you in trouble fast.
- Always store the email + Stripe customer id in your DB on fulfillment so refunds and re-deliveries work without searching Stripe.

---
Generated by SkillMake from https://docs.stripe.com/checkout/quickstart on 2026-05-07T21:42:19.592Z.
Verify against source before relying on details.

File: ~/.claude/skills/course-landing-with-stripe/SKILL.md