# Stripe Testing Guide - Complete Walkthrough

This guide shows you how to test the entire Stripe integration with fake accounts and test data.

## 🎯 Overview

We'll test:

1. Creating test users in your app
2. Subscribing with Stripe test cards
3. Verifying webhook processing
4. Testing subscription management
5. Checking database updates

## 📋 Prerequisites

- [ ] Database migrations run (35 & 36)
- [ ] API running locally
- [ ] Frontend running locally
- [ ] Stripe test mode keys configured
- [ ] Stripe CLI installed (optional but recommended)

## 🔧 Setup for Testing

### 1. Use Stripe Test Mode

**Important**: Always use **test mode** keys for development:

```env
# API .env
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxx  # Test key (not live!)
STRIPE_WEBHOOK_SECRET=whsec_test_xxxxx   # Test webhook secret

# Frontend .env
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxx  # Test publishable key
```

### 2. Create "Standard" Product in Stripe (Test Mode)

1. Go to Stripe Dashboard → Switch to **Test Mode** (toggle in top-right)
2. Navigate to **Products**
3. Click **Add product**
4. Fill in:
   - Name: **Standard**
   - Description: Standard plan for Xtablo
5. Add pricing:
   - **Monthly**: €9.99/month
   - **Yearly** (optional): €99/year
6. **Copy the price ID** (e.g., `price_1234567890abcdef`)
7. Update your frontend `.env`:
   ```env
   VITE_STRIPE_STANDARD_MONTHLY_PRICE_ID=price_1234567890abcdef
   ```

### 3. Set Up Local Webhook Testing

**Option A: Stripe CLI (Recommended)**

```bash
# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login to your Stripe account
stripe login

# Forward webhooks to your local API
stripe listen --forward-to http://localhost:3000/api/v1/stripe/webhook
```

This will give you a webhook secret like `whsec_xxxxx`. Use this in your API `.env`.

**Option B: Use Stripe Dashboard Webhooks**

For production-like testing, add webhook endpoint in Stripe Dashboard pointing to your deployed API.

## 🧪 Testing Flow - Step by Step

### Step 1: Create Test User Account

1. **Sign up** with a test email (e.g., `test@example.com`)
2. **Verify email** (check your inbox or skip if auto-confirm enabled)
3. **Login** to your app
4. Navigate to **Settings** page

### Step 2: Add SubscriptionCard to Settings (If Not Already)

```typescript
// In apps/main/src/pages/settings.tsx
import { SubscriptionCard } from "../components/SubscriptionCard";

// Add in the cards section:
<SubscriptionCard />;
```

### Step 3: Test Free User State

You should see:

- ✅ "Plan Gratuit" badge with "Gratuit" status
- ✅ "Passer à Standard" button
- ✅ User profile shows: `is_paying: false`, `subscription_tier: 'free'`

**Verify in Database:**

```sql
SELECT id, email, is_paying, subscription_tier
FROM profiles
WHERE email = 'test@example.com';
-- Should show: is_paying = false, subscription_tier = 'free'
```

### Step 4: Test Subscription Creation

1. Click **"Passer à Standard"** button
2. You'll be redirected to Stripe Checkout
3. Fill in test card details:

**Test Card Information:**

```
Card Number: 4242 4242 4242 4242
Expiry: Any future date (e.g., 12/34)
CVC: Any 3 digits (e.g., 123)
ZIP: Any 5 digits (e.g., 12345)
Name: Test User
Email: test@example.com
```

4. Click **Subscribe**
5. You'll be redirected back to your app with `?success=true`

### Step 5: Verify Webhook Processing

**Watch in Terminal:**

```bash
# If using Stripe CLI, you'll see:
stripe listen --forward-to http://localhost:3000/api/v1/stripe/webhook

# You should see events like:
✓ customer.created
✓ customer.subscription.created
✓ invoice.created
✓ payment_intent.created
```

**Check API Logs:**
Look for:

```
Processing Stripe webhook: customer.subscription.created
```

**Verify in Database:**

```sql
-- Check customer created
SELECT * FROM stripe_customers WHERE email = 'test@example.com';

-- Check subscription created
SELECT * FROM stripe_subscriptions
WHERE user_id = (SELECT id FROM profiles WHERE email = 'test@example.com');

-- Check profile updated
SELECT is_paying, subscription_tier
FROM profiles
WHERE email = 'test@example.com';
-- Should show: is_paying = true, subscription_tier = 'standard'
```

### Step 6: Verify Frontend Updates

Refresh the settings page. You should see:

- ✅ "Actif" badge (green)
- ✅ "Plan Standard"
- ✅ Renewal date displayed
- ✅ "Gérer l'abonnement" button
- ✅ "Annuler" button

**Check in Console:**

