829 lines
22 KiB
Markdown
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`
|