---
name: stripe-payment-flow-tester
description: Stripe payment integration testing covering checkout sessions, webhooks, subscriptions, payment intents, and billing workflows
category: testing
priority: critical
isolation: worktree
---

# Stripe Payment Flow Tester Agent

## Purpose

Validate complete Stripe payment integration including checkout session creation, webhook handling, subscription management, payment intent processing, and billing workflows.

## Stripe Integration Testing

### 1. Checkout Session Creation

**API:** `POST /api/stripe/create-checkout-session`

**Contract:**

```typescript
interface CheckoutSessionRequest {
  priceId: string; // Stripe price ID (price_xxx)
  userId: string; // Authenticated user ID
  successUrl: string; // Return URL after payment
  cancelUrl: string; // Return URL on cancel
  billingPeriod?: "monthly" | "yearly";
}

interface CheckoutSessionResponse {
  sessionId: string; // Stripe session ID (cs_xxx)
  url: string; // Stripe Checkout URL
}
```

**Test Cases:**

```typescript
test.describe("Stripe Checkout Session", () => {
  test("creates checkout session successfully", async ({ request }) => {
    const response = await request.post("/api/stripe/create-checkout-session", {
      data: {
        priceId: "price_test_tier2_monthly",
        userId: "user_123",
        successUrl: "http://localhost:9000/en/signup/success",
        cancelUrl: "http://localhost:9000/en/pricing",
      },
    });

    expect(response.status()).toBe(200);

    const body = await response.json();
    expect(body).toMatchObject({
      sessionId: expect.stringMatching(/^cs_test_/),
      url: expect.stringContaining("checkout.stripe.com"),
    });
  });

  test("validates price ID exists", async ({ request }) => {
    const response = await request.post("/api/stripe/create-checkout-session", {
      data: {
        priceId: "price_invalid_xxx",
        userId: "user_123",
        successUrl: "http://localhost:9000/en/signup/success",
        cancelUrl: "http://localhost:9000/en/pricing",
      },
    });

    expect(response.status()).toBe(400);
    expect(await response.json()).toMatchObject({
      error: expect.stringContaining("Invalid price"),
    });
  });

  test("associates session with user metadata", async ({ request }) => {
    const response = await request.post("/api/stripe/create-checkout-session", {
      data: {
        priceId: "price_test_tier2_monthly",
        userId: "user_metadata_test",
        successUrl: "http://localhost:9000/en/signup/success",
        cancelUrl: "http://localhost:9000/en/pricing",
      },
    });

    const body = await response.json();

    // Retrieve session from Stripe
    const session = await stripe.checkout.sessions.retrieve(body.sessionId);

    expect(session.metadata?.userId).toBe("user_metadata_test");
  });
});
```

### 2. Webhook Event Handling

**Webhook Endpoint:** `POST /api/stripe/webhook`

**Events to Handle:**

- `checkout.session.completed` - Payment successful
- `payment_intent.succeeded` - One-time payment confirmed
- `payment_intent.payment_failed` - Payment failed
- `customer.subscription.created` - Subscription started
- `customer.subscription.updated` - Subscription changed
- `customer.subscription.deleted` - Subscription cancelled
- `invoice.payment_succeeded` - Recurring payment succeeded
- `invoice.payment_failed` - Recurring payment failed

**Test Cases:**

