Billing Testing Guide
This guide explains how to test the billing functionality using real Stripe test API keys and the integrated Supabase/GraphQL stack.
Overview
Our billing tests use real Stripe test API keys instead of mocks to ensure we're testing actual integration behavior. This approach helps catch real issues that mocks might miss, such as:
- API version compatibility
- Webhook timing and delivery
- Payment method validation
- Subscription state transitions
- Error handling for real Stripe responses
Test Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Test Suite │────▶│ GraphQL Layer │────▶│ Stripe │
│ (Vitest) │ │ (Supabase) │ │ Test Mode │
└─────────────────┘ └──────────────────┘ └─────────────┘
│ │ │
└───────────────────────┴───────────────────────┘
Real API calls & webhooks
Test Files
-
billing-test-setup.ts- Test utilities and helpers- Creates test users and organizations
- Manages test Stripe customers
- Provides cleanup functions
- Includes test card numbers
-
billing-graphql.test.ts- GraphQL resolver tests- Tests all billing queries and mutations
- Verifies authorization and permissions
- Tests error handling
-
billing-hooks.test.tsx- React hooks tests- Tests all billing hooks with real API calls
- Verifies cache invalidation
- Tests loading and error states
-
billing-e2e.test.ts- End-to-end flow tests- Complete subscription lifecycle
- Payment method management
- Failed payment recovery
- Trial period handling
Running Tests
Prerequisites
-
Ensure you have test Stripe API keys in your
.env.local:VITE_STRIPE_PUBLIC_KEY=pk_test_...
VITE_STRIPE_SECRET_KEY=sk_test_... -
Ensure Supabase is running locally or you're connected to a test instance:
supabase start # For local development -
Create test products in your Stripe dashboard:
- Go to Stripe Test Dashboard
- Create at least one product with monthly pricing
- Note: Tests will skip if no products are found
Running All Billing Tests
# Run all billing tests
pnpm test billing
# Run specific test file
pnpm test billing-graphql
pnpm test billing-hooks
pnpm test billing-e2e
# Run in watch mode for development
pnpm test billing --watch
# Run with coverage
pnpm test billing --coverage
Running Individual Tests
# Run a specific test suite
pnpm test -t "Subscription Management"
# Run a specific test
pnpm test -t "should create a subscription"
Test Data Management
Automatic Cleanup
All tests automatically clean up after themselves:
- Deletes test Stripe customers
- Cancels test subscriptions
- Removes test payment methods
- Deletes test organizations and users
Test Isolation
Each test run creates unique test data with timestamps:
- Email:
test_${timestamp}@example.com - Organization:
Test Org ${timestamp} - This prevents conflicts between parallel test runs
Test Cards
The test setup includes several test card numbers:
// Always succeeds
testCard: '4242424242424242'
// Requires 3D Secure authentication
testCard3DS: '4000002500003155'
// Always declined
testCardDeclined: '4000000000000002'
See Stripe Testing Cards for more options.
Writing New Tests
1. GraphQL Resolver Test Example
it('should create a subscription', async () => {
const data = await graphqlClient.request(CREATE_SUBSCRIPTION, {
input: {
priceId: 'price_xxx',
quantity: 1,
paymentMethodId: 'pm_xxx',
},
})
expect(data.createSubscription.success).toBe(true)
expect(data.createSubscription.subscription.status).toBe('active')
})
2. Hook Test Example
it('should fetch billing details', async () => {
const { result } = renderHook(() => useBillingDetails(), { wrapper })
await waitFor(() => {
expect(result.current.isSuccess).toBe(true)
})
expect(result.current.data?.organization).toBeDefined()
})
3. E2E Test Example
it('should complete full subscription flow', async () => {
// 1. Add payment method
const paymentMethod = await createTestPaymentMethod(customerId)
// 2. Create subscription
const subscription = await createSubscription(priceId, paymentMethod.id)
// 3. Wait for webhook
await waitForWebhook(() => checkSubscriptionActive())
// 4. Verify state
expect(subscription.status).toBe('active')
})
Debugging Tests
Enable Debug Logging
# Show all GraphQL requests/responses
DEBUG=graphql pnpm test billing
# Show Stripe API calls
DEBUG=stripe pnpm test billing
# Show all logs
DEBUG=* pnpm test billing
Common Issues
-
"No products available"
- Create test products in your Stripe dashboard
- Ensure products are active and have prices
-
"Webhook timeout"
- Check if Supabase Edge Functions are running
- Verify webhook endpoints are configured
- Increase timeout in
waitForWebhook()
-
"Unauthorized" errors
- Check if test user was created successfully
- Verify auth token is being passed to GraphQL
- Ensure user has correct organization role
-
"Customer not found"
- Verify Stripe customer was created
- Check if customer ID was saved to organization
- Ensure using correct Stripe API keys
Inspecting Test Data
During test development, you can inspect data:
// Log GraphQL responses
console.log(JSON.stringify(data, null, 2))
// Check Stripe dashboard
console.log(`Customer: https://dashboard.stripe.com/test/customers/${customerId}`)
// Query database directly
const { data } = await supabase.from('organizations').select('*').eq('id', orgId)
console.log(data)
CI/CD Integration
GitHub Actions Example
name: Billing Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: pnpm install
- name: Run billing tests
env:
VITE_STRIPE_PUBLIC_KEY: ${{ secrets.STRIPE_TEST_PUBLIC_KEY }}
VITE_STRIPE_SECRET_KEY: ${{ secrets.STRIPE_TEST_SECRET_KEY }}
VITE_SUPABASE_URL: ${{ secrets.SUPABASE_TEST_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_TEST_ANON_KEY }}
run: pnpm test billing --run
Local CI Testing
# Run tests as CI would
CI=true pnpm test billing --run --coverage
Best Practices
- Always use test API keys - Never use production keys in tests
- Clean up test data - Ensure cleanup runs even if tests fail
- Use unique identifiers - Timestamp-based IDs prevent conflicts
- Test error paths - Test declined cards, network errors, etc.
- Wait for webhooks - Don't assume immediate consistency
- Test permissions - Verify non-admin users can't access billing
- Monitor test performance - Real API calls are slower than mocks
Security Considerations
- Never commit API keys - Use environment variables
- Use separate test account - Don't mix test and production data
- Rotate test keys regularly - Treat test keys as sensitive
- Limit test data - Clean up old test customers periodically
- Monitor test usage - Watch for unusual test API activity
Troubleshooting
Viewing Stripe Logs
- Go to Stripe Dashboard Logs
- Filter by test mode
- Look for your test customer/subscription IDs
Checking Webhook Delivery
- Go to Stripe Webhooks
- Check webhook attempts and responses
- Verify endpoint is receiving events
Database Queries
-- Find test organizations
SELECT * FROM organizations
WHERE name LIKE 'Test Org %'
ORDER BY created_at DESC;
-- Check subscription status
SELECT * FROM organizations
WHERE stripe_customer_id IS NOT NULL
AND stripe_subscription_id IS NOT NULL;
-- View payment methods
SELECT * FROM payment_methods
WHERE customer_id = 'cus_xxx';