205 lines
4.5 KiB
Markdown
205 lines
4.5 KiB
Markdown
# 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
|
|
|
|
1. Maak gratis account op https://stripe.com (skip business questions met "test mode")
|
|
2. Dashboard → Test mode (toggle rechtsboven)
|
|
3. Developers → API keys → kopieer:
|
|
- `Publishable key` (`pk_test_...`)
|
|
- `Secret key` (`sk_test_...`)
|
|
|
|
---
|
|
|
|
## Stap 2 — Product + Price aanmaken
|
|
|
|
In Stripe Dashboard:
|
|
|
|
1. Products → Add product
|
|
2. Naam: "Premium plan"
|
|
3. Price: € 9.99 / maand, recurring (subscription)
|
|
4. Save → kopieer `price_xxxxx` van de price die je net maakte
|
|
|
|
---
|
|
|
|
## Stap 3 — Next.js project
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```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`:
|
|
|
|
```typescript
|
|
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`:
|
|
|
|
```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
|
|
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
Open http://localhost:3000:
|
|
|
|
1. Klik "Subscribe nu"
|
|
2. Stripe Checkout opent
|
|
3. Vul test-card in:
|
|
- Card: `4242 4242 4242 4242`
|
|
- Expiry: `12/34`
|
|
- CVC: `123`
|
|
- Postcode: willekeurig
|
|
4. Klik Subscribe
|
|
5. Redirect naar `/success` — werkt!
|
|
6. 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.md` met je eindopdracht-toepassing
|
|
|
|
Zie `Les17-Huiswerk.md`.
|