Stripe Integration Testing
Test your Stripe payment integration locally using Stripe CLI.
Overview
This guide covers testing the complete payment flow:
- User purchases credits via Stripe Checkout
- Webhook confirms payment
- Balance added to user account
Prerequisites
- Stripe account (test mode)
- Stripe CLI installed
- Backend running locally
Installation
Install Stripe CLI
# macOS
brew install stripe/stripe-cli/stripe
# Linux
wget https://github.com/stripe/stripe-cli/releases/latest/download/stripe_linux_x86_64.tar.gz
tar -xzf stripe_linux_x86_64.tar.gz
sudo mv stripe /usr/local/bin/stripe
# Verify
stripe version
Login to Stripe
stripe login
This opens your browser to authenticate with your Stripe account (test mode by default).
Local Testing
Step 1: Start Backend
./start_dev.sh
Step 2: Forward Webhooks
In a separate terminal:
stripe listen --forward-to localhost:8080/webhooks/stripe
You should see:
> Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxxxxx
> Forwarding to localhost:8080/webhooks/stripe
Copy the whsec_... secret and add to your .env:
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
Step 3: Trigger Test Events
# Successful checkout (main event)
stripe trigger checkout.session.completed
# Invoice paid (subscription)
stripe trigger invoice.paid
# Subscription cancelled
stripe trigger customer.subscription.deleted
# Refund
stripe trigger charge.refunded
Step 4: Verify in Logs
Your backend should log:
🔔 Stripe webhook: checkout.session.completed
💰 Payment processed: sk-korad-... +$50.00 via Stripe
Test Cards
Use these test cards in Stripe Checkout:
| Card Number | Result |
|---|---|
4242 4242 4242 4242 | Success |
4000 0000 0000 0002 | Card declined |
4000 0000 0000 9995 | Insufficient funds |
4000 0000 0000 0069 | Expired card |
Any future expiry date and any 3-digit CVC work.
Automated Tests
Python Unit Tests
python scripts/test_stripe_webhook.py
Expected Output:
╔════════════════════════════════════════════╗
║ Korad.AI Stripe Tests ║
╚════════════════════════════════════════════╝
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Signature Verification
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Valid signature accepted
✓ Invalid signature rejected
✓ Dev mode: signature verification skipped
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Checkout Session Handler
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Starter ($10): $1.00 credited (100,000 tokens)
✓ Basic ($50): $11.00 credited (1,000,000 tokens)
✓ Pro ($100): $61.00 credited (5,000,000 tokens)
✓ Enterprise ($500): $561.00 credited (50,000,000 tokens)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Test Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Passed: 4
Failed: 0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Shell Script Tests
# Run all tests
./scripts/stripe_test.sh
# Run specific event
./scripts/stripe_test.sh checkout
./scripts/stripe_test.sh subscription
./scripts/stripe_test.sh invoice
./scripts/stripe_test.sh refund
Integration Testing
Full Checkout Flow
- Create Test Product
Go to Stripe Dashboard → Products → Create product:
- Name: "Starter Credit Package"
- Price: $10.00
- Create Checkout Session
curl -X POST http://localhost:8080/webhooks/checkout/create \
-H "Content-Type: application/json" \
-d '{
"api_key": "sk-korad-test123",
"package_id": "starter",
"success_url": "http://localhost:3000/dashboard",
"cancel_url": "http://localhost:3000/dashboard"
}'
Response:
{
"checkout_url": "https://checkout.stripe.com/...",
"session_id": "cs_test_...",
"package": "Starter ($10)",
"amount": "$10.00",
"credits": "100,000"
}
- Complete Checkout
- Visit the
checkout_url - Use test card:
4242 4242 4242 4242 - Complete payment
- Verify Balance Added
curl http://localhost:8080/v1/user/balance \
-H "Authorization: Bearer sk-korad-test123"
Response:
{
"api_key": "sk-korad-test123",
"balance": 10.00
}
Database Verification
Check the database directly:
-- Check user balance
SELECT api_key, balance FROM users WHERE api_key = 'sk-korad-test123';
-- Check transaction
SELECT * FROM transactions WHERE api_key = 'sk-korad-test123' ORDER BY created_at DESC LIMIT 1;
-- Expected results:
-- balance: 10.00
-- transaction: Starter ($10) purchase, +$10.00
Webhook Verification
Signature Verification Test
import hmac
import hashlib
# Test signature verification
payload = b'{"type": "checkout.session.completed"}'
secret = "whsec_your_webhook_secret"
timestamp = str(int(time.time()))
signed_payload = f"{timestamp}.{payload.decode()}"
signature = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# This should match Stripe's signature
expected_sig = f"t={timestamp},v1={signature}"
Event Verification
# Verify event structure
event = {
"id": "evt_test_...",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_...",
"metadata": {
"api_key": "sk-korad-test123",
"package_id": "starter"
}
}
}
}
# Verify required fields
assert "metadata" in event["data"]["object"]
assert "api_key" in event["data"]["object"]["metadata"]
assert "package_id" in event["data"]["object"]["metadata"]
Production Deployment
1. Get Production Webhook Secret
In Stripe Dashboard → Developers → Webhooks:
- Add endpoint:
https://api.korad.ai/webhooks/stripe - Select events:
checkout.session.completed,invoice.paid, etc. - Copy the webhook signing secret (
whsec_...)
2. Set Production Secret
flyctl secrets set STRIPE_WEBHOOK_SECRET=whsec_... --app excell-ai-prod
3. Register Webhook Endpoint
The webhook endpoint is already registered at /webhooks/stripe.
4. Monitor First Live Payments
After deployment:
- Watch Fly.io logs:
flyctl logs --app excell-ai-prod - Check Stripe Dashboard for webhook deliveries
- Verify database updates
Troubleshooting
"Signature verification failed"
Solutions:
- Verify
STRIPE_WEBHOOK_SECRETmatches - Check for extra whitespace in
.env - Ensure secret starts with
whsec_ - Restart backend after updating
.env
"Event not received"
Solutions:
- Verify webhook forwarder is running
- Check port:
stripe listen --forward-to localhost:8080/webhooks/stripe - Try
http://instead ofhttps://locally - Check firewall rules
"Balance not updated"
Solutions:
- Check database connection
- Verify
add_balance()function works - Look for Python errors in logs
- Check transaction was created
"Duplicate events"
Stripe may retry webhooks. Ensure idempotency:
# Check if event was already processed
if transaction_exists(event_id):
return {"status": "already_processed"}
Best Practices
- Always verify signatures — Prevents fake webhooks
- Use idempotency keys — Prevent duplicate processing
- Log all events — Audit trail for debugging
- Test in test mode — Never use live keys for testing
- Monitor first payments — Watch logs after deployment