# Stripe Integration Setup Guide This guide walks you through setting up Stripe payments integration for Xtablo with a single "Standard" plan. ## Table of Contents 1. [Prerequisites](#prerequisites) 2. [Database Setup](#database-setup) 3. [Stripe Configuration](#stripe-configuration) 4. [Backend API Setup](#backend-api-setup) 5. [Frontend Integration](#frontend-integration) 6. [Testing](#testing) 7. [Usage Examples](#usage-examples) ## Prerequisites - Supabase project with database access - Stripe account (test mode for development) - Supabase Wrappers extension enabled - Node.js API already running ## Database Setup ### 1. Run Migration Scripts Execute the SQL migration files in your Supabase SQL Editor: ```sql -- Execute these in order \i sql/35_stripe_wrappers.sql -- Creates tables, functions, RLS policies \i sql/36_stripe_webhooks.sql -- Creates webhook handler functions ``` These migrations create: - ✅ Subscription tracking tables (`stripe_customers`, `stripe_subscriptions`, `stripe_products`, `stripe_prices`) - ✅ Helper functions (`is_paying_user()`, `get_user_subscription_status()`) - ✅ Automatic triggers to update `profiles.is_paying` - ✅ Row Level Security policies - ✅ Webhook handler functions **No Wrappers needed!** This is a pure webhook-based integration. ## Stripe Configuration ### 1. Create Products and Prices In your Stripe Dashboard: 1. Go to **Products** 2. Click **Add product** 3. Create your subscription tiers (e.g., "Premium", "Pro") 4. Add pricing (monthly, yearly, etc.) 5. Note the `price_id` (starts with `price_xxx`) ### 2. Create the "Standard" Plan In your Stripe Dashboard: 1. Go to **Products** 2. Click **Add product** 3. Name: **"Standard"** (important: must match exactly) 4. Add your pricing: - Monthly: e.g., €9.99/month - Yearly: e.g., €99/year (optional) 5. **Save the price IDs** (e.g., `price_xxxxx`) ### 3. Set up Webhooks 1. Go to **Developers → Webhooks** in Stripe Dashboard 2. Click **Add endpoint** 3. URL: `https://YOUR_API_URL/api/v1/stripe/webhook` 4. Select events to listen to: - `customer.created` - `customer.updated` - `customer.deleted` - `customer.subscription.created` - `customer.subscription.updated` - `customer.subscription.deleted` - `product.created` - `product.updated` - `product.deleted` - `price.created` - `price.updated` - `price.deleted` 5. **Copy the webhook signing secret** (starts with `whsec_`) ## Backend API Setup The backend API handlers are already implemented in: - `api/src/stripe.ts` - Main Stripe routes - `api/src/stripe-webhook.ts` - Webhook handler - `api/src/routers.ts` - Router configuration ### 1. Install Stripe SDK ```bash cd api npm install stripe @stripe/stripe-js ``` ### 2. Configure Environment Variables Add to your `.env` file: ```env # Stripe STRIPE_SECRET_KEY=sk_test_xxxxx STRIPE_WEBHOOK_SECRET=whsec_xxxxx # For webhooks to work SUPABASE_SERVICE_ROLE_KEY=your_service_role_key ``` ### 3. Restart API Server The Stripe routes are automatically registered at: - `POST /api/v1/stripe/webhook` - Webhook endpoint - `POST /api/v1/stripe/create-checkout-session` - Create checkout - `POST /api/v1/stripe/create-portal-session` - Customer portal - `GET /api/v1/stripe/subscription` - Get subscription status - `GET /api/v1/stripe/is-paying` - Check if paying - `GET /api/v1/stripe/prices` - Get available prices - `POST /api/v1/stripe/cancel-subscription` - Cancel subscription - `POST /api/v1/stripe/reactivate-subscription` - Reactivate subscription ## Frontend Integration The frontend hooks are already implemented in `apps/main/src/hooks/stripe.ts`. ### 1. Install Stripe SDK ```bash cd apps/main npm install @stripe/stripe-js ``` ### 2. Configure Environment Variable Add to `apps/main/.env`: ```env VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx ``` ### 3. Available Hooks All hooks are ready to use: ```typescript import { // Direct Supabase queries (RLS-protected) useSubscription, // Get subscription from Supabase useIsPayingUser, // Check if paying (from user.is_paying) useStripePrices, // Get prices from Supabase // API calls (for actions) useCreateCheckoutSession, // Start checkout useCreatePortalSession, // Open customer portal useCancelSubscription, // Cancel subscription useReactivateSubscription, // Reactivate subscription } from "../hooks/stripe"; ``` **Architecture:** - 📖 **Reads**: Direct Supabase queries (fast, RLS-protected) - ✍️ **Writes**: API calls to Stripe → Webhooks update Supabase ## Usage Examples ### Check if User is Paying ```typescript // In any component const { data: isPaying } = useIsPayingUser(); if (!isPaying) { return ; } ``` ### Get Subscription Details (Queries Supabase Directly) ```typescript const { data: subscription } = useSubscription(); if (subscription) { console.log("Status:", subscription.status); console.log("Renews:", subscription.current_period_end); console.log("Will cancel?:", subscription.cancel_at_period_end); // Access related product/price via join console.log("Product:", subscription.price?.product?.name); console.log("Amount:", subscription.price?.unit_amount); } ``` ### Access Subscription Status from Profile ```typescript // The user profile already has is_paying and subscription_tier fields const user = useUser(); if (user.is_paying) { console.log("User is on:", user.subscription_tier); } ``` ### Create Checkout Session ```typescript import { useCreateCheckoutSession } from "../hooks/stripe"; function UpgradeButton() { const { mutate: createCheckout, isPending } = useCreateCheckoutSession(); return ( ); } ``` ### Manage Subscription (Customer Portal) ```typescript import { useCreatePortalSession } from "../hooks/stripe"; function ManageSubscriptionButton() { const { mutate: openPortal, isPending } = useCreatePortalSession(); return ( ); } ``` ### Cancel Subscription ```typescript import { useCancelSubscription } from "../hooks/stripe"; function CancelButton() { const { mutate: cancel, isPending } = useCancelSubscription(); return ( ); } ``` ## Testing ### 1. Use Stripe Test Mode Use test cards from [Stripe Testing](https://stripe.com/docs/testing): - Success: `4242 4242 4242 4242` - Decline: `4000 0000 0000 0002` ### 2. Test Webhooks Locally ```bash # Install Stripe CLI brew install stripe/stripe-cli/stripe # Login stripe login # Forward webhooks to local API stripe listen --forward-to http://localhost:3000/api/v1/stripe/webhook ``` ### 3. Trigger Test Events ```bash stripe trigger customer.subscription.created stripe trigger customer.subscription.updated stripe trigger customer.subscription.deleted ``` ## Security Checklist - [ ] Stripe API keys stored in Supabase Vault - [ ] Webhook signing secret configured - [ ] Row Level Security (RLS) enabled on all tables - [ ] Edge Function uses service role key - [ ] Customer metadata includes user_id for mapping - [ ] Test mode used for development ## Troubleshooting ### Webhooks Not Working 1. Check webhook endpoint URL is correct (`https://your-api/api/v1/stripe/webhook`) 2. Verify signing secret matches in `.env` 3. Check API logs for webhook errors 4. Ensure `SUPABASE_SERVICE_ROLE_KEY` is set in API environment 5. Test with Stripe CLI: `stripe trigger customer.subscription.created` ### Subscription Not Showing 1. Verify customer has `user_id` in metadata 2. Check webhook was received (Stripe Dashboard → Webhooks) 3. Query `stripe_subscriptions` table directly 4. Check `profiles.is_paying` was updated by trigger ### Foreign Tables Empty 1. Verify Stripe API key is correct 2. Check key_id in server configuration 3. Test with: `SELECT * FROM stripe.customers LIMIT 5;` 4. Ensure Wrappers extension is enabled ## Next Steps 1. Create pricing page component 2. Build subscription management UI 3. Implement usage-based billing (if needed) 4. Add customer portal for self-service 5. Set up email notifications for billing events --- For more information, see: - [Supabase Stripe Integration](https://supabase.com/docs/guides/integrations/stripe) - [Stripe Documentation](https://stripe.com/docs) - [Supabase Wrappers](https://supabase.github.io/wrappers/)