```typescript
test.describe("Stripe Webhook Handler", () => {
  test("processes checkout.session.completed", async ({ request }) => {
    const payload = {
      id: "evt_test_checkout_completed",
      type: "checkout.session.completed",
      data: {
        object: {
          id: "cs_test_123",
          customer: "cus_test_123",
          client_reference_id: "user_123",
          metadata: {
            userId: "user_123",
            planId: "tier2",
          },
          amount_total: 9900,
          currency: "usd",
          payment_status: "paid",
        },
      },
    };

    const signature = generateStripeSignature(
      JSON.stringify(payload),
      process.env.STRIPE_WEBHOOK_SECRET!,
    );

    const response = await request.post("/api/stripe/webhook", {
      headers: {
        "stripe-signature": signature,
        "content-type": "application/json",
      },
      data: JSON.stringify(payload),
    });

    expect(response.status()).toBe(200);
    expect(await response.json()).toMatchObject({
      received: true,
    });

    // Verify user subscription updated in database
    const user = await prisma.user.findUnique({
      where: { id: "user_123" },
      include: { subscription: true },
    });

    expect(user?.subscription?.status).toBe("active");
    expect(user?.subscription?.stripeCustomerId).toBe("cus_test_123");
  });

  test("handles payment_intent.succeeded", async ({ request }) => {
    const payload = {
      id: "evt_test_payment_succeeded",
      type: "payment_intent.succeeded",
      data: {
        object: {
          id: "pi_test_123",
          amount: 9900,
          currency: "usd",
          metadata: {
            userId: "user_123",
          },
          status: "succeeded",
        },
      },
    };

    const signature = generateStripeSignature(
      JSON.stringify(payload),
      process.env.STRIPE_WEBHOOK_SECRET!,
    );

    const response = await request.post("/api/stripe/webhook", {
      headers: { "stripe-signature": signature },
      data: JSON.stringify(payload),
    });

    expect(response.status()).toBe(200);

    // Verify payment recorded in database
    const payment = await prisma.payment.findUnique({
      where: { stripePaymentIntentId: "pi_test_123" },
    });

    expect(payment).toBeTruthy();
    expect(payment?.status).toBe("succeeded");
  });

  test("handles payment failure gracefully", async ({ request }) => {
    const payload = {
      id: "evt_test_payment_failed",
      type: "payment_intent.payment_failed",
      data: {
        object: {
          id: "pi_test_failed_123",
          status: "failed",
          last_payment_error: {
            message: "Your card was declined.",
          },
          metadata: {
            userId: "user_123",
          },
        },
      },
    };

    const signature = generateStripeSignature(
      JSON.stringify(payload),
      process.env.STRIPE_WEBHOOK_SECRET!,
    );

    const response = await request.post("/api/stripe/webhook", {
      headers: { "stripe-signature": signature },
      data: JSON.stringify(payload),
    });

    expect(response.status()).toBe(200);

    // Verify user notified (email sent or notification created)
    // Check audit log for payment failure
    const auditLog = await prisma.auditLog.findFirst({
      where: {
        action: "PAYMENT_FAILED",
        userId: "user_123",
      },
      orderBy: { timestamp: "desc" },
    });

    expect(auditLog).toBeTruthy();
  });

  test("webhook is idempotent", async ({ request }) => {
    const payload = {
      id: "evt_idempotent_test_123",
      type: "checkout.session.completed",
      data: { object: { id: "cs_test_123" } },
    };

    const signature = generateStripeSignature(
      JSON.stringify(payload),
      process.env.STRIPE_WEBHOOK_SECRET!,
    );

    // Send webhook twice
    const response1 = await request.post("/api/stripe/webhook", {
      headers: { "stripe-signature": signature },
      data: JSON.stringify(payload),
    });

    const response2 = await request.post("/api/stripe/webhook", {
      headers: { "stripe-signature": signature },
      data: JSON.stringify(payload),
    });

    expect(response1.status()).toBe(200);
    expect(response2.status()).toBe(200);

    // Verify only processed once (check event log in DB)
    const processedEvents = await prisma.stripeEvent.count({
      where: { stripeEventId: "evt_idempotent_test_123" },
    });

    expect(processedEvents).toBe(1); // Only one entry
  });
});
```

### 3. Subscription Management

**Subscription Lifecycle:**

1. User selects plan → Checkout session created
2. Payment succeeds → `checkout.session.completed` event
3. Subscription created → `customer.subscription.created` event
4. Recurring billing → `invoice.payment_succeeded` monthly/yearly
5. Subscription updated → `customer.subscription.updated` event (upgrade/downgrade)
6. Subscription cancelled → `customer.subscription.deleted` event

**Test Cases:**

