subscription-schemas
Subscription Schemas
Production-ready Supabase database schemas for subscription and payment management with comprehensive security policies.
Security Requirements
This skill follows strict security rules:
- NO hardcoded database credentials - All connection strings use placeholders
- Environment variable references - Code reads from
SUPABASE_URLandSUPABASE_KEY - Row Level Security (RLS) - All tables protected with comprehensive policies
- Data encryption - Sensitive payment data properly protected
- Audit logging - Webhook events tracked for compliance
All examples use placeholder values like your_supabase_url_here.
Database Schema Overview
The subscription schema consists of five core tables:
- customers - Customer profiles linked to auth.users
- subscriptions - Active and historical subscriptions
- payments - Payment transaction records
- invoices - Invoice history and status
- webhook_events - Payment provider webhook logs
Table Relationships
auth.users (Supabase Auth)
↓
customers (1:1 with users)
↓
subscriptions (1:many)
↓
payments (1:many per subscription)
invoices (1:many per subscription)
webhook_events (independent audit log)
Use When
- Setting up a new subscription-based application
- Implementing payment tracking for SaaS products
- Migrating payment infrastructure to Supabase
- Adding Row Level Security to payment tables
- Configuring multi-tenant payment isolation
- Creating invoice and payment history tracking
- Implementing webhook event logging for Stripe/Paddle/LemonSqueezy
Instructions
Phase 1: Create Database Tables
-
Review table schemas:
- Read
templates/customers_table.sqlfor customer profiles - Read
templates/subscriptions_table.sqlfor subscription management - Read
templates/payments_table.sqlfor payment records - Read
templates/invoices_table.sqlfor invoice tracking - Read
templates/webhook_events_table.sqlfor webhook logging
- Read
-
Execute table creation:
bash scripts/create-payment-tables.shThis script will:
- Create all five tables with proper indexes
- Set up foreign key relationships
- Add check constraints for data validation
- Create updated_at triggers
Phase 2: Implement Row Level Security
-
Review RLS policies:
- Read
templates/rls_policies.sqlfor complete policy definitions - Understand customer data isolation
- Review subscription access controls
- Check payment data protection rules
- Read
-
Enable RLS and create policies:
bash scripts/setup-rls-policies.shThis script will:
- Enable RLS on all payment tables
- Create SELECT policies for authenticated users
- Create INSERT policies with validation
- Create UPDATE policies with ownership checks
- Create DELETE policies (restricted)
Phase 3: Run Complete Migration
-
Use the complete schema migration:
bash scripts/migrate-schema.shThis orchestrates:
- Table creation in correct order
- Index creation for performance
- RLS policy setup
- Validation of schema structure
-
Verify migration success:
bash scripts/validate-schema.shValidates:
- All tables exist
- Indexes are created
- RLS is enabled
- Policies are active
- Foreign keys are valid
Phase 4: Test RLS Policies
-
Review RLS testing examples:
- Read
examples/rls-testing-examples.sql - Test customer data isolation
- Verify subscription access controls
- Confirm payment data protection
- Read
-
Run sample queries:
- Read
examples/sample-queries.sql - Test common subscription queries
- Verify payment history retrieval
- Check invoice generation queries
- Read
Security Compliance
Row Level Security Policies
All tables implement RLS with these principles:
- Customer Isolation: Users only access their own customer record
- Subscription Ownership: Users only see their own subscriptions
- Payment Privacy: Payment records restricted to owners
- Invoice Access: Invoices accessible only to associated customers
- Webhook Audit: Webhook events visible to admins only
Data Protection
- Sensitive fields: Payment methods, billing details encrypted
- PII protection: Customer data isolated per user
- Audit trail: All webhook events logged
- No direct access: All queries filtered through RLS
Environment Variables
Connection configuration reads from:
SUPABASE_URL=your_supabase_url_here
SUPABASE_ANON_KEY=your_supabase_anon_key_here
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here # For migrations only
Available Scripts
create-payment-tables.sh
Creates all five payment tables with proper structure, indexes, and constraints.
Usage:
bash scripts/create-payment-tables.sh
Environment Required:
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
setup-rls-policies.sh
Enables RLS and creates comprehensive security policies for all tables.
Usage:
bash scripts/setup-rls-policies.sh
Environment Required:
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
migrate-schema.sh
Orchestrates complete schema setup including tables, indexes, and RLS.
Usage:
bash scripts/migrate-schema.sh
Environment Required:
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
validate-schema.sh
Validates that all tables, indexes, and policies are correctly configured.
Usage:
bash scripts/validate-schema.sh
Environment Required:
SUPABASE_URLSUPABASE_ANON_KEY
Available Templates
customers_table.sql
Customer profile table with Supabase Auth integration.
Fields:
id(uuid, primary key)user_id(uuid, foreign key to auth.users)email(text)name(text)billing_address(jsonb)created_at,updated_at(timestamptz)
subscriptions_table.sql
Subscription tracking with status and billing cycles.
Fields:
id(uuid, primary key)customer_id(uuid, foreign key)plan_id(text)status(enum: active, canceled, past_due, trialing)current_period_start,current_period_end(timestamptz)cancel_at_period_end(boolean)created_at,updated_at(timestamptz)
payments_table.sql
Payment transaction records.
Fields:
id(uuid, primary key)subscription_id(uuid, foreign key)amount(numeric)currency(text)status(enum: succeeded, pending, failed)payment_method(text)provider_payment_id(text)created_at(timestamptz)
invoices_table.sql
Invoice generation and tracking.
Fields:
id(uuid, primary key)subscription_id(uuid, foreign key)invoice_number(text, unique)amount_due(numeric)amount_paid(numeric)status(enum: draft, open, paid, void)due_date(date)created_at,updated_at(timestamptz)
webhook_events_table.sql
Webhook event logging for audit and replay.
Fields:
id(uuid, primary key)provider(text, e.g., 'stripe', 'paddle')event_type(text)payload(jsonb)processed(boolean)created_at(timestamptz)
rls_policies.sql
Complete RLS policy definitions for all tables with customer isolation.
Available Examples
complete-schema-migration.sql
Complete migration script showing full schema setup in one file.
sample-queries.sql
Common query patterns:
- Get active subscriptions for user
- Retrieve payment history
- Generate invoice summaries
- Check subscription status
rls-testing-examples.sql
Test cases for RLS policies:
- Verify customer isolation
- Test subscription access
- Validate payment privacy
- Confirm admin-only webhook access
Requirements
- Supabase project with database access
- Service role key for migrations (secure storage required)
- Anon key for client-side queries
- PostgreSQL extensions:
uuid-ossp,pgcrypto
Migration Strategy
Initial Setup
- Run
create-payment-tables.shto create schema - Run
setup-rls-policies.shto enable security - Run
validate-schema.shto confirm setup
Schema Updates
- Create new migration file in
templates/ - Test in development environment first
- Apply using
migrate-schema.sh - Always validate after migrations
Rollback Support
Each template includes a rollback section:
-- Rollback
DROP TABLE IF EXISTS table_name CASCADE;
Performance Considerations
Indexes Created
customers.user_id- Fast auth lookupssubscriptions.customer_id- Customer subscription queriessubscriptions.status- Status filteringpayments.subscription_id- Payment historyinvoices.subscription_id- Invoice retrievalwebhook_events.provider, event_type- Event filtering
Query Optimization
- Use explicit WHERE clauses with RLS
- Include
auth.uid()checks in queries - Cache frequently accessed data
- Use
EXPLAIN ANALYZEfor slow queries
Integration with Payment Providers
Stripe
- Use
webhook_eventsto log Stripe webhooks - Map
provider_payment_idto Stripe payment intent IDs - Store Stripe customer ID in
customers.metadata
Paddle
- Log Paddle webhooks with
provider='paddle' - Map subscription IDs to Paddle subscription IDs
- Store Paddle customer ID in customer metadata
LemonSqueezy
- Track LemonSqueezy events in webhook_events
- Map variant IDs to plan_id in subscriptions
- Store LemonSqueezy customer ID in metadata
Compliance Notes
- PCI DSS: No credit card numbers stored (use payment provider tokens)
- GDPR: Customer data can be deleted via user_id cascade
- Audit Trail: All webhook events logged for compliance
- Data Retention: Configure automated archival policies as needed