fix: update lessons
This commit is contained in:
204
Les17-Externe-APIs/Les17-Lesopdracht.md
Normal file
204
Les17-Externe-APIs/Les17-Lesopdracht.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user