```typescript
test.describe("Subscription Lifecycle", () => {
  test("subscription activates after payment", async ({ request }) => {
    // Simulate checkout completion
    const checkoutPayload = {
      id: "evt_subscription_test",
      type: "checkout.session.completed",
      data: {
        object: {
          id: "cs_test_subscription",
          customer: "cus_test_123",
          subscription: "sub_test_123",
          metadata: {
            userId: "user_subscription_test",
            planId: "tier2",
          },
        },
      },
    };

    await sendWebhook(checkoutPayload);

    // Verify subscription created
    const user = await prisma.user.findUnique({
      where: { id: "user_subscription_test" },
      include: { subscription: true },
    });

    expect(user?.subscription?.status).toBe("active");
    expect(user?.subscription?.stripeSubscriptionId).toBe("sub_test_123");
    expect(user?.subscription?.planId).toBe("tier2");
  });

  test("subscription upgrade updates database", async ({ request }) => {
    // Create initial subscription (tier1)
    await createTestSubscription("user_upgrade_test", "tier1");

    // Simulate subscription update to tier2
    const updatePayload = {
      id: "evt_subscription_updated",
      type: "customer.subscription.updated",
      data: {
        object: {
          id: "sub_test_upgrade",
          customer: "cus_test_123",
          items: {
            data: [
              {
                price: {
                  id: "price_tier2_monthly",
                  product: "prod_tier2",
                },
              },
            ],
          },
          metadata: {
            userId: "user_upgrade_test",
          },
        },
      },
    };

    await sendWebhook(updatePayload);

    // Verify plan upgraded
    const subscription = await prisma.subscription.findFirst({
      where: { userId: "user_upgrade_test" },
    });

    expect(subscription?.planId).toBe("tier2");
  });

  test("subscription cancellation updates status", async ({ request }) => {
    await createTestSubscription("user_cancel_test", "tier2");

    const cancelPayload = {
      id: "evt_subscription_deleted",
      type: "customer.subscription.deleted",
      data: {
        object: {
          id: "sub_test_cancel",
          customer: "cus_test_123",
          metadata: {
            userId: "user_cancel_test",
          },
        },
      },
    };

    await sendWebhook(cancelPayload);

    const subscription = await prisma.subscription.findFirst({
      where: { userId: "user_cancel_test" },
    });

    expect(subscription?.status).toBe("cancelled");
    expect(subscription?.cancelledAt).toBeTruthy();
  });
});
```

### 4. Payment Intent Testing

**Test Cards (Stripe Test Mode):**

- `4242 4242 4242 4242` - Success
- `4000 0000 0000 9995` - Declined
- `4000 0025 0000 3155` - Requires authentication (3D Secure)
- `4000 0000 0000 0341` - Attaches to Customer

**Test Cases:**

```typescript
test.describe("Payment Intent Flows", () => {
  test("successful payment with test card", async ({ page }) => {
    await page.goto("/en/pricing");
    await page.click('[data-tier="tier2"] button');

    // Fill signup form
    await page.fill('[name="email"]', "payment-test@example.com");
    await page.fill('[name="password"]', "SecurePass123!");
    await page.fill('[name="fullName"]', "Payment Test");
    await page.click('[type="submit"]');

    // Skip OTP (auto-verify in test mode)
    await page.goto("/en/signup/company-info");

    // Fill company info
    await page.fill('[name="companyName"]', "Test Clinic");
    await page.selectOption('[name="industry"]', "healthcare");
    await page.click('[type="submit"]');

    // Should redirect to Stripe Checkout
    await page.waitForURL(/checkout.stripe.com/);

    // Fill payment details
    await page.fill('[name="cardNumber"]', "4242424242424242");
    await page.fill('[name="cardExpiry"]', "12/25");
    await page.fill('[name="cardCvc"]', "123");
    await page.fill('[name="billingName"]', "Payment Test");
    await page.click('[type="submit"]');

    // Should redirect back to success URL
    await page.waitForURL("**/signup/success");

    // Verify payment succeeded
    const successMessage = await page
      .locator('[data-testid="payment-success"]')
      .textContent();
    expect(successMessage).toContain("Payment successful");
  });

  test("handles declined card", async ({ page }) => {
    // Use declined test card: 4000 0000 0000 9995
    await page.goto("/en/pricing");
    await page.click('[data-tier="tier2"] button');

    // ... fill signup form ...

    // At Stripe Checkout
    await page.waitForURL(/checkout.stripe.com/);
    await page.fill('[name="cardNumber"]', "4000000000009995");
    await page.fill('[name="cardExpiry"]', "12/25");
    await page.fill('[name="cardCvc"]', "123");
    await page.click('[type="submit"]');

    // Should show error
    const errorMessage = await page.locator(".error-message").textContent();
    expect(errorMessage).toContain("declined");
  });

  test("handles 3D Secure authentication", async ({ page }) => {
    // Use 3DS test card: 4000 0025 0000 3155
    await page.goto("/en/pricing");
    await page.click('[data-tier="tier3"] button');

    // ... fill signup and company info ...

    await page.waitForURL(/checkout.stripe.com/);
    await page.fill('[name="cardNumber"]', "4000002500003155");
    await page.fill('[name="cardExpiry"]', "12/25");
    await page.fill('[name="cardCvc"]', "123");
    await page.click('[type="submit"]');

    // Should redirect to 3DS authentication
    await page.waitForSelector('[name="challengeDataEntry"]');

    // Complete 3DS (Stripe test mode auto-completes)
    await page.click('[data-testid="complete-authentication"]');

    // Should complete payment
    await page.waitForURL("**/signup/success");
  });
});
```

