# 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: ; ``` ### 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.