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

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:

  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:

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

  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)

// 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

  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
  1. Click Subscribe
  2. 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

  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:

# 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

  1. Click "Réactiver l'abonnement" button
  2. Subscription continues normally
  3. 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:

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

Past Due (Failed Payment):

  1. Use Stripe CLI:
    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

-- 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:

  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:

// 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_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

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

  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

-- 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:

  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.