### 5. Webhook Signature Verification

**Security Critical:**

```typescript
test.describe("Webhook Security", () => {
  test("rejects webhook without signature", async ({ request }) => {
    const response = await request.post("/api/stripe/webhook", {
      data: {
        type: "payment_intent.succeeded",
        data: { object: {} },
      },
      // No stripe-signature header
    });

    expect(response.status()).toBe(400);
    expect(await response.json()).toMatchObject({
      error: expect.stringContaining("signature"),
    });
  });

  test("rejects webhook with invalid signature", async ({ request }) => {
    const response = await request.post("/api/stripe/webhook", {
      headers: {
        "stripe-signature": "t=123,v1=invalidsignature",
      },
      data: {
        type: "payment_intent.succeeded",
        data: { object: {} },
      },
    });

    expect(response.status()).toBe(400);
  });

  test("accepts webhook with valid signature", async ({ request }) => {
    const payload = {
      id: "evt_valid_signature_test",
      type: "payment_intent.succeeded",
      data: {
        object: {
          id: "pi_test_123",
          amount: 9900,
          status: "succeeded",
        },
      },
    };

    const signature = generateStripeSignature(
      JSON.stringify(payload),
      process.env.STRIPE_WEBHOOK_SECRET!,
    );

    const response = await request.post("/api/stripe/webhook", {
      headers: { "stripe-signature": signature },
      data: JSON.stringify(payload),
    });

    expect(response.status()).toBe(200);
  });
});

// Helper function
function generateStripeSignature(payload: string, secret: string): string {
  const crypto = require("crypto");
  const timestamp = Math.floor(Date.now() / 1000);
  const signedPayload = `${timestamp}.${payload}`;
  const signature = crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  return `t=${timestamp},v1=${signature}`;
}
```

### 6. Pricing Tier Validation

**Plans to Test:**

- Tier 1 (Starter): $49/mo or $470/yr (4% discount)
- Tier 2 (Professional): $99/mo or $950/yr (4% discount)
- Tier 3 (Enterprise): $199/mo or $1,910/yr (4% discount)

**Test Cases:**

```typescript
test.describe("Pricing Calculation", () => {
  test("monthly pricing is correct", async ({ request }) => {
    const plans = [
      { tier: "tier1", expectedAmount: 4900 }, // $49.00
      { tier: "tier2", expectedAmount: 9900 }, // $99.00
      { tier: "tier3", expectedAmount: 19900 }, // $199.00
    ];

    for (const plan of plans) {
      const response = await request.post(
        "/api/stripe/create-checkout-session",
        {
          data: {
            priceId: `price_${plan.tier}_monthly`,
            userId: "user_123",
            billingPeriod: "monthly",
            successUrl: "http://localhost:9000/en/signup/success",
            cancelUrl: "http://localhost:9000/en/pricing",
          },
        },
      );

      const body = await response.json();
      const session = await stripe.checkout.sessions.retrieve(body.sessionId);

      expect(session.amount_total).toBe(plan.expectedAmount);
    }
  });

  test("yearly pricing includes 4% discount", async ({ request }) => {
    const plans = [
      { tier: "tier1", monthly: 4900, yearly: 47040 }, // $49 × 12 × 0.96
      { tier: "tier2", monthly: 9900, yearly: 95040 }, // $99 × 12 × 0.96
      { tier: "tier3", monthly: 19900, yearly: 191040 }, // $199 × 12 × 0.96
    ];

    for (const plan of plans) {
      const response = await request.post(
        "/api/stripe/create-checkout-session",
        {
          data: {
            priceId: `price_${plan.tier}_yearly`,
            userId: "user_123",
            billingPeriod: "yearly",
            successUrl: "http://localhost:9000/en/signup/success",
            cancelUrl: "http://localhost:9000/en/pricing",
          },
        },
      );

      const body = await response.json();
      const session = await stripe.checkout.sessions.retrieve(body.sessionId);

      const expectedYearly = plan.monthly * 12 * 0.96; // 4% discount
      expect(session.amount_total).toBeCloseTo(expectedYearly, 0);
    }
  });
});
```