```typescript
// In browser console:
console.log(user.is_paying); // Should be true
console.log(user.subscription_tier); // Should be 'standard'
```

### Step 7: Test Customer Portal

1. Click **"Gérer l'abonnement"**
2. Opens Stripe Customer Portal
3. You can:
   - Update payment method
   - View invoices
   - Cancel subscription
   - Update billing info

### Step 8: Test Subscription Cancellation

**From Your App:**

1. Click **"Annuler"** button in SubscriptionCard
2. Confirms cancellation at period end
3. UI updates to show "Annulation en cours"
4. Shows "Réactiver l'abonnement" button

**Verify Webhook:**

```bash
# Should see in Stripe CLI:
✓ customer.subscription.updated
```

**Verify in Database:**

```sql
SELECT cancel_at_period_end, current_period_end
FROM stripe_subscriptions
WHERE user_id = (SELECT id FROM profiles WHERE email = 'test@example.com');
-- cancel_at_period_end should be true
```

### Step 9: Test Subscription Reactivation

1. Click **"Réactiver l'abonnement"** button
2. Subscription continues normally
3. UI returns to active state

**Verify in Database:**

```sql
SELECT cancel_at_period_end
FROM stripe_subscriptions
WHERE user_id = (SELECT id FROM profiles WHERE email = 'test@example.com');
-- cancel_at_period_end should be false
```

## 🧪 Advanced Testing Scenarios

### Test Different Card Behaviors

```
Success (Visa):        4242 4242 4242 4242
Success (Mastercard):  5555 5555 5555 4444
Decline:               4000 0000 0000 0002
Insufficient funds:    4000 0000 0000 9995
Expired card:          4000 0000 0000 0069
Processing error:      4000 0000 0000 0119
Requires auth (3DS):   4000 0027 6000 3184
```

### Test Subscription States

**Active Subscription:**

1. Complete checkout with `4242 4242 4242 4242`
2. Verify `status = 'active'`

**Past Due (Failed Payment):**

1. Use Stripe CLI:
   ```bash
   stripe trigger customer.subscription.updated \
     --add customer_subscription:status=past_due
   ```
2. Check database: `status = 'past_due'`

**Trial Period:**

1. Create price with trial in Stripe Dashboard
2. Subscribe with test card
3. Verify `status = 'trialing'`

### Test Multiple Test Users

Create multiple test users to verify isolation:

```
test1@example.com → Subscribe
test2@example.com → Stay free
test3@example.com → Subscribe then cancel
```

Verify each user only sees their own subscription data.

## 🔍 Verification Checklist

After each test, verify:

### Database

```sql
-- Check user's subscription
SELECT
  p.email,
  p.is_paying,
  p.subscription_tier,
  s.status,
  s.current_period_end,
  s.cancel_at_period_end
FROM profiles p
LEFT JOIN stripe_subscriptions s ON s.user_id = p.id
WHERE p.email = 'test@example.com';
```

### API Endpoints

```bash
# Test is-paying endpoint
curl http://localhost:3000/api/v1/stripe/is-paying \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Test subscription endpoint
curl http://localhost:3000/api/v1/stripe/subscription \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Test prices endpoint (no auth needed)
curl http://localhost:3000/api/v1/stripe/prices
```

### Frontend

```typescript
// In browser console
import { useUser } from "./providers/UserStoreProvider";
const user = useUser();
console.log({
  isPaying: user.is_paying,
  tier: user.subscription_tier,
});
```

## 🎬 Complete Test Scenario

### Scenario: User Lifecycle

**Day 1: Sign Up (Free)**

```
1. Create account: test-user@example.com
2. Verify email
3. Login
4. Check: is_paying = false
```

**Day 2: Subscribe to Standard**

```
1. Go to Settings
2. Click "Passer à Standard"
3. Use card: 4242 4242 4242 4242
4. Complete checkout
5. Verify: is_paying = true
6. Check subscription end date in UI
```

**Day 15: Update Payment Method**

```
1. Click "Gérer l'abonnement"
2. Add new payment method
3. Remove old one
4. Verify webhook: customer.updated
```

**Day 20: Cancel Subscription**

```
1. Click "Annuler" in SubscriptionCard
2. Verify: cancel_at_period_end = true
3. Check UI shows cancellation notice
4. Verify still have access until period end
```

**Day 25: Reactivate**

```
1. Click "Réactiver l'abonnement"
2. Verify: cancel_at_period_end = false
3. Check UI shows active status
```

**Day 30: Let it Expire (Alternative)**

```
1. Wait for period_end (or use Stripe CLI to simulate)
2. Verify: is_paying = false
3. Check: subscription_tier = 'free'
```

## 🐛 Common Issues & Solutions

### Issue: Webhook not received

**Solution:**

