xtablo-source/docs/STRIPE_SETUP.md
Arthur Belleville 7bb90becb9
IA docs
2025-11-03 09:46:10 +01:00

347 lines
8.6 KiB
Markdown

# 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 <UpgradePrompt />;
}
```
### 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 (
<button
onClick={() =>
createCheckout({
priceId: "price_xxxxx", // Your Standard plan price ID
})
}
disabled={isPending}
>
{isPending ? "Loading..." : "Subscribe to Standard"}
</button>
);
}
```
### Manage Subscription (Customer Portal)
```typescript
import { useCreatePortalSession } from "../hooks/stripe";
function ManageSubscriptionButton() {
const { mutate: openPortal, isPending } = useCreatePortalSession();
return (
<button
onClick={() => openPortal(window.location.href)}
disabled={isPending}
>
Manage Subscription
</button>
);
}
```
### Cancel Subscription
```typescript
import { useCancelSubscription } from "../hooks/stripe";
function CancelButton() {
const { mutate: cancel, isPending } = useCancelSubscription();
return (
<button onClick={() => cancel()} disabled={isPending}>
Cancel Subscription
</button>
);
}
```
## 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/)