### 7. Currency Detection & Conversion

**Supported Currencies:**

- USD (United States)
- EUR (Europe)
- GBP (United Kingdom)
- AED (UAE)
- SAR (Saudi Arabia)
- QAR (Qatar)

**Test Cases:**

```typescript
test.describe("Currency Handling", () => {
  test("detects currency from user location", async ({ page }) => {
    // Mock geolocation for UAE
    await page.addInitScript(() => {
      navigator.geolocation.getCurrentPosition = (success) => {
        success({
          coords: {
            latitude: 25.2048,
            longitude: 55.2708,
          },
          timestamp: Date.now(),
        });
      };
    });

    await page.goto("/en/pricing");

    // Should display prices in AED
    const currency = await page
      .locator('[data-testid="currency-display"]')
      .textContent();
    expect(currency).toContain("AED");
  });

  test("converts prices correctly", async ({ page }) => {
    await page.goto("/en/pricing?currency=AED");

    const tier2Price = await page
      .locator('[data-tier="tier2"] .price')
      .textContent();

    // $99 USD ≈ 364 AED (at 1 USD = 3.67 AED)
    expect(tier2Price).toContain("364");
    expect(tier2Price).toContain("AED");
  });

  test("creates Stripe session with correct currency", async ({ request }) => {
    const response = await request.post("/api/stripe/create-checkout-session", {
      data: {
        priceId: "price_tier2_monthly_aed",
        userId: "user_123",
        currency: "aed",
        successUrl: "http://localhost:9000/en/signup/success",
        cancelUrl: "http://localhost:9000/en/pricing",
      },
    });

    const body = await response.json();
    const session = await stripe.checkout.sessions.retrieve(body.sessionId);

    expect(session.currency).toBe("aed");
  });
});
```

### 8. Billing & Invoice Testing

**Test Cases:**

```typescript
test.describe("Billing & Invoices", () => {
  test("invoice generated on subscription renewal", async ({ request }) => {
    const payload = {
      type: "invoice.payment_succeeded",
      data: {
        object: {
          id: "in_test_123",
          customer: "cus_test_123",
          subscription: "sub_test_123",
          amount_paid: 9900,
          currency: "usd",
          hosted_invoice_url: "https://invoice.stripe.com/...",
        },
      },
    };

    await sendWebhook(payload);

    // Verify invoice recorded
    const invoice = await prisma.invoice.findUnique({
      where: { stripeInvoiceId: "in_test_123" },
    });

    expect(invoice).toBeTruthy();
    expect(invoice?.status).toBe("paid");
    expect(invoice?.amount).toBe(9900);
  });

  test("failed payment suspends access", async ({ request }) => {
    const payload = {
      type: "invoice.payment_failed",
      data: {
        object: {
          id: "in_test_failed",
          customer: "cus_test_123",
          subscription: "sub_test_123",
          metadata: {
            userId: "user_payment_failed",
          },
        },
      },
    };

    await sendWebhook(payload);

    // Verify subscription marked past_due
    const subscription = await prisma.subscription.findFirst({
      where: { userId: "user_payment_failed" },
    });

    expect(subscription?.status).toBe("past_due");

    // Verify dashboard access restricted
    // (Test with actual dashboard route)
  });
});
```

## Stripe CLI Testing

### Local Webhook Testing

```bash
# 1. Login to Stripe
npm run stripe:login

# 2. Start webhook forwarding
npm run stripe:listen
# Or with events filter
stripe listen --forward-to localhost:9000/api/stripe/webhook --events payment_intent.succeeded,checkout.session.completed

# 3. Trigger test events
npm run stripe:trigger:payment
npm run stripe:trigger:checkout
npm run stripe:trigger:subscription

# 4. Monitor webhook logs
tail -f /tmp/stripe-webhook.log
```

### Test Event Triggers

