18 KiB
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:
- Creating test users in your app
- Subscribing with Stripe test cards
- Verifying webhook processing
- Testing subscription management
- 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:
# 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)
- Go to Stripe Dashboard → Switch to Test Mode (toggle in top-right)
- Navigate to Products
- Click Add product
- Fill in:
- Name: Standard
- Description: Standard plan for Xtablo
- Add pricing:
- Monthly: €9.99/month
- Yearly (optional): €99/year
- Copy the price ID (e.g.,
price_1234567890abcdef) - Update your frontend
.env:VITE_STRIPE_STANDARD_MONTHLY_PRICE_ID=price_1234567890abcdef
3. Set Up Local Webhook Testing
Option A: Stripe CLI (Recommended)
# 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
- Sign up with a test email (e.g.,
test@example.com) - Verify email (check your inbox or skip if auto-confirm enabled)
- Login to your app
- Navigate to Settings page
Step 2: Add SubscriptionCard to Settings (If Not Already)
// 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:
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
- Click "Passer à Standard" button
- You'll be redirected to Stripe Checkout
- 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
- Click Subscribe
- You'll be redirected back to your app with
?success=true
Step 5: Verify Webhook Processing
Watch in Terminal:
# 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:
-- 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:
// In browser console:
console.log(user.is_paying); // Should be true
console.log(user.subscription_tier); // Should be 'standard'
Step 7: Test Customer Portal
- Click "Gérer l'abonnement"
- Opens Stripe Customer Portal
- You can:
- Update payment method
- View invoices
- Cancel subscription
- Update billing info
Step 8: Test Subscription Cancellation
From Your App:
- Click "Annuler" button in SubscriptionCard
- Confirms cancellation at period end
- UI updates to show "Annulation en cours"
- Shows "Réactiver l'abonnement" button
Verify Webhook:
# Should see in Stripe CLI:
✓ customer.subscription.updated
Verify in Database:
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
- Click "Réactiver l'abonnement" button
- Subscription continues normally
- UI returns to active state
Verify in Database:
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:
- Complete checkout with
4242 4242 4242 4242 - Verify
status = 'active'
Past Due (Failed Payment):
- Use Stripe CLI:
stripe trigger customer.subscription.updated \ --add customer_subscription:status=past_due - Check database:
status = 'past_due'
Trial Period:
- Create price with trial in Stripe Dashboard
- Subscribe with test card
- 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
-- 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
# 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
// 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:
# 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:
-- 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:
-- 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:
- Is
STRIPE_SECRET_KEYset correctly? - Is price ID valid? (Check Stripe Dashboard)
- Check API logs for error details
- Verify user is authenticated
🎮 Interactive Test Script
Run this in your browser console after logging in:
// 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
# 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_payingupdates to truesubscription_tierupdates 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
# 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
# 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
# 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
# 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
# Use declining card
Card: 4000 0000 0000 0002
# Or trigger webhook
stripe trigger invoice.payment_failed
Simulate Subscription Expiration
-- 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:
- ✅ Free user state (upgrade prompt)
- ✅ Stripe Checkout page
- ✅ Active subscription (green badge)
- ✅ Cancellation warning (orange)
- ✅ Stripe Customer Portal
- ✅ Database showing correct data
🚨 What to Watch For
Red Flags
- ❌
is_payingnot 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
-- 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
-- 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:
- ✅ Free users can upgrade via Stripe Checkout
- ✅ Successful payment creates subscription record
- ✅ Webhook updates
is_paying = true - ✅ UI shows subscription status correctly
- ✅ Users can manage subscription in portal
- ✅ Cancellation sets cancel_at_period_end
- ✅ Reactivation clears cancellation
- ✅ Each user only sees their own data
- ✅ All test cards behave as expected
- ✅ Webhook events process without errors
📞 Need Help?
If something isn't working:
- Check API logs - Look for webhook errors
- Check Stripe Dashboard - Webhooks → Events
- Check Stripe CLI output - See webhook delivery
- Check browser console - Frontend errors
- Query database directly - Verify data state
- 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.