737 lines
18 KiB
Markdown
737 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.
|