```bash
# Successful payment
stripe trigger payment_intent.succeeded

# Checkout session completed
stripe trigger checkout.session.completed

# Subscription created
stripe trigger customer.subscription.created

# Subscription updated
stripe trigger customer.subscription.updated

# Payment failed
stripe trigger payment_intent.payment_failed

# Invoice paid
stripe trigger invoice.payment_succeeded
```

### Verify Webhook Delivery

```bash
# Check recent events
stripe events list --limit 10

# Get specific event
stripe events retrieve evt_xxx

# Check webhook endpoint logs
stripe webhook-endpoints list
stripe webhook-endpoints retrieve we_xxx
```

## Integration Testing

### End-to-End Payment Flow

```typescript
test.describe("E2E Payment Flow", () => {
  test("complete payment from pricing to dashboard access", async ({
    page,
  }) => {
    // 1. Select plan
    await page.goto("/en/pricing");
    await page.click('[data-tier="tier2"] button');

    // 2. Complete signup
    await page.fill('[name="email"]', "e2e-payment@example.com");
    await page.fill('[name="password"]', "SecurePass123!");
    await page.fill('[name="fullName"]', "E2E Payment Test");
    await page.click('[type="submit"]');

    // 3. Verify email (auto in test mode)
    await page.goto("/en/signup/verify");
    await page.fill('[data-index="0"]', "1");
    await page.fill('[data-index="1"]', "2");
    await page.fill('[data-index="2"]', "3");
    await page.fill('[data-index="3"]', "4");
    await page.fill('[data-index="4"]', "5");
    await page.fill('[data-index="5"]', "6");
    await page.click('[data-testid="verify-button"]');

    // 4. Fill company info
    await page.fill('[name="companyName"]', "E2E Test Clinic");
    await page.selectOption('[name="industry"]', "healthcare");
    await page.selectOption('[name="companySize"]', "1-10");
    await page.selectOption('[name="gccCountry"]', "uae");
    await page.fill('[name="phoneNumber"]', "+971501234567");
    await page.fill('[name="agentCount"]', "5");
    await page.click('[type="submit"]');

    // 5. Complete payment at Stripe
    await page.waitForURL(/checkout.stripe.com/);
    await page.fill('[name="cardNumber"]', "4242424242424242");
    await page.fill('[name="cardExpiry"]', "12/25");
    await page.fill('[name="cardCvc"]', "123");
    await page.fill('[name="billingName"]', "E2E Payment Test");
    await page.click('[type="submit"]');

    // 6. Verify success
    await page.waitForURL("**/signup/success");
    const successHeading = await page.locator("h1").textContent();
    expect(successHeading).toContain("Welcome");

    // 7. Wait for webhook processing
    await page.waitForTimeout(3000);

    // 8. Verify dashboard access
    await page.goto("/en/dashboard");
    await expect(page).toHaveURL(/.*dashboard/);

    const dashboardWelcome = await page
      .locator('[data-testid="user-name"]')
      .textContent();
    expect(dashboardWelcome).toContain("E2E Payment Test");

    // 9. Verify subscription status in database
    const user = await prisma.user.findUnique({
      where: { email: "e2e-payment@example.com" },
      include: { subscription: true },
    });

    expect(user?.subscription?.status).toBe("active");
    expect(user?.subscription?.planId).toBe("tier2");
  });
});
```

## Test Data Management

### Stripe Test Mode Setup

```bash
# Create test products and prices
stripe products create --name "Tier 1 - Starter"
stripe prices create --product prod_xxx --unit-amount 4900 --currency usd --recurring interval=month

# Create test customers
stripe customers create --email test@example.com --name "Test Customer"

# Create test payment methods
stripe payment_methods create --type card --card[number]=4242424242424242 --card[exp_month]=12 --card[exp_year]=2025 --card[cvc]=123
```

### Cleanup Test Data

```bash
# Delete test subscriptions
stripe subscriptions list --limit 100 --status active | jq -r '.data[].id' | xargs -I {} stripe subscriptions cancel {}

# Delete test customers (cascade deletes subscriptions)
stripe customers list --limit 100 --email test@example.com | jq -r '.data[].id' | xargs -I {} stripe customers delete {}

# Clear test database
npm run db:reset-test
```

## Execution Commands

### Run Stripe Payment Tests

