Prompt-to-PR: Eine Preisseite hinzufügen
SOP zum Erstellen einer Produktions-Preisseite mit monatlichem/jährlichem Umschalter, Feature-Matrix und Stripe Checkout-Links in einem Next.js Tailwind-Projekt.
CursorClaude CodeCodexWindsurf Next.jsTypeScriptTailwind
Eine der am häufigsten generierten Seiten in KI-Codierungssitzungen. Dieses Playbook zwingt den Agenten, echte, eingebundene Stripe Checkout-Links zu erstellen, anstatt Platzhalter-Buttons.
1. Anforderung
Füge eine /pricing-Seite mit zwei oder drei Tarifen, einem monatlichen/jährlichen Abrechnungsumschalter (jährlich gibt 20% Rabatt), einer Funktionsvergleichsmatrix und Stripe Checkout-Integration hinzu. Preise müssen aus Umgebungsvariablen stammen, nicht in JSX hartcodiert sein.
2. Erster Prompt
Add a /pricing page to this Next.js 15 App Router project with Tailwind.
Requirements:1. Create `src/app/pricing/page.tsx` — a Server Component that renders `<PricingCards>`.2. Create `src/components/PricingCards.tsx` — a Client Component with: - Monthly/annual toggle (useState). Annual prices are monthly × 0.8. - Three tiers: Hobby (free), Pro, Business. Read plan data from `src/config/pricing.ts` — not hardcoded in JSX. - A feature matrix table below the cards showing which features each tier includes. Use checkmarks and dashes; do not use emojis in code. - A "Get started" CTA for Hobby (links to /sign-up) and "Subscribe" for paid tiers.3. Create `src/config/pricing.ts` exporting a `PLANS` array. Each plan has: { id, name, monthlyPriceUsd, stripePriceIdMonthly, stripePriceIdAnnually, features: string[], highlighted: boolean } Read Stripe price IDs from env vars: NEXT_PUBLIC_STRIPE_PRICE_PRO_MONTHLY NEXT_PUBLIC_STRIPE_PRICE_PRO_ANNUALLY NEXT_PUBLIC_STRIPE_PRICE_BIZ_MONTHLY NEXT_PUBLIC_STRIPE_PRICE_BIZ_ANNUALLY4. Create `src/app/api/checkout/route.ts` (POST). Accept `{ priceId, userId }`. Create a Stripe Checkout Session (mode: "subscription") with: - success_url: NEXT_PUBLIC_APP_URL + /dashboard?checkout=success - cancel_url: NEXT_PUBLIC_APP_URL + /pricing Return `{ url }`.5. In PricingCards, the Subscribe button POSTs to /api/checkout and redirects to the returned url.6. Install `stripe` package. Use env var STRIPE_SECRET_KEY.3. Erwartete Dateiänderungen
package.json (stripe)src/app/pricing/page.tsx (new — Server Component shell)src/components/PricingCards.tsx (new — Client Component with toggle)src/config/pricing.ts (new — plan definitions)src/app/api/checkout/route.ts (new — Stripe Checkout Session).env.local.example (STRIPE_* and NEXT_PUBLIC_* vars)4. Review-Checkliste
STRIPE_SECRET_KEYwird nie in einem Client-Komponenten importiert oder überNEXT_PUBLIC_offengelegt.- Der Checkout-Session verwendet
mode: "subscription"nichtmode: "payment"für wiederkehrende Abrechnung. - Jährliche Preis-IDs sind separate Stripe-Preisobjekte – kein berechneter Rabatt im Code.
- Der Umschalter zeigt jährliche Ersparnisse deutlich an (z.B. „20% sparen“).
- Das
PLANS-Array ist die einzige Quelle der Wahrheit – die Funktionsmatrix verwendet dieselben Daten, nicht eine separate hartcodierte Tabelle. - Fehlerbehandlung beim Checkout-POST: Gibt einen 500 mit einer Nachricht zurück, wenn Stripe einen Fehler wirft.
- Kein
console.logvon Stripe-Schlüsseln oder Kundendaten im Code.
5. Testbefehle
bun dev
# Toggle monthly/annual — confirm prices update correctly# Confirm annual = monthly * 0.8
# Test checkout endpoint with a test price IDcurl -X POST http://localhost:3000/api/checkout \ -H "Content-Type: application/json" \ -d '{"priceId":"price_test_xxx","userId":"user_1"}' | jq .url# Expect a Stripe Checkout URL
bun tsc --noEmit6. Häufige Fehler
STRIPE_SECRET_KEYclientseitig durchgesickert – Agent importiertstripeinPricingCards.tsx. Verschiebe alle Stripe-Aufrufe in die API-Route.- Jährlicher Umschalter zeigt denselben Preis wie monatlich – Agent berechnet
price * 0.8vergisst aber, denisAnnual-Zustand zu verwenden. Bestätige, dass der Umschalterzustand an die Preisanzeige übergeben wird. - Checkout gibt 500 mit „No such price“ zurück – Preis-ID-Umgebungsvariable nicht gesetzt oder falsche Umgebung (Test vs. Live). Überprüfe mit
stripe prices retrieve <id>in der Stripe CLI. success_urlist relativ – Stripe benötigt eine absolute URL. FügeNEXT_PUBLIC_APP_URLvoran.
7. Fehlerbehebungsprompt
The annual/monthly toggle renders correctly visually but the Subscribe buttonalways sends the monthly priceId regardless of which toggle state is active.
In PricingCards.tsx, the `priceId` passed to the checkout POST must be`isAnnual ? plan.stripePriceIdAnnually : plan.stripePriceIdMonthly`.Check that the isAnnual state variable is read at the point the button'sonClick handler calls the checkout API.8. PR-Beschreibung
## Feature: Pricing page with Stripe Checkout
- `/pricing` page with monthly/annual toggle and three tiers (Hobby, Pro, Business)- Feature matrix driven from `src/config/pricing.ts` — one source of truth- POST `/api/checkout` creates a Stripe Subscription Checkout Session- Price IDs read from env vars — no hardcoded Stripe IDs in code- Annual plan = 20% discount via separate Stripe Price objects
**Required env vars**: `STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PRICE_*`,`NEXT_PUBLIC_APP_URL` (see `.env.local.example`)