Skip to main content

Stripe Integration Testing

Test your Stripe payment integration locally using Stripe CLI.

Overview

This guide covers testing the complete payment flow:

  1. User purchases credits via Stripe Checkout
  2. Webhook confirms payment
  3. 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 NumberResult
4242 4242 4242 4242Success
4000 0000 0000 0002Card declined
4000 0000 0000 9995Insufficient funds
4000 0000 0000 0069Expired 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

  1. Create Test Product

Go to Stripe Dashboard → Products → Create product:

  • Name: "Starter Credit Package"
  • Price: $10.00
  1. 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"
}
  1. Complete Checkout
  • Visit the checkout_url
  • Use test card: 4242 4242 4242 4242
  • Complete payment
  1. 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:

  1. Verify STRIPE_WEBHOOK_SECRET matches
  2. Check for extra whitespace in .env
  3. Ensure secret starts with whsec_
  4. Restart backend after updating .env

"Event not received"

Solutions:

  1. Verify webhook forwarder is running
  2. Check port: stripe listen --forward-to localhost:8080/webhooks/stripe
  3. Try http:// instead of https:// locally
  4. Check firewall rules

"Balance not updated"

Solutions:

  1. Check database connection
  2. Verify add_balance() function works
  3. Look for Python errors in logs
  4. 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

  1. Always verify signatures — Prevents fake webhooks
  2. Use idempotency keys — Prevent duplicate processing
  3. Log all events — Audit trail for debugging
  4. Test in test mode — Never use live keys for testing
  5. Monitor first payments — Watch logs after deployment

Resources

Development Setup → Deployment →