```bash
# Check Stripe CLI is running
stripe listen --forward-to http://localhost:3000/api/v1/stripe/webhook

# Check API logs for errors
# Check Stripe Dashboard → Webhooks for delivery status
```

### Issue: is_paying not updating

**Solution:**

```sql
-- Manually trigger the update function
SELECT update_profile_subscription_status();

-- Or check if trigger exists
SELECT * FROM pg_trigger WHERE tgname = 'update_profile_on_subscription_change';
```

### Issue: Customer not found error

**Solution:**

```sql
-- Verify customer was created
SELECT * FROM stripe_customers WHERE user_id = 'user-uuid';

-- Create manually if needed
INSERT INTO stripe_customers (id, user_id, stripe_customer_id, email)
VALUES ('cus_xxxxx', 'user-uuid', 'cus_xxxxx', 'test@example.com');
```

### Issue: Checkout session fails

**Check:**

1. Is `STRIPE_SECRET_KEY` set correctly?
2. Is price ID valid? (Check Stripe Dashboard)
3. Check API logs for error details
4. Verify user is authenticated

## 🎮 Interactive Test Script

Run this in your browser console after logging in:

```javascript
// Check current status
const checkStatus = async () => {
  const user = window.__USER__; // Or get from store
  console.log("Current Status:", {
    isPaying: user.is_paying,
    tier: user.subscription_tier,
  });

  // Fetch latest subscription
  const response = await fetch("/api/v1/stripe/subscription", {
    headers: { Authorization: `Bearer ${sessionStorage.getItem("token")}` },
  });
  const data = await response.json();
  console.log("Subscription:", data);
};

checkStatus();
```

## 📊 Test Data Reference

### Test Emails

```
test1@example.com - For basic testing
premium@example.com - For subscription testing
cancel@example.com - For cancellation testing
trial@example.com - For trial testing
```

### Test Cards

| Scenario           | Card Number           | Expected Result                |
| ------------------ | --------------------- | ------------------------------ |
| Successful payment | `4242 4242 4242 4242` | Creates active subscription    |
| Declined           | `4000 0000 0000 0002` | Payment fails, no subscription |
| Insufficient funds | `4000 0000 0000 9995` | Payment fails                  |
| 3D Secure required | `4000 0027 6000 3184` | Requires authentication        |
| Expired            | `4000 0000 0000 0069` | Card expired error             |

### Test Webhooks

```bash
# Trigger specific events
stripe trigger customer.subscription.created
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted
stripe trigger invoice.payment_succeeded
stripe trigger invoice.payment_failed
```

## ✅ Complete Test Checklist

### Setup Tests

- [ ] Stripe test keys configured
- [ ] Webhook endpoint accessible
- [ ] Stripe CLI forwarding webhooks
- [ ] "Standard" product exists in Stripe
- [ ] Price IDs saved in environment

### Functionality Tests

- [ ] New user signup works
- [ ] Free user sees upgrade prompt
- [ ] Can create checkout session
- [ ] Checkout redirects to Stripe
- [ ] Can complete payment with test card
- [ ] Returns to app after payment
- [ ] Webhook processes subscription
- [ ] `is_paying` updates to true
- [ ] `subscription_tier` updates to 'standard'
- [ ] UI reflects subscription status
- [ ] Can open customer portal
- [ ] Can cancel subscription
- [ ] Cancellation sets cancel_at_period_end
- [ ] Can reactivate subscription
- [ ] Subscription shows correct end date

### Database Tests

- [ ] Customer record created
- [ ] Subscription record created
- [ ] Profile updated with is_paying
- [ ] Subscription tier set to 'standard'
- [ ] Historical data preserved
- [ ] RLS works (users see only their data)

### Edge Case Tests

- [ ] Multiple subscriptions handling
- [ ] Webhook replay doesn't duplicate data
- [ ] Invalid price ID shows error
- [ ] Non-existent customer handled
- [ ] Webhook signature validation works
- [ ] Failed payment webhooks handled

## 🎓 Full Test Walkthrough

### Create and Test 3 Users

**User 1: Free Forever**

```bash
# 1. Create account
Email: free@example.com
Password: TestPass123!

# 2. Login
# 3. Go to Settings
# 4. Verify: Shows "Plan Gratuit"
# 5. Verify Database:
SELECT is_paying FROM profiles WHERE email = 'free@example.com';
-- Result: false
```

**User 2: Active Subscriber**

```bash
# 1. Create account
Email: premium@example.com
Password: TestPass123!

# 2. Login → Settings
# 3. Click "Passer à Standard"
# 4. Use card: 4242 4242 4242 4242
# 5. Complete checkout
# 6. Verify: Shows "Actif" badge
# 7. Verify Database:
SELECT is_paying, subscription_tier FROM profiles WHERE email = 'premium@example.com';
-- Result: is_paying = true, subscription_tier = 'standard'

# 8. Check subscription details:
SELECT * FROM stripe_subscriptions WHERE user_id = (
  SELECT id FROM profiles WHERE email = 'premium@example.com'
);
```

