xtablo-source/docs/STRIPE_IMPLEMENTATION_SUMMARY.md
2025-11-10 08:53:03 +01:00

394 lines
11 KiB
Markdown

# 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 <PremiumFeatureLockedPrompt />;
}
```
## 📝 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