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

736 lines
18 KiB
Markdown

# 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.