xtablo-source/docs/STRIPE_QUICK_REFERENCE.md

213 lines
5.5 KiB
Markdown
Raw Permalink Normal View History

2025-11-02 07:56:31 +00:00
# Stripe Integration - Quick Reference
## 🚀 Quick Start (For Your Node.js API)
### 1. Install Dependencies
```bash
2025-11-10 07:53:03 +00:00
cd apps/api && npm install stripe @stripe/stripe-js
2025-11-02 07:56:31 +00:00
cd apps/main && npm install @stripe/stripe-js
```
### 2. Environment Variables
**API (`.env`):**
```env
STRIPE_SECRET_KEY=sk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
SUPABASE_SERVICE_ROLE_KEY=your_service_key
FRONTEND_URL=http://localhost:5173
```
**Frontend (`apps/main/.env`):**
```env
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
```
### 3. Stripe Dashboard Setup
1. Create product named **"Standard"**
2. Add pricing (save price IDs)
3. Add webhook: `https://your-api.com/api/v1/stripe/webhook`
4. Copy webhook secret
### 4. Run Migrations
```sql
-- Execute in Supabase SQL Editor
\i sql/35_stripe_wrappers.sql
\i sql/36_stripe_webhooks.sql
```
2025-11-03 08:46:10 +00:00
## 📋 API Endpoints (Actions Only)
2025-11-02 07:56:31 +00:00
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/stripe/webhook` | ❌ | Stripe webhook (signature verified) |
| POST | `/api/v1/stripe/create-checkout-session` | ✅ | Start subscription flow |
| POST | `/api/v1/stripe/create-portal-session` | ✅ | Open customer portal |
| POST | `/api/v1/stripe/cancel-subscription` | ✅ | Cancel subscription |
| POST | `/api/v1/stripe/reactivate-subscription` | ✅ | Reactivate subscription |
2025-11-03 08:46:10 +00:00
**Note:** Reading subscription data (status, prices, etc.) is done directly via Supabase client from the frontend.
2025-11-02 07:56:31 +00:00
## 🎣 Frontend Hooks
```typescript
import {
2025-11-03 08:46:10 +00:00
// Direct Supabase queries (RLS-protected, no API call)
useSubscription, // Get full subscription from Supabase
useIsPayingUser, // Get is_paying from user profile
useStripePrices, // Get prices from Supabase
// API calls (for Stripe actions)
2025-11-02 07:56:31 +00:00
useCreateCheckoutSession, // Create checkout & redirect
useCreatePortalSession, // Open customer portal
useCancelSubscription, // Cancel at period end
useReactivateSubscription // Undo cancellation
} from '@/hooks/stripe';
```
2025-11-03 08:46:10 +00:00
**Benefits of Direct Supabase Access:**
-**Faster**: No API hop for reads
- 🔒 **Secure**: RLS policies protect data
- 📊 **Real-time**: Can subscribe to changes
2025-11-02 07:56:31 +00:00
## 💡 Common Use Cases
### Check if User is Paying
```typescript
const user = useUser();
if (user.is_paying) {
// Show premium feature
}
```
### Subscribe to Standard Plan
```typescript
const { mutate: checkout } = useCreateCheckoutSession();
<button onClick={() => checkout({ priceId: 'price_xxxxx' })}>
Subscribe to Standard
</button>
```
### Manage Subscription
```typescript
const { mutate: openPortal } = useCreatePortalSession();
<button onClick={() => openPortal()}>
Manage Subscription
</button>
```
### Show Subscription Status
```typescript
const { data: subscription } = useSubscription();
{subscription?.status === 'active' && (
<div>
Active until {subscription.current_period_end}
{subscription.cancel_at_period_end && (
<span>Will cancel at period end</span>
)}
</div>
)}
```
## 🔍 Database Queries
2025-11-03 08:46:10 +00:00
### Frontend Queries (Using Supabase Client)
```typescript
// Get user's subscription
const { data } = await supabase
.from('stripe_subscriptions')
.select('*, price:stripe_prices(*, product:stripe_products(*))')
.eq('user_id', userId)
.single();
// Get available prices
const { data } = await supabase
.from('stripe_prices')
.select('*, product:stripe_products!inner(*)')
.eq('active', true)
.eq('product.name', 'Standard');
```
### Backend SQL Queries
2025-11-02 07:56:31 +00:00
```sql
-- Is user paying?
SELECT is_paying_user('user-uuid-here');
-- Get subscription details
SELECT * FROM get_user_subscription_status('user-uuid-here');
2025-11-03 08:46:10 +00:00
-- Get current user's active subscription (secure, RLS-compliant)
SELECT * FROM get_my_active_subscription();
2025-11-02 07:56:31 +00:00
```
## 🎨 Profile Fields
After subscription sync, profiles have:
```typescript
{
is_paying: boolean, // true if active/trialing subscription
subscription_tier: string, // 'free' | 'standard'
}
```
## 🧪 Test Cards
| Card | Result |
|------|--------|
| `4242 4242 4242 4242` | ✅ Success |
| `4000 0000 0000 0002` | ❌ Declined |
| `4000 0000 0000 9995` | ❌ Insufficient funds |
## 🔔 Webhook Events Handled
-`customer.created`
-`customer.updated`
-`customer.deleted`
-`customer.subscription.created`
-`customer.subscription.updated`
-`customer.subscription.deleted`
-`product.created` / `updated` / `deleted`
-`price.created` / `updated` / `deleted`
## 🎯 Standard Plan Details
**Single Tier Model:**
- **Free**: Default (no subscription)
- **Standard**: Paid subscription
**Subscription automatically:**
- Sets `is_paying = true`
- Sets `subscription_tier = 'standard'`
- Updates on webhook events
- Reverts to free on cancellation
## 🔐 Security
- ✅ Webhook signature verification
- ✅ Row Level Security on all tables
- ✅ Users can only see their own subscriptions
- ✅ Service role for webhook functions
- ✅ Stripe keys in environment (never in code)
## 📞 Support
For issues, check:
1. API logs for webhook errors
2. Stripe Dashboard → Webhooks → Event logs
3. Supabase logs for database errors
4. Browser console for frontend errors
---
**Files:**
2025-11-03 08:46:10 +00:00
- Database: `sql/35_stripe_wrappers.sql` + `sql/36_fix_stripe_subscription_dates.sql` + `sql/37_secure_active_subscriptions.sql`
- Backend: `api/src/stripe.ts` + `api/src/stripeSync.ts`
2025-11-02 07:56:31 +00:00
- Frontend: `apps/main/src/hooks/stripe.ts`
- Types: `packages/shared/src/types/stripe.types.ts`
2025-11-03 08:46:10 +00:00
- Security: `docs/STRIPE_SECURITY_FIX.md`
2025-11-02 07:56:31 +00:00