# Stripe Integration Architecture

## 🏗️ Simplified Webhook-Based Architecture

### Overview

This integration uses a **pure webhook-based approach** without Foreign Data Wrappers:

```
┌─────────────┐
│   Stripe    │ (Source of truth)
└──────┬──────┘
       │ Webhooks
       ↓
┌─────────────┐
│  Node.js    │ (Webhook processor)
│     API     │
└──────┬──────┘
       │ SQL Functions
       ↓
┌─────────────┐
│  Supabase   │ (Data storage)
│   Database  │
└──────┬──────┘
       │ Direct queries (RLS)
       ↓
┌─────────────┐
│  Frontend   │ (React app)
│   (Vite)    │
└─────────────┘
```

## 📊 Data Flow Patterns

### Read Pattern (Fast ⚡)
```
Frontend → Supabase Client → RLS Policies → Database Tables
```

**Examples:**
- Check if user is paying: `user.is_paying`
- Get subscription: `useSubscription()` → queries `stripe_subscriptions`
- Get prices: `useStripePrices()` → queries `stripe_prices`

**Benefits:**
- No API latency
- Real-time capable
- Secure via RLS
- Automatic caching

### Write Pattern (via Stripe)
```
Frontend → API → Stripe → Webhook → Database
                    ↓
              (Direct update for instant feedback)
```

**Examples:**
- Create subscription → API creates checkout → Stripe processes → Webhook syncs
- Cancel subscription → API updates Stripe → immediately updates DB → Webhook confirms
- Manage subscription → Opens Stripe portal → Stripe handles → Webhook syncs

## 🗄️ Database Schema

### Tables

**stripe_customers**
```sql
- id (PK)
- user_id (FK → auth.users) 
- stripe_customer_id (unique)
- email
```

**stripe_subscriptions**
```sql
- id (PK)
- user_id (FK → auth.users)
- stripe_customer_id (FK)
- status (active, trialing, canceled, etc.)
- price_id
- current_period_start
- current_period_end
- cancel_at_period_end
```

**stripe_products**
```sql
- id (PK)
- name ("Standard")
- active
- description
```

**stripe_prices**
```sql
- id (PK)
- product_id (FK)
- unit_amount (in cents)
- currency
- interval (month, year)
```

**profiles** (extended)
```sql
- is_paying (boolean) ← Auto-updated by trigger
- subscription_tier (text) ← Auto-updated by trigger
```

### RLS Policies

```sql
-- Users see only their own data
stripe_customers: WHERE auth.uid() = user_id
stripe_subscriptions: WHERE auth.uid() = user_id

-- Products/prices are public (for pricing page)
stripe_products: WHERE true
stripe_prices: WHERE true
```

### Automatic Triggers

```sql
-- When subscription changes → Update profile
CREATE TRIGGER update_profile_on_subscription_change
  AFTER INSERT OR UPDATE ON stripe_subscriptions
  → Updates profiles.is_paying and profiles.subscription_tier
```

## 🔄 Webhook Flow

### 1. Stripe Event Occurs
```
User subscribes → Stripe creates subscription
```

### 2. Webhook Sent
```
POST https://your-api.com/api/v1/stripe/webhook
Headers: stripe-signature
Body: {
  type: "customer.subscription.created",
  data: { object: { ...subscription } }
}
```

### 3. API Processes Event
```typescript
// stripe-webhook.ts
1. Verify signature
2. Parse event type
3. Call appropriate database function
```

### 4. Database Function Executes
```sql
-- handle_stripe_subscription_upsert()
1. Upsert subscription record
2. Trigger fires automatically
3. Profile updated (is_paying = true)
```

### 5. Frontend Sees Update
```typescript
// Next time query runs
const { data: subscription } = useSubscription();
// Returns updated data with RLS filtering
```

## 🔐 Security Model

### Frontend → Database
```
Supabase Client (anon key)
  → RLS Policies
    → Only own user_id data
      → Read-only access
```

**Frontend CANNOT:**
- ❌ Modify subscription data
- ❌ See other users' subscriptions
- ❌ Delete customer records

**Frontend CAN:**
- ✅ Read own subscription
- ✅ Read public products/prices
- ✅ Call API endpoints for actions

### API → Database
```
Supabase Client (service role key)
  → Full database access
    → Used only in webhook handlers
      → Validates Stripe signature first
```

**API CAN:**
- ✅ Insert/update via webhook functions
- ✅ Query any subscription for operations
- ✅ Execute privileged database functions

### Webhook → Database
```
Stripe Webhook
  → Signature verified
    → Service role functions
      → Bypass RLS for writes
```

## 🎯 Why This Architecture?

