Files
bakery-ia/STRIPE_TESTING_GUIDE.md
2026-01-11 07:50:34 +01:00

829 lines
22 KiB
Markdown

# Stripe Integration Testing Guide
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Environment Setup](#environment-setup)
3. [Stripe Dashboard Configuration](#stripe-dashboard-configuration)
4. [Test Card Numbers](#test-card-numbers)
5. [Testing Scenarios](#testing-scenarios)
6. [Webhook Testing](#webhook-testing)
7. [Common Issues & Solutions](#common-issues--solutions)
8. [Production Checklist](#production-checklist)
---
## Prerequisites
Before you begin testing, ensure you have:
- ✅ Stripe account created (sign up at [stripe.com](https://stripe.com))
- ✅ Node.js and Python environments set up
- ✅ Frontend application running (React + Vite)
- ✅ Backend API running (FastAPI)
- ✅ Database configured and accessible
- ✅ Redis instance running (for caching)
---
## Environment Setup
### Step 1: Access Stripe Test Mode
1. Log in to your Stripe Dashboard: [https://dashboard.stripe.com](https://dashboard.stripe.com)
2. Click on your profile icon in the top right corner
3. Ensure **Test Mode** is enabled (you'll see "TEST DATA" banner at the top)
4. If not enabled, toggle to "Switch to test data"
### Step 2: Retrieve API Keys
1. Navigate to **Developers****API keys**
2. You'll see two types of keys:
- **Publishable key** (starts with `pk_test_...`) - Used in frontend
- **Secret key** (starts with `sk_test_...`) - Used in backend
3. Click "Reveal test key" for the Secret key and copy both keys
### Step 3: Configure Environment Variables
#### Frontend `.env` file:
```bash
# Create or update: /frontend/.env
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key_here
```
#### Backend `.env` file:
```bash
# Create or update: /services/tenant/.env
STRIPE_SECRET_KEY=sk_test_your_secret_key_here
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_here
```
**Note:** The webhook secret will be obtained in Step 4 when setting up webhooks.
### Step 4: Install/Update Dependencies
#### Backend:
```bash
cd services/tenant
pip install -r requirements.txt
# This will install stripe==14.1.0
```
#### Frontend:
```bash
cd frontend
npm install
# Verifies @stripe/react-stripe-js and @stripe/stripe-js are installed
```
---
## Stripe Dashboard Configuration
### Step 1: Create Products and Prices
1. In Stripe Dashboard (Test Mode), go to **Products****Add product**
2. **Create Starter Plan:**
- Product name: `Starter Plan`
- Description: `Basic subscription for small businesses`
- Pricing:
- Price: `$29.00`
- Billing period: `Monthly`
- Currency: `USD` (or your preferred currency)
- Click **Save product**
- Copy the **Price ID** (starts with `price_...`) - you'll need this
3. **Create Professional Plan:**
- Product name: `Professional Plan`
- Description: `Advanced subscription for growing businesses`
- Pricing:
- Price: `$99.00`
- Billing period: `Monthly`
- Currency: `USD`
- Click **Save product**
- Copy the **Price ID**
4. **Update your application configuration:**
- Store these Price IDs in your application settings
- You'll use these when creating subscriptions
### Step 2: Configure Webhooks
1. Navigate to **Developers****Webhooks**
2. Click **+ Add endpoint**
3. **For Local Development:**
- Endpoint URL: `https://your-ngrok-url.ngrok.io/webhooks/stripe`
- (We'll set up ngrok later for local testing)
4. **Select events to listen to:**
- `checkout.session.completed`
- `customer.subscription.created`
- `customer.subscription.updated`
- `customer.subscription.deleted`
- `invoice.payment_succeeded`
- `invoice.payment_failed`
- `customer.subscription.trial_will_end`
5. Click **Add endpoint**
6. **Copy the Webhook Signing Secret:**
- Click on the newly created endpoint
- Click **Reveal** next to "Signing secret"
- Copy the secret (starts with `whsec_...`)
- Add it to your backend `.env` file as `STRIPE_WEBHOOK_SECRET`
---
## Test Card Numbers
Stripe provides test card numbers to simulate different scenarios. **Never use real card details in test mode.**
### Basic Test Cards
| Scenario | Card Number | CVC | Expiry Date |
|----------|-------------|-----|-------------|
| **Successful payment** | `4242 4242 4242 4242` | Any 3 digits | Any future date |
| **Visa (debit)** | `4000 0566 5566 5556` | Any 3 digits | Any future date |
| **Mastercard** | `5555 5555 5555 4444` | Any 3 digits | Any future date |
| **American Express** | `3782 822463 10005` | Any 4 digits | Any future date |
### Authentication & Security
| Scenario | Card Number | Notes |
|----------|-------------|-------|
| **3D Secure authentication required** | `4000 0025 0000 3155` | Triggers authentication modal |
| **3D Secure 2 authentication** | `4000 0027 6000 3184` | Requires SCA authentication |
### Declined Cards
| Scenario | Card Number | Error Message |
|----------|-------------|---------------|
| **Generic decline** | `4000 0000 0000 0002` | Card declined |
| **Insufficient funds** | `4000 0000 0000 9995` | Insufficient funds |
| **Lost card** | `4000 0000 0000 9987` | Lost card |
| **Stolen card** | `4000 0000 0000 9979` | Stolen card |
| **Expired card** | `4000 0000 0000 0069` | Expired card |
| **Incorrect CVC** | `4000 0000 0000 0127` | Incorrect CVC |
| **Processing error** | `4000 0000 0000 0119` | Processing error |
| **Card declined (rate limit)** | `4000 0000 0000 9954` | Exceeds velocity limit |
### Additional Scenarios
| Scenario | Card Number | Notes |
|----------|-------------|-------|
| **Charge succeeds, then fails** | `4000 0000 0000 0341` | Attaches successfully but charge fails |
| **Dispute (fraudulent)** | `4000 0000 0000 0259` | Creates a fraudulent dispute |
| **Dispute (warning)** | `4000 0000 0000 2685` | Creates early fraud warning |
**Important Notes:**
- For **expiry date**: Use any future date (e.g., 12/30)
- For **CVC**: Use any 3-digit number (e.g., 123) or 4-digit for Amex (e.g., 1234)
- For **postal code**: Use any valid format (e.g., 12345)
---
## Testing Scenarios
### Scenario 1: Successful Registration with Payment
**Objective:** Test the complete registration flow with valid payment method.
**Steps:**
1. **Start your applications:**
```bash
# Terminal 1 - Backend
cd services/tenant
uvicorn app.main:app --reload --port 8000
# Terminal 2 - Frontend
cd frontend
npm run dev
```
2. **Navigate to registration page:**
- Open browser: `http://localhost:5173/register` (or your frontend URL)
3. **Fill in user details:**
- Full Name: `John Doe`
- Email: `john.doe+test@example.com`
- Company: `Test Company`
- Password: Create a test password
4. **Fill in payment details:**
- Card Number: `4242 4242 4242 4242`
- Expiry: `12/30`
- CVC: `123`
- Cardholder Name: `John Doe`
- Email: `john.doe+test@example.com`
- Address: `123 Test Street`
- City: `Test City`
- State: `CA`
- Postal Code: `12345`
- Country: `US`
5. **Select a plan:**
- Choose `Starter Plan` or `Professional Plan`
6. **Submit the form**
**Expected Results:**
- ✅ Payment method created successfully
- ✅ User account created
- ✅ Subscription created in Stripe
- ✅ Database records created
- ✅ User redirected to dashboard
- ✅ No console errors
**Verification:**
1. **In Stripe Dashboard:**
- Go to **Customers** → Find "John Doe"
- Go to **Subscriptions** → See active subscription
- Status should be `active`
2. **In your database:**
```sql
SELECT * FROM subscriptions WHERE tenant_id = 'your-tenant-id';
```
- Verify subscription record exists
- Status should be `active`
- Check `stripe_customer_id` is populated
3. **Check application logs:**
- Look for successful subscription creation messages
- Verify no error logs
---
### Scenario 2: Payment with 3D Secure Authentication
**Objective:** Test Strong Customer Authentication (SCA) flow.
**Steps:**
1. Follow steps 1-3 from Scenario 1
2. **Fill in payment details with 3DS card:**
- Card Number: `4000 0025 0000 3155`
- Expiry: `12/30`
- CVC: `123`
- Fill remaining details as before
3. **Submit the form**
4. **Complete authentication:**
- Stripe will display an authentication modal
- Click **"Complete"** (in test mode, no real auth needed)
**Expected Results:**
- ✅ Authentication modal appears
- ✅ After clicking "Complete", payment succeeds
- ✅ Subscription created successfully
- ✅ User redirected to dashboard
**Note:** This simulates European and other markets requiring SCA.
---
### Scenario 3: Declined Payment
**Objective:** Test error handling for declined cards.
**Steps:**
1. Follow steps 1-3 from Scenario 1
2. **Use a declined test card:**
- Card Number: `4000 0000 0000 0002`
- Fill remaining details as before
3. **Submit the form**
**Expected Results:**
- ❌ Payment fails with error message
- ✅ Error displayed to user: "Your card was declined"
- ✅ No customer created in Stripe
- ✅ No subscription created
- ✅ No database records created
- ✅ User remains on payment form
- ✅ Can retry with different card
**Verification:**
- Check Stripe Dashboard → Customers (should not see new customer)
- Check application logs for error handling
- Verify user-friendly error message displayed
---
### Scenario 4: Insufficient Funds
**Objective:** Test specific decline reason handling.
**Steps:**
1. Use card number: `4000 0000 0000 9995`
2. Follow same process as Scenario 3
**Expected Results:**
- ❌ Payment fails
- ✅ Error message: "Your card has insufficient funds"
- ✅ Proper error handling and logging
---
### Scenario 5: Subscription Cancellation
**Objective:** Test subscription cancellation flow.
**Steps:**
1. **Create an active subscription** (use Scenario 1)
2. **Cancel the subscription:**
- Method 1: Through your application UI (if implemented)
- Method 2: API call:
```bash
curl -X POST http://localhost:8000/api/v1/subscriptions/cancel \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
-d '{
"tenant_id": "your-tenant-id",
"reason": "Testing cancellation"
}'
```
**Expected Results:**
- ✅ Subscription status changes to `pending_cancellation`
- ✅ `cancellation_effective_date` is set
- ✅ User retains access until end of billing period
- ✅ Response includes days remaining
- ✅ Subscription cache invalidated
**Verification:**
1. Check database:
```sql
SELECT status, cancellation_effective_date, cancelled_at
FROM subscriptions
WHERE tenant_id = 'your-tenant-id';
```
2. Verify API response:
```json
{
"success": true,
"message": "Subscription cancelled successfully...",
"status": "pending_cancellation",
"cancellation_effective_date": "2026-02-10T...",
"days_remaining": 30
}
```
---
### Scenario 6: Subscription Reactivation
**Objective:** Test reactivating a cancelled subscription.
**Steps:**
1. **Cancel a subscription** (use Scenario 5)
2. **Reactivate the subscription:**
```bash
curl -X POST http://localhost:8000/api/v1/subscriptions/reactivate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
-d '{
"tenant_id": "your-tenant-id",
"plan": "starter"
}'
```
**Expected Results:**
- ✅ Subscription status changes back to `active`
- ✅ `cancelled_at` and `cancellation_effective_date` cleared
- ✅ Next billing date set
- ✅ Subscription cache invalidated
---
### Scenario 7: Retrieve Invoices
**Objective:** Test invoice retrieval from Stripe.
**Steps:**
1. **Create subscription with successful payment** (Scenario 1)
2. **Retrieve invoices:**
```bash
curl -X GET http://localhost:8000/api/v1/subscriptions/{tenant_id}/invoices \
-H "Authorization: Bearer YOUR_AUTH_TOKEN"
```
**Expected Results:**
- ✅ List of invoices returned
- ✅ Each invoice contains:
- `id`
- `date`
- `amount`
- `currency`
- `status`
- `invoice_pdf` URL
- `hosted_invoice_url` URL
**Verification:**
- Click on `hosted_invoice_url` to view invoice in browser
- Download PDF from `invoice_pdf` URL
---
## Webhook Testing
Webhooks are critical for handling asynchronous events from Stripe. Test them thoroughly.
### Option 1: Using Stripe CLI (Recommended for Local Development)
#### Step 1: Install Stripe CLI
**macOS:**
```bash
brew install stripe/stripe-cli/stripe
```
**Windows:**
Download from: https://github.com/stripe/stripe-cli/releases
**Linux:**
```bash
wget https://github.com/stripe/stripe-cli/releases/latest/download/stripe_linux_x86_64.tar.gz
tar -xvf stripe_linux_x86_64.tar.gz
sudo mv stripe /usr/local/bin/
```
#### Step 2: Login to Stripe
```bash
stripe login
```
This opens a browser to authorize the CLI.
#### Step 3: Forward Webhooks to Local Server
```bash
stripe listen --forward-to localhost:8000/webhooks/stripe
```
**Expected Output:**
```
> Ready! Your webhook signing secret is whsec_abc123... (^C to quit)
```
**Important:** Copy this webhook signing secret and add it to your backend `.env`:
```bash
STRIPE_WEBHOOK_SECRET=whsec_abc123...
```
#### Step 4: Trigger Test Events
Open a new terminal and run:
```bash
# Test subscription created
stripe trigger customer.subscription.created
# Test payment succeeded
stripe trigger invoice.payment_succeeded
# Test payment failed
stripe trigger invoice.payment_failed
# Test subscription updated
stripe trigger customer.subscription.updated
# Test subscription deleted
stripe trigger customer.subscription.deleted
# Test trial ending
stripe trigger customer.subscription.trial_will_end
```
#### Step 5: Verify Webhook Processing
**Check your application logs for:**
- ✅ "Processing Stripe webhook event"
- ✅ Event type logged
- ✅ Database updates (check subscription status)
- ✅ No signature verification errors
**Example log output:**
```
INFO Processing Stripe webhook event event_type=customer.subscription.updated
INFO Subscription updated in database subscription_id=sub_123 tenant_id=tenant-id
```
### Option 2: Using ngrok (For Public URL Testing)
#### Step 1: Install ngrok
Download from: https://ngrok.com/download
#### Step 2: Start ngrok
```bash
ngrok http 8000
```
**Output:**
```
Forwarding https://abc123.ngrok.io -> http://localhost:8000
```
#### Step 3: Update Stripe Webhook Endpoint
1. Go to Stripe Dashboard → Developers → Webhooks
2. Click on your endpoint
3. Update URL to: `https://abc123.ngrok.io/webhooks/stripe`
4. Save changes
#### Step 4: Test by Creating Real Events
Create a test subscription through your app, and webhooks will be sent to your ngrok URL.
### Option 3: Testing Webhook Handlers Directly
You can also test webhook handlers by sending test payloads:
```bash
curl -X POST http://localhost:8000/webhooks/stripe \
-H "Content-Type: application/json" \
-H "stripe-signature: test-signature" \
-d @webhook-test-payload.json
```
**Note:** This will fail signature verification unless you disable it temporarily for testing.
---
## Common Issues & Solutions
### Issue 1: "Stripe.js has not loaded correctly"
**Symptoms:**
- Error when submitting payment form
- Console error about Stripe not being loaded
**Solutions:**
1. Check internet connection (Stripe.js loads from CDN)
2. Verify `VITE_STRIPE_PUBLISHABLE_KEY` is set correctly
3. Check browser console for loading errors
4. Ensure no ad blockers blocking Stripe.js
### Issue 2: "Invalid signature" on Webhook
**Symptoms:**
- Webhook endpoint returns 400 error
- Log shows "Invalid webhook signature"
**Solutions:**
1. Verify `STRIPE_WEBHOOK_SECRET` matches Stripe Dashboard
2. For Stripe CLI, use the secret from `stripe listen` output
3. Ensure you're using the test mode secret, not live mode
4. Check that you're not modifying the request body before verification
### Issue 3: Payment Method Not Attaching
**Symptoms:**
- PaymentMethod created but subscription fails
- Error about payment method not found
**Solutions:**
1. Verify you're passing `paymentMethod.id` to backend
2. Check that payment method is being attached to customer
3. Ensure customer_id exists before creating subscription
4. Review backend logs for detailed error messages
### Issue 4: Test Mode vs Live Mode Confusion
**Symptoms:**
- Keys not working
- Data not appearing in dashboard
**Solutions:**
1. **Always check the mode indicator** in Stripe Dashboard
2. Test keys start with `pk_test_` and `sk_test_`
3. Live keys start with `pk_live_` and `sk_live_`
4. Never mix test and live keys
5. Use separate databases for test and live environments
### Issue 5: CORS Errors
**Symptoms:**
- Browser console shows CORS errors
- Requests to backend failing
**Solutions:**
1. Ensure FastAPI CORS middleware is configured:
```python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
### Issue 6: Webhook Events Not Processing
**Symptoms:**
- Webhooks received but database not updating
- Events logged but handlers not executing
**Solutions:**
1. Check event type matches handler (case-sensitive)
2. Verify database session is committed
3. Check for exceptions in handler functions
4. Review logs for specific error messages
5. Ensure subscription exists in database before update
### Issue 7: Card Element vs Payment Element Confusion
**Symptoms:**
- TypeError when calling `stripe.createPaymentMethod()`
- Elements not rendering correctly
**Solutions:**
- Use `PaymentElement` (modern, recommended)
- Call `elements.submit()` before creating payment method
- Pass `elements` object to `createPaymentMethod({ elements })`
- **Our implementation now uses the correct PaymentElement API**
---
## Production Checklist
Before going live with Stripe payments:
### Security
- [ ] All API keys stored in environment variables (never in code)
- [ ] Webhook signature verification enabled and working
- [ ] HTTPS enabled on all endpoints
- [ ] Rate limiting implemented on payment endpoints
- [ ] Input validation on all payment-related forms
- [ ] SQL injection prevention (using parameterized queries)
- [ ] XSS protection enabled
- [ ] CSRF tokens implemented where needed
### Stripe Configuration
- [ ] Live mode API keys obtained from Stripe Dashboard
- [ ] Live mode webhook endpoints configured
- [ ] Webhook signing secret updated for live mode
- [ ] Products and prices created in live mode
- [ ] Business information completed in Stripe Dashboard
- [ ] Bank account added for payouts
- [ ] Tax settings configured (if applicable)
- [ ] Stripe account activated and verified
### Application Configuration
- [ ] Environment variables updated for production
- [ ] Database migrations run on production database
- [ ] Redis cache configured and accessible
- [ ] Error monitoring/logging configured (e.g., Sentry)
- [ ] Payment failure notifications set up
- [ ] Trial ending notifications configured
- [ ] Invoice email delivery tested
### Testing
- [ ] All test scenarios passed (see above)
- [ ] Webhook handling verified for all event types
- [ ] 3D Secure authentication tested
- [ ] Subscription lifecycle tested (create, update, cancel, reactivate)
- [ ] Error handling tested for all failure scenarios
- [ ] Invoice retrieval tested
- [ ] Load testing completed
- [ ] Security audit performed
### Monitoring
- [ ] Stripe Dashboard monitoring set up
- [ ] Application logs reviewed regularly
- [ ] Webhook delivery monitoring configured
- [ ] Payment success/failure metrics tracked
- [ ] Alert thresholds configured
- [ ] Failed payment retry logic implemented
### Compliance
- [ ] Terms of Service updated to mention subscriptions
- [ ] Privacy Policy updated for payment data handling
- [ ] GDPR compliance verified (if applicable)
- [ ] PCI compliance requirements reviewed
- [ ] Customer data retention policy defined
- [ ] Refund policy documented
### Documentation
- [ ] API documentation updated
- [ ] Internal team trained on Stripe integration
- [ ] Customer support documentation created
- [ ] Troubleshooting guide prepared
- [ ] Subscription management procedures documented
---
## Quick Reference Commands
### Stripe CLI Commands
```bash
# Login to Stripe
stripe login
# Listen for webhooks (local development)
stripe listen --forward-to localhost:8000/webhooks/stripe
# Trigger test events
stripe trigger customer.subscription.created
stripe trigger invoice.payment_succeeded
stripe trigger invoice.payment_failed
# View recent events
stripe events list
# Get specific event
stripe events retrieve evt_abc123
# Test webhook endpoint
stripe webhooks test --endpoint-secret whsec_abc123
```
### Testing Shortcuts
```bash
# Start backend (from project root)
cd services/tenant && uvicorn app.main:app --reload --port 8000
# Start frontend (from project root)
cd frontend && npm run dev
# Update backend dependencies
cd services/tenant && pip install -r requirements.txt
# Run database migrations (if using Alembic)
cd services/tenant && alembic upgrade head
```
---
## Additional Resources
- **Stripe Documentation:** https://stripe.com/docs
- **Stripe API Reference:** https://stripe.com/docs/api
- **Stripe Testing Guide:** https://stripe.com/docs/testing
- **Stripe Webhooks Guide:** https://stripe.com/docs/webhooks
- **Stripe CLI Documentation:** https://stripe.com/docs/stripe-cli
- **React Stripe.js Docs:** https://stripe.com/docs/stripe-js/react
- **Stripe Support:** https://support.stripe.com
---
## Support
If you encounter issues not covered in this guide:
1. **Check Stripe Dashboard Logs:**
- Developers → Logs
- View detailed request/response information
2. **Review Application Logs:**
- Check backend logs for detailed error messages
- Look for structured log output from `structlog`
3. **Test in Isolation:**
- Test frontend separately
- Test backend API with cURL
- Verify webhook handling with Stripe CLI
4. **Contact Stripe Support:**
- Live chat available in Stripe Dashboard
- Email support: support@stripe.com
- Community forum: https://stripe.com/community
---
**Last Updated:** January 2026
**Stripe Library Versions:**
- Frontend: `@stripe/stripe-js@4.0.0`, `@stripe/react-stripe-js@3.0.0`
- Backend: `stripe@14.1.0`