Docs/Tutorial
// tutorial
Ricevere pagamenti (Stripe)
Stripe è già cablato: bottone di checkout lato client, sessione creata lato server, webhook che aggiorna il database quando arriva il pagamento. Tu devi solo definire i piani.
1. Definisci un piano in config.ts
I piani vivono in config.ts sotto stripe.plans. Ogni piano ha un priceId (creato su Stripe), un nome, un prezzo e una lista di feature:
stripe: {
plans: [
{
priceId: process.env.NODE_ENV === "development"
? "price_xxxTest"
: "price_xxxProd",
name: "Starter",
description: "Tutto quello che ti serve per partire",
price: 199,
priceAnchor: 249,
features: [
{ name: "Repo completo" },
{ name: "1 anno di aggiornamenti" },
],
},
],
},Vai su dashboard.stripe.com/products, crea un prodotto, copia il price_... e incollalo qui. Un priceId per la modalità test, uno per la produzione.
2. Il bottone Checkout
<ButtonCheckout /> è già usato dentro <Pricing />. Ricapitolando: il click chiama l'API /api/stripe/create-checkout, riceve un URL di Stripe e ci redirige l'utente.
const { url } = await apiClient.post("/stripe/create-checkout", {
priceId,
successUrl: window.location.href,
cancelUrl: window.location.href,
mode, // "payment" o "subscription"
});
window.location.href = url;3. La sessione lato server
Dentro app/api/stripe/create-checkout/route.ts recuperiamo l'utente Supabase, leggiamo il suo profilo (per precompilare email e customer_id) e chiamiamo createCheckout da libs/stripe.ts:
const stripeSession = await stripe.checkout.sessions.create({
mode,
line_items: [{ price: priceId, quantity: 1 }],
client_reference_id: user?.id,
success_url: successUrl,
cancel_url: cancelUrl,
allow_promotion_codes: true,
});
return stripeSession.url;Nota il client_reference_id: passiamo lì l'ID Supabase dell'utente, così quando il webhook riceve l'evento sappiamo esattamente a chi dare accesso.
4. Il webhook
Quando Stripe conferma il pagamento manda un POST a /api/webhook/stripe. Sull'evento checkout.session.completed aggiorniamo il profilo nell'utente Supabase:
case "checkout.session.completed": {
const session = await findCheckoutSession(stripeObject.id);
const customerId = session?.customer;
const priceId = session?.line_items?.data[0]?.price.id;
const userId = stripeObject.client_reference_id;
await supabase
.from("profiles")
.update({
customer_id: customerId,
price_id: priceId,
has_access: true,
})
.eq("id", userId);
break;
}Gestiamo anche customer.subscription.deleted, invoice.paid e invoice.payment_failed — per gli abbonamenti ricorrenti. Dai un'occhiata al file, è tutto commentato.
5. Testare il webhook in locale
Installa la Stripe CLI, poi apri un secondo terminale mentre npm run dev gira:
stripe login
stripe listen --forward-to localhost:3000/api/webhook/stripeLa CLI ti stampa un whsec_...: mettilo in .env.local come STRIPE_WEBHOOK_SECRET. Adesso ogni pagamento test triggera il webhook sul tuo localhost.