**User 3: Canceled Subscription**

```bash
# 1. Create account
Email: canceling@example.com
Password: TestPass123!

# 2. Subscribe (same as User 2)
# 3. Click "Annuler"
# 4. Verify: Shows "Annulation en cours" warning
# 5. Verify Database:
SELECT cancel_at_period_end FROM stripe_subscriptions WHERE user_id = (
  SELECT id FROM profiles WHERE email = 'canceling@example.com'
);
-- Result: cancel_at_period_end = true

# 6. Click "Réactiver l'abonnement"
# 7. Verify: cancel_at_period_end = false
```

## 🔬 Advanced Testing

### Test Webhook Locally

```bash
# Terminal 1: Run Stripe CLI
stripe listen --forward-to http://localhost:3000/api/v1/stripe/webhook

# Terminal 2: Trigger events
stripe trigger customer.subscription.created
stripe trigger customer.subscription.updated --add customer_subscription:cancel_at_period_end=true
stripe trigger customer.subscription.deleted
```

### Test Failed Payments

```bash
# Use declining card
Card: 4000 0000 0000 0002

# Or trigger webhook
stripe trigger invoice.payment_failed
```

### Simulate Subscription Expiration

```sql
-- Manually expire a subscription for testing
UPDATE stripe_subscriptions
SET
  current_period_end = NOW() - INTERVAL '1 day',
  status = 'canceled'
WHERE user_id = (SELECT id FROM profiles WHERE email = 'test@example.com');

-- Run the update trigger
SELECT update_profile_subscription_status();

-- Verify profile updated
SELECT is_paying, subscription_tier FROM profiles WHERE email = 'test@example.com';
-- Should show: is_paying = false, subscription_tier = 'free'
```

## 📸 Testing Screenshots Checklist

Take screenshots to verify:

1. ✅ Free user state (upgrade prompt)
2. ✅ Stripe Checkout page
3. ✅ Active subscription (green badge)
4. ✅ Cancellation warning (orange)
5. ✅ Stripe Customer Portal
6. ✅ Database showing correct data

## 🚨 What to Watch For

### Red Flags

- ❌ `is_paying` not updating after payment
- ❌ Multiple subscriptions for same user
- ❌ Webhook signature validation failing
- ❌ Users seeing other users' subscriptions
- ❌ Subscription status not syncing

### Green Flags

- ✅ Webhooks arrive within seconds
- ✅ Database updates automatically
- ✅ UI reflects changes immediately
- ✅ RLS prevents unauthorized access
- ✅ All test cards behave as expected

## 🎯 Quick Verification Commands

### One-Line Database Check

```sql
-- Check everything for a test user
SELECT
  p.email,
  p.is_paying,
  p.subscription_tier,
  s.status as subscription_status,
  s.cancel_at_period_end,
  to_char(s.current_period_end, 'YYYY-MM-DD HH24:MI') as ends_at,
  sc.stripe_customer_id
FROM profiles p
LEFT JOIN stripe_subscriptions s ON s.user_id = p.id
LEFT JOIN stripe_customers sc ON sc.user_id = p.id
WHERE p.email LIKE 'test%'
ORDER BY p.created_at DESC;
```

### Check Webhook Function Works

```sql
-- Test is_paying function
SELECT is_paying_user((SELECT id FROM profiles WHERE email = 'premium@example.com'));

-- Test subscription status function
SELECT * FROM get_user_subscription_status(
  (SELECT id FROM profiles WHERE email = 'premium@example.com')
);
```

## 🎉 Success Criteria

Your integration is working correctly when:

1. ✅ Free users can upgrade via Stripe Checkout
2. ✅ Successful payment creates subscription record
3. ✅ Webhook updates `is_paying = true`
4. ✅ UI shows subscription status correctly
5. ✅ Users can manage subscription in portal
6. ✅ Cancellation sets cancel_at_period_end
7. ✅ Reactivation clears cancellation
8. ✅ Each user only sees their own data
9. ✅ All test cards behave as expected
10. ✅ Webhook events process without errors

## 📞 Need Help?

If something isn't working:

1. **Check API logs** - Look for webhook errors
2. **Check Stripe Dashboard** - Webhooks → Events
3. **Check Stripe CLI output** - See webhook delivery
4. **Check browser console** - Frontend errors
5. **Query database directly** - Verify data state
6. **Check environment variables** - All keys set?

---

**Next**: Once all tests pass, you're ready for production!  
See `docs/STRIPE_SETUP.md` for production deployment guide.