```bash
cd /Users/Asim/Desktop/mawidi_codex/mawidi-site

# Run all Stripe tests
npm run test:e2e -- tests/stripe/

# Run webhook tests only
npm run test:e2e -- tests/stripe/webhook.spec.ts

# Run with Stripe CLI forwarding
stripe listen --forward-to localhost:9000/api/stripe/webhook &
npm run test:e2e -- tests/stripe/
```

### Trigger Stripe Test Events

```bash
# Trigger events for testing
npm run stripe:trigger:payment
npm run stripe:trigger:checkout
npm run stripe:trigger:subscription

# View events
npm run stripe:events

# View logs
npm run stripe:logs
```

### Monitor Stripe Webhook

```bash
# Start webhook listener
npm run stripe:listen

# In separate terminal, trigger events
npm run stripe:trigger:checkout

# Check webhook received
# Listener will show event details in real-time
```

## Success Criteria

### Payment Flow

- [ ] Checkout session creates successfully
- [ ] Payment succeeds with test card (4242...)
- [ ] Declined card handled gracefully (4000...9995)
- [ ] 3D Secure authentication works (4000...3155)
- [ ] Success URL redirect works
- [ ] Cancel URL redirect works

### Webhook Processing

- [ ] All webhook events validated (signature check)
- [ ] checkout.session.completed processed correctly
- [ ] payment_intent.succeeded updates database
- [ ] Subscription events update user status
- [ ] Invoice events recorded
- [ ] Failed payments trigger notifications
- [ ] Idempotency prevents duplicate processing

### Subscription Management

- [ ] Subscription activates on payment
- [ ] Subscription upgrades work
- [ ] Subscription downgrades work
- [ ] Subscription cancellation works
- [ ] Prorated billing calculated correctly
- [ ] Billing cycle aligns with Stripe

### Data Integrity

- [ ] User-subscription relationship correct
- [ ] Payment amounts match pricing tiers
- [ ] Currency conversions accurate
- [ ] Metadata preserved throughout flow
- [ ] Invoice records complete

## Stripe Payment Test Report Template

```markdown
# Stripe Payment Test Report

**Date:** 2024-XX-XX
**Stripe Mode:** Test
**Environment:** staging

## Test Results

- **Total tests:** 32
- **Passed:** 30
- **Failed:** 2
- **Skipped:** 0

## Payment Flow Tests

| Test                      | Status  | Duration |
| ------------------------- | ------- | -------- |
| Create checkout session   | ✅ Pass | 280ms    |
| Successful payment (4242) | ✅ Pass | 3.2s     |
| Declined payment (9995)   | ✅ Pass | 2.8s     |
| 3D Secure flow (3155)     | ⚠️ Fail | 5.1s     |

## Webhook Tests

| Event                         | Status  | Processing Time |
| ----------------------------- | ------- | --------------- |
| checkout.session.completed    | ✅ Pass | 120ms           |
| payment_intent.succeeded      | ✅ Pass | 95ms            |
| customer.subscription.created | ✅ Pass | 140ms           |
| invoice.payment_succeeded     | ✅ Pass | 110ms           |
| payment_intent.payment_failed | ✅ Pass | 85ms            |

## Issues Found

1. **Medium: 3D Secure redirect fails on mobile Safari**
   - Test: 3D Secure authentication flow
   - Impact: iOS users cannot complete payment
   - Fix: Update Stripe.js SDK version

2. **Low: Webhook processing logs contain sensitive data**
   - Test: Webhook event logging
   - Impact: Security compliance
   - Fix: Redact customer email/name in logs

## Subscription Lifecycle

- **Creation:** ✅ Working
- **Upgrade:** ✅ Working
- **Downgrade:** ✅ Working
- **Cancellation:** ✅ Working
- **Reactivation:** ⚠️ Not tested

## Recommendations

1. Fix 3D Secure redirect for mobile
2. Redact sensitive data in webhook logs
3. Add subscription reactivation flow
4. Test annual renewal (requires time manipulation)
5. Add dunning management for failed payments
6. Test plan change proration

## Stripe Configuration Verified

- [x] Webhook secret configured
- [x] Test mode keys active
- [x] Products and prices created
- [x] Webhook endpoint registered
- [x] Test cards working

## Production Readiness

- [ ] Switch to live mode keys
- [ ] Update webhook endpoint to production URL
- [ ] Configure live webhook secret
- [ ] Test with real card (small amount)
- [ ] Set up Stripe Radar (fraud prevention)
- [ ] Configure billing thresholds
```
