### Run Stripe Fixtures Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Use Stripe CLI fixtures to configure products without manual UI setup. Ensure you update the command with your Stripe secret key. ```bash stripe fixtures ./stripe-fixtures.json --api-key UPDATE_THIS_WITH_YOUR_STRIPE_SK ``` -------------------------------- ### Supabase Migration: Initial Schema Setup Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt This SQL script sets up the initial database schema for Supabase, including tables for users, customers, products, prices, and subscriptions. It also includes functions for handling new user signups and enabling real-time functionality for specific tables. ```sql --supabase/migrations/20240115041359_init.sql -- Run migration against linked Supabase project: -- bun run migration:up -- Tables created: -- users → id (uuid, FK auth.users), full_name, avatar_url, billing_address (jsonb), payment_method (jsonb) -- customers → id (uuid, FK auth.users), stripe_customer_id (text) [private, no RLS policies] -- products → id (text PK), active, name, description, image, metadata (jsonb) [public read] -- prices → id (text PK), product_id (FK products), active, unit_amount, currency, type, interval, ... -- subscriptions → id (text PK), user_id (FK auth.users), status (enum), price_id (FK prices), ... -- Auto-create user row on auth signup: create function public.handle_new_user() returns trigger as $$ begin insert into public.users (id, full_name, avatar_url) values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); return new; end; $$ language plpgsql security definer; create trigger on_auth_user_created after insert on auth.users for each row execute procedure public.handle_new_user(); -- Realtime enabled for products and prices: create publication supabase_realtime for table products, prices; ``` -------------------------------- ### getSubscription Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Get the user's active or trialing subscription, including related price and product data. ```APIDOC ## getSubscription ### Description Queries the `subscriptions` table for an `active` or `trialing` subscription, joining related price and product data. ### Method ``` async function getSubscription() ``` ### Returns - `data` (object | null) - The subscription object, including nested `prices` and `products` data. Returns null if no active or trialing subscription is found or if an error occurs. ``` -------------------------------- ### GET /manage-subscription Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Redirects the user to the Stripe Customer Portal to manage their subscription. Requires authentication. ```APIDOC ## `GET /manage-subscription` — Redirect to Stripe Customer Portal Creates a Stripe Billing Portal session for the current user and redirects them to it. The portal allows customers to update payment methods, view invoices, and cancel subscriptions. ### Usage In the Account page: ```typescript ``` ### Internals 1. `getSession()` — throws if not authenticated 2. `getCustomerId({ userId })` — fetches `stripe_customer_id` 3. `stripe.billingPortal.sessions.create()` with `customer` and `return_url` 4. `redirect(portalUrl)` — sends user to Stripe-hosted portal ``` -------------------------------- ### GET /auth/callback Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Handles the OAuth callback from Supabase. It exchanges the auth code for a session, checks the user's subscription status, and redirects accordingly. ```APIDOC ## GET /auth/callback ### Description Exchanges the Supabase auth code for a session, checks subscription status, and redirects to `/pricing` if the user has no active subscription, or to `/` if they do. ### Method GET ### Endpoint `/auth/callback` ### Query Parameters - **code** (string) - Required - The authorization code received from Supabase. ### Response Redirects to `/` or `/pricing` based on subscription status. ``` -------------------------------- ### Get Active User Subscription Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Queries the 'subscriptions' table for active or trialing subscriptions, including related price and product information. Returns null if no active subscription is found. ```typescript // src/features/account/controllers/get-subscription.ts export async function getSubscription() { const supabase = await createSupabaseServerClient(); const { data, error } = await supabase .from('subscriptions') .select('*, prices(*, products(*))') .in('status', ['trialing', 'active']) .maybeSingle(); if (error) console.error(error); return data; } // Usage in AccountPage: const subscription = await getSubscription(); // subscription.status → 'active' // subscription.price_id → 'price_xxx' // subscription.prices.unit_amount → 1000 // subscription.prices.products.name → 'Pro' ``` -------------------------------- ### getSession Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Get the current server-side session. Used in Server Components and API routes to verify authentication. ```APIDOC ## getSession ### Description Get the current server-side session. Used in Server Components and API routes to verify authentication. ### Method ``` async function getSession() ``` ### Returns - `session` (object | null) - The active Supabase session or null if not authenticated. ``` -------------------------------- ### Get Server-Side Supabase Session Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Retrieves the active Supabase session from the server. Essential for Server Components and API routes to authenticate requests. ```typescript // src/features/account/controllers/get-session.ts import { createSupabaseServerClient } from '@/libs/supabase/supabase-server-client'; export async function getSession() { const supabase = await createSupabaseServerClient(); const { data, error } = await supabase.auth.getSession(); if (error) console.error(error); return data.session; } // Usage in a Server Component: export default async function Navigation() { const session = await getSession(); // session.user.id, session.user.email, etc. return session ? : ; } ``` -------------------------------- ### Get or Create Stripe Customer Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Finds an existing Stripe customer ID for a user or creates a new one in Stripe and Supabase if it doesn't exist. Returns the Stripe customer ID. ```typescript // src/features/account/controllers/get-or-create-customer.ts const customerId = await getOrCreateCustomer({ userId: 'uuid-from-supabase-auth', email: 'user@example.com', }); // If no record in customers table: // → stripe.customers.create({ email, metadata: { userId } }) // → supabase.from('customers').insert([{ id: userId, stripe_customer_id: 'cus_xxx' }]) // → returns 'cus_xxx' // If record exists: // → returns existing 'cus_xxx' immediately ``` -------------------------------- ### Deploy to Vercel Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Use this button to quickly deploy the starter project to Vercel. Ensure you have the necessary environment variables configured during the deployment process. ```bash bunx supabase login bunx supabase init bun run supabase:link bun run migration:up ``` -------------------------------- ### Send Welcome Email with Resend Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Uses a React Email component for welcome emails, sent via the Resend SDK. Styled with Tailwind CSS. Preview emails locally or export to HTML. ```typescript // src/features/emails/welcome.tsx + src/libs/resend/resend-client.ts import WelcomeEmail from '@/features/emails/welcome'; import { resendClient } from '@/libs/resend/resend-client'; // Send a welcome email after successful signup: await resendClient.emails.send({ from: 'no-reply@yourapp.com', to: 'newuser@example.com', subject: 'Welcome!', react: , }); // Preview emails locally: // bun run email:dev → opens React Email dev server on port 3001 // Export to HTML: // bun run email:export ``` -------------------------------- ### Create Supabase Database Migration Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Create a new migration file for adding a database table. This command generates the migration file, which you then edit with your SQL schema. ```bash npm run migration:new add-invites-table ``` -------------------------------- ### Sign in with Email using Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Sends a passwordless magic link (OTP) to the user's email via Supabase Auth. The user clicks the link in the email to complete sign-in via `/auth/callback`. Ensure email configurations are set up in Supabase. ```typescript // src/app/(auth)/auth-actions.ts export async function signInWithEmail(email: string) { const supabase = await createSupabaseServerClient(); const { error } = await supabase.auth.signInWithOtp({ email, options: { emailRedirectTo: getURL('/auth/callback'), }, }); if (error) { console.error(error); return { data: null, error }; } // Returns { data: null, error: null } — email sent successfully return { data: null, error: null }; } // Usage in a client form handler: const response = await signInWithEmail('user@example.com'); if (response?.error) { toast({ variant: 'destructive', description: 'Authentication error. Please try again.' }); } else { toast({ description: `Check your inbox at: user@example.com` }); } ``` -------------------------------- ### getProducts Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Fetches all active products from Supabase, including their associated active prices. Results are ordered by metadata index and price amount. ```APIDOC ## `getProducts` — Fetch all active products with prices Retrieves all active products from Supabase, including their active prices, ordered by `metadata.index` and price amount. ### Usage ```typescript const products = await getProducts(); ``` ### Response Example (`ProductWithPrices[]`) ```json [ { "id": "prod_xxx", "name": "Basic", "description": "Perfect for small businesses.", "active": true, "metadata": { "price_card_variant": "basic", "generated_images": "10", ... }, "prices": [ { "id": "price_xxx", "unit_amount": 500, "interval": "month", "active": true }, { "id": "price_yyy", "unit_amount": 5000, "interval": "year", "active": true } ] }, { "name": "Pro", ... }, { "name": "Enterprise", "prices": [] } // no price = contact sales ] ``` ``` -------------------------------- ### Sign in with OAuth using Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Initiates an OAuth redirect flow for GitHub or Google sign-in. Redirects to `/auth/callback` on success. Ensure Supabase is configured for OAuth providers. ```typescript // src/app/(auth)/auth-actions.ts 'use server'; import { createSupabaseServerClient } from '@/libs/supabase/supabase-server-client'; import { getURL } from '@/utils/get-url'; export async function signInWithOAuth(provider: 'github' | 'google') { const supabase = await createSupabaseServerClient(); const { data, error } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo: getURL('/auth/callback'), // e.g. https://yourapp.com/auth/callback }, }); if (error) { console.error(error); return { data: null, error }; } // Redirects browser to GitHub/Google OAuth consent screen return redirect(data.url); } // Usage in a client component: await signInWithOAuth('google'); await signInWithOAuth('github'); ``` -------------------------------- ### Create Supabase Server Client Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Creates a Supabase client for Server Components and Route Handlers, using Next.js `cookies()` for session management. Reads/writes session cookies automatically. ```typescript // src/libs/supabase/supabase-server-client.ts const supabase = await createSupabaseServerClient(); // Reads/writes session cookies automatically const { data: { user } } = await supabase.auth.getUser(); const { data } = await supabase.from('subscriptions').select('*').eq('user_id', user.id); ``` -------------------------------- ### Fetch Products with Prices Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Controller function to retrieve all active products from Supabase, including their associated active prices. Results are ordered by metadata index and price amount. ```typescript // src/features/pricing/controllers/get-products.ts const products = await getProducts(); // Returns: ProductWithPrices[] // [ // { // id: 'prod_xxx', // name: 'Basic', // description: 'Perfect for small businesses.', // active: true, // metadata: { price_card_variant: 'basic', generated_images: '10', ... }, // prices: [ // { id: 'price_xxx', unit_amount: 500, interval: 'month', active: true }, // { id: 'price_yyy', unit_amount: 5000, interval: 'year', active: true }, // ] // }, // { name: 'Pro', ... }, // { name: 'Enterprise', prices: [] } // no price = contact sales // ] ``` -------------------------------- ### Utility: Construct Absolute URLs with getURL Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt This TypeScript utility constructs absolute URLs. It uses the NEXT_PUBLIC_SITE_URL environment variable in production and defaults to 'http://localhost:3000' in development. Ensure NEXT_PUBLIC_SITE_URL is set in your .env.local file for production builds. ```typescript //src/utils/get-url.ts import { getURL } from '@/utils/get-url'; getURL() // 'https://yourapp.com' getURL('/account') // 'https://yourapp.com/account' getURL('/auth/callback') // 'https://yourapp.com/auth/callback' // .env.local: // NEXT_PUBLIC_SITE_URL=https://yourapp.com ``` -------------------------------- ### getOrCreateCustomer Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Resolve or create a Stripe customer, linking it to a Supabase user. ```APIDOC ## getOrCreateCustomer ### Description Looks up the Stripe customer ID for a given user in the `customers` table. If none exists, creates a new Stripe customer and stores the mapping. ### Method ``` async function getOrCreateCustomer(options: { userId: string; email: string; }): Promise ``` ### Parameters - **userId** (string) - The Supabase user ID. - **email** (string) - The user's email address. ### Returns - `customerId` (string) - The Stripe customer ID. ``` -------------------------------- ### signInWithEmail Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Sends a passwordless magic link (OTP) to the user's email via Supabase Auth. The user receives an email with a link to complete the sign-in process. ```APIDOC ## signInWithEmail ### Description Sends a one-time password (OTP) magic link to the user's email via Supabase Auth. On success, the user receives an email with a link back to `/auth/callback`. ### Method Server Action (TypeScript) ### Parameters - **email** (string) - Required - The user's email address. ### Usage Example ```typescript const response = await signInWithEmail('user@example.com'); ``` ``` -------------------------------- ### OAuth Callback Route Handler Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Handles the callback from OAuth providers, exchanges the auth code for a session, and checks the user's subscription status. Redirects to `/pricing` for new users or `/` for existing subscribers. ```typescript // src/app/(auth)/auth/callback/route.ts export async function GET(request: NextRequest) { const requestUrl = new URL(request.url); const code = requestUrl.searchParams.get('code'); if (code) { const supabase = await createSupabaseServerClient(); await supabase.auth.exchangeCodeForSession(code); const { data: { user } } = await supabase.auth.getUser(); if (!user?.id) { return NextResponse.redirect(`${siteUrl}/login`); } // Check for active or trialing subscription const { data: userSubscription } = await supabase .from('subscriptions') .select('*, prices(*, products(*))') .in('status', ['trialing', 'active']) .maybeSingle(); // New users → pricing page; existing subscribers → home return userSubscription ? NextResponse.redirect(`${siteUrl}`) : NextResponse.redirect(`${siteUrl}/pricing`); } return NextResponse.redirect(siteUrl); } ``` -------------------------------- ### signInWithOAuth Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Initiates an OAuth redirect flow using Supabase Auth for GitHub or Google sign-in. It redirects to the callback route upon successful authentication. ```APIDOC ## signInWithOAuth ### Description Initiates an OAuth redirect flow using Supabase Auth. Accepts `'github'` or `'google'` as the provider and redirects to `/auth/callback` on success. ### Method Server Action (TypeScript) ### Parameters - **provider** (string) - Required - Either 'github' or 'google'. ### Usage Example ```typescript await signInWithOAuth('google'); await signInWithOAuth('github'); ``` ``` -------------------------------- ### createCheckoutAction Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Server action to create a Stripe Checkout session. Redirects unauthenticated users to `/signup`. Supports one-time and subscription payments. ```APIDOC ## `createCheckoutAction` — Create a Stripe Checkout session Server action that creates a Stripe Checkout session for a given price. Unauthenticated users are redirected to `/signup`. Supports both one-time and subscription pricing modes. ### Usage ```typescript // Called from PricingCard's "Get Started" button: await createCheckoutAction({ price: selectedPrice }); ``` ### Internals 1. Verifies session — redirects to `/signup` if unauthenticated 2. `getOrCreateCustomer({ userId, email })` 3. `stripe.checkout.sessions.create()` with appropriate parameters (`payment_method_types`, `customer`, `line_items`, `mode`, `allow_promotion_codes`, `success_url`, `cancel_url`) 4. Redirects to Stripe Checkout URL ``` -------------------------------- ### Apply Database Migrations Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Apply pending database migrations to update your schema. This command executes the SQL defined in your migration files. ```bash npm run migration:up ``` -------------------------------- ### Create Supabase Admin Client Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt A Supabase client initialized with the `SUPABASE_SERVICE_ROLE_KEY` that bypasses Row Level Security. Used exclusively in server-side webhook handlers and account management flows. Never expose this client to the browser. ```typescript // src/libs/supabase/supabase-admin.ts import { supabaseAdminClient } from '@/libs/supabase/supabase-admin'; // Used in webhook handler to write customer/subscription data: await supabaseAdminClient .from('customers') .insert([{ id: userId, stripe_customer_id: 'cus_xxx' }]); // WARNING: Never expose this client to the browser. // Only use in 'use server' files or API route handlers. ``` -------------------------------- ### Supabase Client Factories Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Provides Supabase client instances for server-side and admin use cases. ```APIDOC ## `createSupabaseServerClient` — Cookie-aware server client ### Description Creates a Supabase client for use in Server Components and Route Handlers, backed by Next.js `cookies()` for session management. ### Usage ```typescript const supabase = await createSupabaseServerClient(); // Reads/writes session cookies automatically const { data: { user } } = await supabase.auth.getUser(); const { data } = await supabase.from('subscriptions').select('*').eq('user_id', user.id); ``` ``` ```APIDOC ## `supabaseAdminClient` — Service-role admin client ### Description A Supabase client initialized with the `SUPABASE_SERVICE_ROLE_KEY`, bypassing Row Level Security. Used exclusively in server-side webhook handlers and account management flows. ### Usage ```typescript import { supabaseAdminClient } from '@/libs/supabase/supabase-admin'; // Used in webhook handler to write customer/subscription data: await supabaseAdminClient .from('customers') .insert([{ id: userId, stripe_customer_id: 'cus_xxx' }]); // WARNING: Never expose this client to the browser. // Only use in 'use server' files or API route handlers. ``` ``` -------------------------------- ### Fetch User Profile from Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Fetches the authenticated user's profile from the 'users' table. This data is automatically created on first sign-up. ```typescript // src/features/account/controllers/get-user.ts export async function getUser() { const supabase = await createSupabaseServerClient(); const { data, error } = await supabase.from('users').select('*').single(); if (error) console.error(error); return data; } // Expected shape: // { // id: 'uuid', // full_name: 'Jane Doe', // avatar_url: 'https://...', // billing_address: { city: 'NYC', ... }, // payment_method: { card: { brand: 'visa', last4: '4242' } } // } ``` -------------------------------- ### Create Stripe Checkout Session Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Server action to create a Stripe Checkout session. Redirects unauthenticated users to `/signup`. Supports subscription or one-time payment modes. ```typescript // src/features/pricing/actions/create-checkout-action.ts 'use server'; // Called from PricingCard's "Get Started" button: await createCheckoutAction({ price: selectedPrice }); // Internally: // 1. Verifies session — redirects to /signup if unauthenticated // 2. getOrCreateCustomer({ userId, email }) // 3. stripe.checkout.sessions.create({ // payment_method_types: ['card'], // customer: 'cus_xxx', // line_items: [{ price: 'price_xxx', quantity: 1 }], // mode: 'subscription', // or 'payment' for one-time // allow_promotion_codes: true, // success_url: 'https://yourapp.com/account', // cancel_url: 'https://yourapp.com/', // }) // 4. Redirects to Stripe Checkout URL ``` -------------------------------- ### Send Email with Resend Client Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Send an email using the Resend client. Ensure you have configured the client and have the necessary email component and recipient details. ```typescript import WelcomeEmail from '@/features/emails/welcome'; import { resendClient } from '@/libs/resend/resend-client'; resendClient.emails.send({ from: 'no-reply@your-domain.com', to: userEmail, subject: 'Welcome!', react: , }); ``` -------------------------------- ### Transactional Email with Resend Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Utilizes React Email components and the Resend SDK to send transactional emails, such as welcome emails. ```APIDOC ## `WelcomeEmail` / `resendClient` — Transactional email via Resend ### Description React Email component for welcome emails, sent via the Resend SDK. Styled with Tailwind CSS using a shared email-specific Tailwind config. ### Usage ```typescript import WelcomeEmail from '@/features/emails/welcome'; import { resendClient } from '@/libs/resend/resend-client'; // Send a welcome email after successful signup: await resendClient.emails.send({ from: 'no-reply@yourapp.com', to: 'newuser@example.com', subject: 'Welcome!', react: , }); // Preview emails locally: // bun run email:dev → opens React Email dev server on port 3001 // Export to HTML: // bun run email:export ``` ``` -------------------------------- ### upsertUserSubscription Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Sync a Stripe subscription to Supabase, updating the `subscriptions` table and potentially user billing details. ```APIDOC ## upsertUserSubscription ### Description Retrieves the latest subscription state from Stripe and upserts it into the Supabase `subscriptions` table. For new subscriptions, it also copies billing details to the customer record. ### Method ``` async function upsertUserSubscription(options: { subscriptionId: string; customerId: string; isCreateAction: boolean; }) ``` ### Parameters - **subscriptionId** (string) - The ID of the Stripe subscription. - **customerId** (string) - The ID of the Stripe customer. - **isCreateAction** (boolean) - Set to `true` on checkout.session.completed to indicate a new subscription creation. ``` -------------------------------- ### Define New Database Table Schema Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Define the schema for a new table, including its columns, data types, and constraints. This SQL is used within a migration file. ```sql create table invites ( id uuid not null primary key default gen_random_uuid(), email text not null, ); alter table invites enable row level security; ``` -------------------------------- ### Sync Stripe catalog to Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt These functions are called by the webhook handler to keep the `products` and `prices` tables in Supabase synchronized with Stripe whenever products or prices are created or updated. ```APIDOC ## `upsertProduct` / `upsertPrice` ### Description Synchronizes Stripe product and price data with Supabase tables. ### Usage ```typescript // Upserts into supabase.from('products'): // { id, active, name, description, image, metadata } await upsertProduct(stripeProduct); // Upserts into supabase.from('prices'): // { id, product_id, active, currency, type, unit_amount, // interval, interval_count, trial_period_days, metadata } await upsertPrice(stripePrice); ``` ### Bootstrap Products ```bash stripe fixtures ./stripe-fixtures.json --api-key sk_test_xxx ``` ``` -------------------------------- ### Redirect to Stripe Customer Portal Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt API route that creates a Stripe Billing Portal session for the current user. The portal allows users to manage their subscriptions, payment methods, and view invoices. ```typescript // src/app/(account)/manage-subscription/route.ts // Navigating to /manage-subscription triggers: // 1. getSession() — throws if not authenticated // 2. getCustomerId({ userId }) — fetches stripe_customer_id // 3. stripe.billingPortal.sessions.create({ // customer: 'cus_xxx', // return_url: 'https://yourapp.com/account', // }) // 4. redirect(portalUrl) — sends user to Stripe-hosted portal // In the Account page: ``` -------------------------------- ### Sync Stripe Catalog to Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt These functions are called by the webhook handler to keep the 'products' and 'prices' tables in Supabase synchronized with Stripe. They are used when products or prices are created or updated. ```typescript // src/features/pricing/controllers/upsert-product.ts await upsertProduct(stripeProduct); // Upserts into supabase.from('products'): // { id, active, name, description, image, metadata } ``` ```typescript // src/features/pricing/controllers/upsert-price.ts await upsertPrice(stripePrice); // Upserts into supabase.from('prices'): // { id, product_id, active, currency, type, unit_amount, // interval, interval_count, trial_period_days, metadata } ``` ```bash # Bootstrap products via Stripe fixture (run once): # stripe fixtures ./stripe-fixtures.json --api-key sk_test_xxx ``` -------------------------------- ### getUser Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Fetch the current user's profile row from the `users` table. ```APIDOC ## getUser ### Description Fetch the current user's profile row from the `users` table in Supabase. This record is created automatically on first sign-up. ### Method ``` async function getUser() ``` ### Returns - `data` (object | null) - The user's profile data, including fields like `id`, `full_name`, `avatar_url`, `billing_address`, and `payment_method`. Returns null if an error occurs or no user data is found. ``` -------------------------------- ### POST /api/webhooks Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Processes incoming Stripe webhook events to keep Supabase synchronized with Stripe. Verifies signatures and dispatches events to handlers. ```APIDOC ## `POST /api/webhooks` — Stripe webhook event processor Verifies incoming Stripe webhook signatures and dispatches events to the appropriate handlers to keep Supabase in sync with Stripe. ### Handled Events - `product.created` → `upsertProduct()` - `product.updated` → `upsertProduct()` - `price.created` → `upsertPrice()` - `price.updated` → `upsertPrice()` - `checkout.session.completed` → `upsertUserSubscription({ isCreateAction: true })` - `customer.subscription.created` → `upsertUserSubscription()` - `customer.subscription.updated` → `upsertUserSubscription()` - `customer.subscription.deleted` → `upsertUserSubscription()` ### Local Development Use the Stripe CLI for local development: ```bash stripe listen --forward-to=localhost:3000/api/webhooks ``` ### Example Event Processing (`checkout.session.completed`) ```typescript // event.data.object = { mode: 'subscription', subscription: 'sub_xxx', customer: 'cus_xxx' } // → upsertUserSubscription({ subscriptionId: 'sub_xxx', customerId: 'cus_xxx', isCreateAction: true }) ``` ``` -------------------------------- ### UI Component: SexyBoarder Animated Gradient Border Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt This React component provides a decorative wrapper with a rotating rainbow gradient border. It's useful for highlighting specific elements, like pricing cards. The animation is achieved using Tailwind CSS custom animations. You can adjust the border thickness using the 'offset' prop. ```tsx //src/components/sexy-boarder.tsx import { SexyBoarder } from '@/components/sexy-boarder'; // Default usage (offset=10):
Pro plan card content
// Custom offset (larger = thicker visible border): // The animated gradient uses Tailwind custom animation: // bg-gradient-to-r from-[#5ED4FF] via-[#D13C5F] to-[#F98324] // animate-spin-slow (defined in tailwind.config.ts) ``` -------------------------------- ### TypeScript Product Metadata Parsing Source: https://github.com/kolbysisk/next-supabase-stripe-starter/blob/main/README.md Safely parse product metadata using Zod schema after fetching products. This ensures type safety when accessing metadata fields like 'teamInvites'. ```typescript const products = await getProducts(); const productMetadata = productMetadataSchema.parse(products[0].metadata); // Now it's typesafe 🙌! productMetadata.teamInvites; // The value you set in the fixture ``` -------------------------------- ### Session Management with `updateSession` Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Refreshes session cookies on every request using Next.js middleware to prevent premature session expiry and enable route guarding. ```APIDOC ## `updateSession` (middleware) — Refresh session cookies on every request ### Description The Next.js middleware function that refreshes the Supabase auth token on every request to prevent premature session expiry. Also serves as the integration point for adding route guards. ### Usage ```typescript // src/middleware.ts + src/libs/supabase/supabase-middleware-client.ts export async function middleware(request: NextRequest) { return await updateSession(request); } export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'], }; // Adding a route guard inside updateSession: const guardedRoutes = ['/dashboard']; if (!user && guardedRoutes.includes(request.nextUrl.pathname)) { const url = request.nextUrl.clone(); url.pathname = '/login'; return NextResponse.redirect(url); } ``` ``` -------------------------------- ### Sync Stripe Subscription to Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Updates the Supabase 'subscriptions' table with the latest subscription state from Stripe. For new subscriptions, it also copies billing details to the customer record. ```typescript // src/features/account/controllers/upsert-user-subscription.ts await upsertUserSubscription({ subscriptionId: 'sub_1OgXXX', customerId: 'cus_1OgXXX', isCreateAction: true, // set true on checkout.session.completed }); // What it does internally: // 1. Looks up userId from customers table by stripe_customer_id // 2. Retrieves full subscription from Stripe (with payment method expanded) // 3. Upserts into Supabase subscriptions table: // { id, user_id, status, price_id, cancel_at_period_end, // current_period_start, current_period_end, trial_start, trial_end, ... } // 4. If isCreateAction=true, copies name/phone/address to Stripe customer // and updates users.billing_address + users.payment_method in Supabase ``` -------------------------------- ### productMetadataSchema Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt A Zod schema for parsing and transforming Stripe product metadata into camelCase TypeScript types. Used for UI features driven by Stripe data. ```APIDOC ## `productMetadataSchema` — Typed Stripe product metadata Zod schema that parses and transforms the `metadata` field on Stripe products into camelCase TypeScript types. Used to drive UI features (card styling, feature lists) from Stripe-managed data. ### Usage ```typescript import { productMetadataSchema } from '@/features/pricing/models/product-metadata'; const products = await getProducts(); const metadata = productMetadataSchema.parse(products[0].metadata); // Accessing transformed metadata: // metadata.priceCardVariant → 'basic' | 'pro' | 'enterprise' // metadata.generatedImages → 10 | 100 | 'enterprise' // metadata.imageEditor → 'basic' | 'pro' // metadata.supportLevel → 'email' | 'live' ``` ### Stripe Fixture Metadata Fields ```json { "price_card_variant": "pro", "generated_images": 100, "image_editor": "pro", "support_level": "live" } ``` ``` -------------------------------- ### getCustomerId Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Fetch a Stripe customer ID by user ID from the `customers` table. ```APIDOC ## getCustomerId ### Description Retrieves the `stripe_customer_id` from the `customers` table. Throws an error if the customer ID is not found. ### Method ``` async function getCustomerId(options: { userId: string; }): Promise ``` ### Parameters - **userId** (string) - The Supabase user ID. ### Returns - `stripeCustomerId` (string) - The Stripe customer ID. ### Throws - Error - If the `stripe_customer_id` is not found for the given `userId`. ``` -------------------------------- ### Fetch Stripe Customer ID by User ID Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Retrieves the 'stripe_customer_id' from the 'customers' table using the Supabase user ID. Throws an error if the customer ID is not found, typically used when the customer is expected to exist. ```typescript // src/features/account/controllers/get-customer-id.ts const stripeCustomerId = await getCustomerId({ userId: session.user.id }); // Returns: 'cus_xxx' // Throws: Error('Error fetching stripe_customer_id') if not found ``` -------------------------------- ### Refresh Session Cookies with Middleware Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt The Next.js middleware function that refreshes the Supabase auth token on every request to prevent premature session expiry. It also serves as the integration point for adding route guards. ```typescript // src/middleware.ts + src/libs/supabase/supabase-middleware-client.ts export async function middleware(request: NextRequest) { return await updateSession(request); } export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'], }; // Adding a route guard inside updateSession: const guardedRoutes = ['/dashboard']; if (!user && guardedRoutes.includes(request.nextUrl.pathname)) { const url = request.nextUrl.clone(); url.pathname = '/login'; return NextResponse.redirect(url); } ``` -------------------------------- ### signOut Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Signs out the currently authenticated user from Supabase by clearing their session cookie. ```APIDOC ## signOut ### Description Signs out the authenticated user from Supabase, clearing their session cookie. ### Method Server Action (TypeScript) ### Usage Example ```typescript async function handleLogoutClick() { const response = await signOut(); // Handle response... } ``` ``` -------------------------------- ### Stripe Webhook Handler Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt API route to process incoming Stripe webhook events. Verifies signatures and dispatches events to update Supabase data, keeping it in sync with Stripe. ```typescript // src/app/api/webhooks/route.ts // Handled events: const relevantEvents = new Set([ 'product.created', // → upsertProduct() 'product.updated', // → upsertProduct() 'price.created', // → upsertPrice() 'price.updated', // → upsertPrice() 'checkout.session.completed', // → upsertUserSubscription({ isCreateAction: true }) 'customer.subscription.created', // → upsertUserSubscription() 'customer.subscription.updated', // → upsertUserSubscription() 'customer.subscription.deleted', // → upsertUserSubscription() ]); // Stripe CLI for local development: // stripe listen --forward-to=localhost:3000/api/webhooks // Example: what happens on checkout.session.completed // event.data.object = { mode: 'subscription', subscription: 'sub_xxx', customer: 'cus_xxx' } // → upsertUserSubscription({ subscriptionId: 'sub_xxx', customerId: 'cus_xxx', isCreateAction: true }) ``` -------------------------------- ### Typed Stripe Product Metadata Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Zod schema for parsing and transforming Stripe product metadata into camelCase TypeScript types. Used for UI features driven by Stripe data. ```typescript // src/features/pricing/models/product-metadata.ts import { productMetadataSchema } from '@/features/pricing/models/product-metadata'; const products = await getProducts(); const metadata = productMetadataSchema.parse(products[0].metadata); // metadata.priceCardVariant → 'basic' | 'pro' | 'enterprise' // metadata.generatedImages → 10 | 100 | 'enterprise' // metadata.imageEditor → 'basic' | 'pro' // metadata.supportLevel → 'email' | 'live' // Stripe fixture metadata fields: // { // "price_card_variant": "pro", // "generated_images": 100, // "image_editor": "pro", // "support_level": "live" // } ``` -------------------------------- ### Utility: Safe Environment Variable Accessor getEnvVar Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt This TypeScript utility provides a safe way to access environment variables. It throws a descriptive ReferenceError if a required variable is undefined, preventing silent failures. Ensure all required variables are defined in your .env.local file. ```typescript //src/utils/get-env-var.ts import { getEnvVar } from '@/utils/get-env-var'; // Safe access — throws ReferenceError if undefined: const stripeKey = getEnvVar(process.env.STRIPE_SECRET_KEY, 'STRIPE_SECRET_KEY'); // → throws: "Reference to undefined env var: STRIPE_SECRET_KEY" // Required .env.local variables: // NEXT_PUBLIC_SUPABASE_URL= // NEXT_PUBLIC_SUPABASE_ANON_KEY= // SUPABASE_SERVICE_ROLE_KEY= // SUPABASE_DB_PASSWORD= // NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= // STRIPE_SECRET_KEY= // STRIPE_WEBHOOK_SECRET= // RESEND_API_KEY= // NEXT_PUBLIC_SITE_URL= ``` -------------------------------- ### Sign out current user with Supabase Source: https://context7.com/kolbysisk/next-supabase-stripe-starter/llms.txt Signs out the currently authenticated user from Supabase by clearing their session cookie. This action should be used when a user explicitly logs out. ```typescript // src/app/(auth)/auth-actions.ts export async function signOut() { const supabase = await createSupabaseServerClient(); const { error } = await supabase.auth.signOut(); if (error) { console.error(error); return { data: null, error }; } return { data: null, error: null }; } // Usage in AccountMenu component: async function handleLogoutClick() { const response = await signOut(); if (response?.error) { toast({ variant: 'destructive', description: 'Logout failed. Please try again.' }); } else { router.refresh(); toast({ description: 'You have been logged out.' }); } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.