### No Foreign Data Wrappers
**Reasons:**
- ❌ Extra complexity
- ❌ Requires Vault configuration
- ❌ API rate limits apply
- ❌ Slower than cached data
- ✅ **Webhooks provide all needed data**

### Direct Supabase Access
**Benefits:**
- ⚡ **Performance**: No API hop for reads
- 🔒 **Security**: RLS enforces data access
- 🎯 **Simplicity**: Standard Supabase patterns
- 📊 **Real-time**: Can use Supabase subscriptions
- 🔌 **Offline**: Can cache subscription data

### API for Actions Only
**Why:**
- 🔑 **Secret management**: API has Stripe secret key
- ✅ **Validation**: Server-side validation before Stripe calls
- 🎫 **Checkout**: Creates secure checkout sessions
- 🔐 **Signatures**: Verifies webhook signatures

## 📈 Performance Characteristics

### Query Performance

**Check if user is paying:**
```typescript
user.is_paying  // Instant (already in memory)
```

**Get subscription details:**
```typescript
useSubscription()  
// ~50-100ms (Supabase query with join)
// Cached for 5 minutes
```

**Start checkout:**
```typescript
useCreateCheckoutSession()
// ~500-1000ms (API → Stripe → Redirect)
```

### Webhook Processing

**Typical webhook flow:**
```
Stripe event → API receives → DB function → Trigger
Total: ~100-300ms
```

**Database trigger:**
```
Subscription updated → Profile updated
Total: ~10-50ms
```

## 🎨 Frontend Usage Patterns

### Pattern 1: Simple Payment Check
```typescript
const user = useUser();
if (!user.is_paying) {
  return <PaywallComponent />;
}
return <PremiumFeature />;
```

### Pattern 2: Detailed Subscription Info
```typescript
const { data: subscription, isLoading } = useSubscription();

if (isLoading) return <Spinner />;
if (!subscription) return <UpgradePrompt />;

return (
  <div>
    Status: {subscription.status}
    Renews: {subscription.current_period_end}
    {subscription.cancel_at_period_end && <CancellationWarning />}
  </div>
);
```

### Pattern 3: Pricing Page
```typescript
const { data: prices } = useStripePrices();
const { mutate: checkout } = useCreateCheckoutSession();

return prices?.map(price => (
  <PriceCard
    key={price.id}
    amount={price.unit_amount}
    interval={price.interval}
    onSelect={() => checkout({ priceId: price.id })}
  />
));
```

## 🔧 Maintenance

### Syncing Products/Prices

Products and prices are synced automatically via webhooks when you:
1. Create/update product in Stripe Dashboard
2. Webhook fires → Syncs to `stripe_products` table
3. Frontend queries updated pricing

### Monitoring Subscriptions

```sql
-- View current user's active subscription (secure, RLS-compliant)
SELECT * FROM get_my_active_subscription();

-- Check for subscription status distribution (requires direct stripe schema access)
SELECT 
  status::text,
  COUNT(*) as count
FROM stripe.subscriptions
GROUP BY status;

-- Find subscriptions ending soon (admin query)
SELECT 
  p.email,
  si.current_period_end
FROM stripe.subscriptions s
JOIN stripe.customers c ON c.id = s.customer
JOIN stripe.subscription_items si ON si.subscription = s.id
JOIN profiles p ON p.id = (c.metadata->>'user_id')::uuid
WHERE to_timestamp(si.current_period_end) < NOW() + INTERVAL '7 days'
  AND s.status::text = 'active';
```

## 📝 Summary

**This architecture provides:**

1. ✅ **Simple**: No wrappers, just webhooks + RLS
2. ✅ **Fast**: Direct Supabase queries for reads
3. ✅ **Secure**: RLS + signature verification
4. ✅ **Maintainable**: Standard patterns
5. ✅ **Scalable**: Handles thousands of subscriptions
6. ✅ **Cost-effective**: Fewer API calls
7. ✅ **Developer-friendly**: TypeScript + React hooks

**Trade-offs:**
- ⏱️ Eventual consistency (webhook delay ~1-5 seconds)
- 🔄 Requires webhook endpoint to be reliable
- 📦 More database tables vs API-only approach

**Verdict:** ✅ Optimal for SaaS apps with subscription model

---

**Implementation Files:**
- Database: `sql/35_stripe_wrappers.sql`, `sql/36_stripe_webhooks.sql`
- Backend: `api/src/stripe.ts`, `api/src/stripe-webhook.ts`
- Frontend: `apps/main/src/hooks/stripe.ts`, `apps/main/src/components/SubscriptionCard.tsx`
- Types: `packages/shared/src/types/stripe.types.ts`

