Database Schema
Learn about the database schema used to store Stripe-related data in your application.
Schema Overview
The database schema includes tables for customers, products, and prices. You can find the complete schema in `supabase/seed.sql`:
-- Customers tableCREATE TABLE IF NOT EXISTS public.customers (id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),user_id UUID REFERENCES public.users(id) ON DELETE CASCADE NOT NULL,stripe_customer_id TEXT UNIQUE,subscription_status TEXT,subscription_tier TEXT DEFAULT 'free',subscription_updated_at TIMESTAMP WITH TIME ZONE,trial_end TIMESTAMP WITH TIME ZONE,is_lifetime BOOLEAN DEFAULT FALSE,current_period_end TIMESTAMP WITH TIME ZONE);-- Products tableCREATE TABLE IF NOT EXISTS public.products (id TEXT PRIMARY KEY,active BOOLEAN DEFAULT TRUE NOT NULL,name TEXT NOT NULL,description TEXT,image TEXT,metadata JSONB);-- Prices tableCREATE TABLE IF NOT EXISTS public.prices (id TEXT PRIMARY KEY,product_id TEXT REFERENCES public.products(id) NOT NULL,active BOOLEAN DEFAULT TRUE NOT NULL,description TEXT,unit_amount BIGINT NOT NULL,currency TEXT NOT NULL,type TEXT NOT NULL,interval TEXT,interval_count INTEGER,trial_period_days INTEGER,metadata JSONB);
Row Level Security (RLS)
The boilerplate comes with pre-configured RLS policies to secure your data:
-- Enable RLSALTER TABLE public.customers ENABLE ROW LEVEL SECURITY;ALTER TABLE public.products ENABLE ROW LEVEL SECURITY;ALTER TABLE public.prices ENABLE ROW LEVEL SECURITY;-- Only authenticated users can read customer dataCREATE POLICY "Authenticated users can read customer data" ON public.customersFOR SELECT USING (auth.role() = 'authenticated');-- Users can read their own customer recordCREATE POLICY "Users can read own customer data" ON public.customersFOR SELECT USING (auth.uid() = user_id);-- Allow authenticated users to insert their own customer recordsCREATE POLICY "Users can insert own customer data" ON public.customersFOR INSERT WITH CHECK (auth.uid() = user_id);-- Users can update their own customer recordsCREATE POLICY "Users can update own customer data" ON public.customersFOR UPDATE USING (auth.uid() = user_id);-- Anyone can read active productsCREATE POLICY "Anyone can read active products" ON public.productsFOR SELECT USING (active = true);-- Anyone can read active pricesCREATE POLICY "Anyone can read active prices" ON public.pricesFOR SELECT USING (active = true);
Indexes
The schema includes optimized indexes for better query performance:
-- Create indexesCREATE INDEX IF NOT EXISTS customers_user_id_idx ON public.customers (user_id);CREATE INDEX IF NOT EXISTS prices_product_id_idx ON public.prices (product_id);CREATE INDEX IF NOT EXISTS customers_stripe_customer_idx ON public.customers (stripe_customer_id);
Default Products and Prices
The boilerplate comes with pre-configured products and pricing tiers:
- Free Plan - Basic features for individuals
- Basic Plan - Essential features for small teams (monthly/yearly)
- Pro Plan - Advanced features for growing businesses (monthly/yearly)
- Enterprise Plan - Custom solutions for large organizations
- Lifetime Basic - One-time payment for Basic features
- Lifetime Pro - One-time payment for Pro features
You can find the complete product and price seed data in `supabase/seed.sql`.
Best Practices
- The schema is designed to handle both subscription and one-time (lifetime) purchases
- Subscription status and tier are stored in the customers table for quick access
- Products and prices use Stripe IDs as primary keys for easy synchronization
- RLS policies ensure data security at the database level
- Metadata is stored as JSONB for flexible feature sets and configuration
- Indexes are optimized for common queries in the application