4.5 KiB
4.5 KiB
Les 17 — Lesopdracht
Stripe Checkout setup + eerste test-betaling
Duur: 30 min in-class
Doel
Werkende Stripe Checkout in een Next.js app — pricing page, checkout session, en een succesvolle test-betaling.
Stap 1 — Stripe account
- Maak gratis account op https://stripe.com (skip business questions met "test mode")
- Dashboard → Test mode (toggle rechtsboven)
- Developers → API keys → kopieer:
Publishable key(pk_test_...)Secret key(sk_test_...)
Stap 2 — Product + Price aanmaken
In Stripe Dashboard:
- Products → Add product
- Naam: "Premium plan"
- Price: € 9.99 / maand, recurring (subscription)
- Save → kopieer
price_xxxxxvan de price die je net maakte
Stap 3 — Next.js project
pnpm create next-app@latest stripe-demo \
--typescript --tailwind --app --no-src-dir --import-alias "@/*"
cd stripe-demo
pnpm add stripe
.env.local:
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PRICE_ID=price_...
APP_URL=http://localhost:3000
Stap 4 — Pricing page
app/page.tsx:
"use client";
export default function Pricing() {
async function subscribe() {
const res = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
priceId: process.env.NEXT_PUBLIC_STRIPE_PRICE_ID,
email: "test@example.com",
}),
});
const { url } = await res.json();
window.location.href = url;
}
return (
<main className="max-w-md mx-auto p-12 text-center">
<h1 className="text-4xl font-bold mb-4">Premium Plan</h1>
<p className="text-2xl mb-2">€ 9.99 / maand</p>
<ul className="text-left my-6 space-y-2">
<li>✓ Onbeperkte projecten</li>
<li>✓ Priority support</li>
<li>✓ Advanced analytics</li>
</ul>
<button
onClick={subscribe}
className="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold"
>
Subscribe nu
</button>
</main>
);
}
Stap 5 — Checkout endpoint
app/api/checkout/route.ts:
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { priceId, email } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
customer_email: email,
success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/`,
});
return Response.json({ url: session.url });
}
Stap 6 — Success page
app/success/page.tsx:
export default function Success() {
return (
<main className="max-w-md mx-auto p-12 text-center">
<h1 className="text-4xl font-bold mb-4 text-green-600">✓ Betaling geslaagd!</h1>
<p>Bedankt voor je aanmelding. Je krijgt zo een welkomstmail.</p>
</main>
);
}
Stap 7 — Test
pnpm dev
Open http://localhost:3000:
- Klik "Subscribe nu"
- Stripe Checkout opent
- Vul test-card in:
- Card:
4242 4242 4242 4242 - Expiry:
12/34 - CVC:
123 - Postcode: willekeurig
- Card:
- Klik Subscribe
- Redirect naar
/success— werkt! - Check Stripe Dashboard → Payments — sessie zichtbaar
Eisen
- Stripe account in test mode
- Product + price aangemaakt
- Pricing page werkt
- Checkout endpoint werkt
- Test-betaling slaagt met 4242 card
- Success page laadt na betaling
- In Stripe Dashboard zie je de transactie
Tijdsindeling (30 min)
| Stap | Tijd |
|---|---|
| 1-2 — Stripe setup | 8 min |
| 3 — Project setup | 3 min |
| 4-5 — Pages + endpoint | 10 min |
| 6 — Success page | 2 min |
| 7 — Test | 7 min |
Veelvoorkomende problemen
| Symptoom | Oplossing |
|---|---|
Stripe is not defined |
pnpm add stripe opnieuw |
Invalid API key |
Check sk_test_ (niet pk) in .env.local |
priceId required |
Check NEXT_PUBLIC_ prefix correct |
| 4242 card geweigerd | Check je staat in test mode (rechtsboven dashboard) |
| Success URL 404 | App URL klopt? http://localhost:3000 (geen trailing slash) |
Klaar? Verder met huiswerk
Het huiswerk voegt toe:
- Webhook handler met signature verify
- Resend welkomstmail bij betaling
- Retry logic voor externe calls
APIS.mdmet je eindopdracht-toepassing
Zie Les17-Huiswerk.md.