# Stripe Integration Implementation Summary ## Overview Complete Stripe integration for Xtablo with a single "Standard" subscription plan. **Architecture:** - ✅ **Webhook-based**: Stripe webhooks sync data to Supabase - ✅ **Direct Supabase access**: Frontend queries Supabase directly (no API for reads) - ✅ **API for actions**: Checkout and subscription management via Node.js API - ✅ **RLS-protected**: Row Level Security ensures data privacy ## ✅ What Has Been Implemented ### 1. Database Schema (`sql/35_stripe_wrappers.sql`) #### Tables (Synced via Webhooks) - `public.stripe_customers` - Customer records with user mapping - `public.stripe_subscriptions` - Subscription history - `public.stripe_products` - Product catalog - `public.stripe_prices` - Pricing information #### Profile Enhancements - `profiles.is_paying` - Boolean flag for quick checks - `profiles.subscription_tier` - Current tier ('free' or 'standard') #### Helper Functions ```sql -- Check if user is paying SELECT is_paying_user(auth.uid()); -- Get subscription details SELECT * FROM get_user_subscription_status(auth.uid()); -- Get Stripe customer ID SELECT get_user_stripe_customer_id(auth.uid()); ``` #### Views - `active_subscriptions` - All active subs with user info #### Automatic Updates - Triggers automatically update `profiles.is_paying` when subscriptions change - `updated_at` timestamps managed automatically ### 2. Webhook Handlers (`sql/36_stripe_webhooks.sql`) Includes functions to process all major Stripe events: **Customer Events:** - `handle_stripe_customer_created()` - `handle_stripe_customer_updated()` - `handle_stripe_customer_deleted()` **Product Events:** - `handle_stripe_product_upsert()` - `handle_stripe_product_deleted()` **Price Events:** - `handle_stripe_price_upsert()` - `handle_stripe_price_deleted()` **Subscription Events:** - `handle_stripe_subscription_upsert()` - `handle_stripe_subscription_deleted()` ### 3. Backend API (`api/src/stripe.ts` & `api/src/stripe-webhook.ts`) **Endpoints:** - `POST /api/v1/stripe/webhook` - Stripe webhook handler (signature verified) - `POST /api/v1/stripe/create-checkout-session` - Start subscription checkout - `POST /api/v1/stripe/create-portal-session` - Open customer portal - `POST /api/v1/stripe/cancel-subscription` - Cancel at period end - `POST /api/v1/stripe/reactivate-subscription` - Reactivate subscription **Note:** Subscription status queries (`is-paying`, `subscription`, `prices`) are handled directly by the frontend using Supabase client with RLS policies. **Features:** - ✅ Automatic customer creation with user mapping - ✅ Secure webhook signature verification - ✅ Complete subscription lifecycle management - ✅ Customer portal access - ✅ Subscription cancellation/reactivation - ✅ Optimistic updates (API updates DB before webhook for instant UI feedback) ### 4. Frontend Hooks (`apps/main/src/hooks/stripe.ts`) **Available Hooks:** **Direct Supabase Queries (Fast, RLS-protected):** - `useSubscription()` - Get subscription details from Supabase - `useIsPayingUser()` - Check if user is paying (from user profile) - `useStripePrices()` - Get available prices from Supabase **API Calls (For Actions):** - `useCreateCheckoutSession()` - Start checkout (calls API) - `useCreatePortalSession()` - Open portal (calls API) - `useCancelSubscription()` - Cancel subscription (calls API) - `useReactivateSubscription()` - Reactivate subscription (calls API) ### 5. TypeScript Types (`packages/shared/src/types/stripe.types.ts`) Complete type definitions: - `StripeCustomer` - `StripeSubscription` - `StripeProduct` - `StripePrice` - Helper functions for formatting and status checks ### 6. Documentation (`docs/STRIPE_SETUP.md`) Comprehensive setup guide including: - Step-by-step configuration - Edge Function example - Frontend integration examples - Testing instructions - Troubleshooting tips ## 🎯 Key Features ### Security - ✅ Row Level Security (RLS) on all tables - ✅ Users can only see their own data - ✅ Stripe API key stored securely in Vault - ✅ Webhook signature verification - ✅ Service role functions for webhooks ### Performance - ✅ Indexed on user_id, customer_id, status - ✅ Efficient queries for subscription checks - ✅ Cached subscription status in profiles table ### Data Integrity - ✅ Foreign key relationships - ✅ Cascading deletes - ✅ Automatic timestamp management - ✅ Transaction safety in webhook handlers ## 📊 How to Check if User is Paying ### Method 1: From Profile (Fastest) ```typescript const user = useUser(); if (user.is_paying) { // User has active Standard subscription console.log(`User is on ${user.subscription_tier} plan`); // 'standard' } ``` ### Method 2: Using Hook (Same as Profile) ```typescript const { data: isPaying } = useIsPayingUser(); // Returns user.is_paying directly ``` ### Method 3: Direct Supabase Query ```typescript const { data: subscription } = await supabase .from("stripe_subscriptions") .select("*") .eq("user_id", userId) .in("status", ["active", "trialing"]) .single(); const isPaying = !!subscription; ``` ## 🔄 Data Flow ### Subscription Creation Flow 1. **User Clicks "Subscribe to Standard"** → Frontend calls `useCreateCheckoutSession({ priceId })` → Calls API: `POST /api/v1/stripe/create-checkout-session` 2. **API Creates Checkout** → Creates/retrieves Stripe customer (with `user_id` in metadata) → Creates Stripe checkout session → Returns checkout URL → Frontend redirects to Stripe 3. **User Completes Payment** → Stripe processes payment → Fires `customer.subscription.created` webhook 4. **Webhook Received** → API endpoint `POST /api/v1/stripe/webhook` receives event → Verifies signature → Calls `handle_stripe_subscription_upsert()` database function 5. **Database Updated** → Function inserts into `stripe_subscriptions` → Trigger fires: `update_profile_on_subscription_change` → Updates `profiles.is_paying = true` and `subscription_tier = 'standard'` 6. **Frontend Queries Supabase** → `useSubscription()` queries `stripe_subscriptions` table directly → `useUser()` shows updated `is_paying` and `subscription_tier` → UI automatically reflects new status ### Read Flow (No API Needed) ``` Frontend → Supabase Client → RLS Policies → stripe_subscriptions table → User's data ``` Benefits: - ⚡ **Fast**: Direct database access, no API hop - 🔒 **Secure**: RLS ensures users only see their data - 📊 **Real-time**: Can use Supabase realtime subscriptions if needed ## 🚀 Next Steps to Complete Implementation ### Required Setup: 1. **Update Vault Key ID** - Get key_id after storing Stripe API key - Update in `sql/35_stripe_wrappers.sql` line 27 2. **Install Stripe SDK** ```bash cd apps/api npm install stripe @stripe/stripe-js ``` 3. **Configure API Environment Variables** Add to `api/.env`: ```env STRIPE_SECRET_KEY=sk_test_xxxxx STRIPE_WEBHOOK_SECRET=whsec_xxxxx SUPABASE_SERVICE_ROLE_KEY=your_service_role_key ``` 4. **Configure Frontend Environment** Add to `apps/main/.env`: ```env VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx ``` 5. **Create "Standard" Plan in Stripe** - Create product named exactly **"Standard"** - Add pricing (monthly/yearly) - Note price IDs for frontend 6. **Configure Webhook in Stripe Dashboard** - Add endpoint: `https://your-api.com/api/v1/stripe/webhook` - Select all customer/subscription/product/price events - Copy webhook signing secret ### Frontend Implementation: 7. **Hooks Already Created** ✅ - `useSubscription()` - Get subscription details - `useIsPayingUser()` - Check payment status - `useCreateCheckoutSession()` - Initiate checkout - `useCreatePortalSession()` - Customer portal - `useCancelSubscription()` - Cancel subscription - `useReactivateSubscription()` - Reactivate 8. **Build UI Components** - Pricing page - Subscription management page - Payment status badges - Upgrade prompts 9. **Add Feature Gates** ```typescript if (!user.is_paying) { return ; } ``` ## 📝 Environment Variables Needed ### API (`api/.env`) ```env # Stripe STRIPE_SECRET_KEY=sk_test_xxxxx (or sk_live_xxxxx) STRIPE_WEBHOOK_SECRET=whsec_xxxxx # Supabase SUPABASE_URL=https://xxx.supabase.co SUPABASE_SERVICE_ROLE_KEY=eyJxxx... # Frontend URL (for redirects) FRONTEND_URL=http://localhost:5173 (or https://app.xtablo.com) ``` ### Frontend (`apps/main/.env`) ```env # Stripe VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx (or pk_live_xxxxx) ``` ## 🧪 Testing Checklist - [ ] Run database migrations (35 & 36) - [ ] Install Stripe SDK in API: `npm install stripe @stripe/stripe-js` - [ ] Install Stripe SDK in frontend: `npm install @stripe/stripe-js` - [ ] Configure environment variables (API + Frontend) - [ ] Create "Standard" product in Stripe Dashboard - [ ] Configure webhook endpoint in Stripe - [ ] Test webhook with Stripe CLI - [ ] Can create checkout session from frontend - [ ] Payment redirects back to app - [ ] Webhook syncs subscription to database - [ ] `profiles.is_paying` updates automatically - [ ] Users can only see their own subscription data - [ ] `is_paying_user()` function returns correct status - [ ] Can access customer portal - [ ] Subscription cancellation updates status - [ ] Test cards work in test mode ## 📚 Reference - **Database Schema**: `sql/35_stripe_wrappers.sql` - **Webhook Handlers**: `sql/36_stripe_webhooks.sql` - **API Routes**: `api/src/stripe.ts` - **Webhook Handler**: `api/src/stripe-webhook.ts` - **Frontend Hooks**: `apps/main/src/hooks/stripe.ts` - **TypeScript Types**: `packages/shared/src/types/stripe.types.ts` - **Setup Guide**: `docs/STRIPE_SETUP.md` - **Stripe API Docs**: https://stripe.com/docs/api - **Supabase Wrappers**: https://supabase.github.io/wrappers/stripe/ ## 🎉 Benefits 1. **Direct Stripe Access** - Query Stripe data without API calls 2. **Instant Status Checks** - `is_paying` flag on user profile 3. **Automatic Sync** - Webhooks keep data current 4. **Secure** - RLS ensures data privacy 5. **Type-Safe** - Full TypeScript support 6. **Scalable** - Handles thousands of subscriptions 7. **Historical Data** - Keep subscription history --- ## 🎯 Single "Standard" Plan Architecture This implementation is optimized for a single subscription tier: **Plan Structure:** - **Free** - Default tier, `is_paying = false` - **Standard** - Paid tier, `is_paying = true`, `subscription_tier = 'standard'` **Why This Works:** - Simple pricing model - Easy to check: just `user.is_paying` - Future-proof: can add more tiers later by updating the tier logic - Webhook automatically handles upgrades/downgrades **API Endpoints Specific to Standard:** - `GET /api/v1/stripe/prices` - Returns only "Standard" plan prices - Creates customers with metadata linking to user_id - All subscriptions automatically set tier to 'standard' --- **Implementation Status**: ✅ Complete (Database, API, Frontend, Types) **Next Step**: Configure Stripe and test webhook integration