Docs/Componenti
// componente
Button Checkout
Il bottone che porta l'utente su Stripe Checkout. Crea la sessione lato server e poi fa un redirect completo del browser. È il bottone che <Pricing /> renderizza dentro ogni card di prezzo. Client component.
File
components/ButtonCheckout.tsx
Uso base
import ButtonCheckout from "@/components/ButtonCheckout";
// pagamento una tantum
<ButtonCheckout priceId={plan.priceId} />
// abbonamento ricorrente, sul piano evidenziato
<ButtonCheckout
priceId={plan.priceId}
mode="subscription"
featured={plan.isFeatured}
/>Props
priceId: string— obbligatoria. È ilprice_...di Stripe. Nel boilerplate arriva daconfig.stripe.plans[].priceId.mode?: "payment" | "subscription"— default"payment". Deve combaciare con il tipo di price che hai creato su Stripe: un price ricorrente conmode: "payment"fa fallire la sessione.featured?: boolean— defaultfalse. Cambia solo lo stile:btn-brutal-mustardsetrue,btn-brutal-primarysefalse.
L'etichetta è “Ottieni {config.appName}”: prende il nome da config.ts, non c'è una prop per il testo.
Il giro completo
Al click il componente manda:
const { url }: { url: string } = await apiClient.post(
"/stripe/create-checkout",
{
priceId,
successUrl: window.location.origin + "/checkout-success",
cancelUrl: window.location.href,
mode,
}
);
window.location.href = url;Nota che successUrl e cancelUrl sono calcolati nel browser: il successo va sempre su /checkout-success, l'annullamento riporta esattamente sulla pagina da cui hai cliccato.
app/api/stripe/create-checkout/route.ts valida priceId, le due URL e mode (400 se manca qualcosa), legge l'utente Supabase dalla sessione, recupera la sua riga profiles e passa a createCheckout di libs/stripe.ts:
clientReferenceId: user?.id— così il webhook sa a chi assegnare l'acquisto;user.emaileuser.customerId— per precompilare il Checkout e riusare la carta già salvata.
La route risponde { url } e il componente fa il redirect. Se qualcosa va storto, l'interceptor di libs/api.ts mostra il toast con il messaggio di errore; su 401 spedisce l'utente su config.auth.loginUrl.
Personalizzare
- Pagina di successo: cambia
"/checkout-success"nel componente se vuoi un'altra landing post-pagamento. - Coupon: la route ha già il punto pronto — decommenta
couponId: body.couponIde passalo dal componente nel body della richiesta. - Login obbligatorio: di default un utente anonimo può comunque aprire il Checkout (senza
clientReferenceId). Se vuoi forzare il login, aggiungi un401nella route quandouserè nullo. - Etichetta e icona: il testo e il fulmine SVG sono inline nel componente.