# Stripe Integration {#stripe-integration}



## Overview {#overview}


The **backend** service handles all Stripe integration including customer management, subscription plans, checkout, invoicing, and webhook processing.


## Customer Creation {#customer-creation}


```python
# backend/api/infrastructure/externals/stripe_resolver.py
async def create_stripe_customer(email: str, name: str, tenant_id: str) -> str:
    customer = await stripe.Customer.create_async(
        email=email,
        name=name,
        metadata={"tenant_id": tenant_id}
    )
    return customer.id
```


## Subscription Management {#subscription-management}



### Trial Subscription {#trial-subscription}


```python
async def create_trial_subscription(tenant_id: str, price_id: str, customer_id: str):
    subscription = await stripe.Subscription.create_async(
        customer=customer_id,
        items=[{"price": price_id}],
        payment_behavior="default_incomplete",
        metadata={"tenant_id": tenant_id}
    )
    # Updates tenant with subscription_id, is_trial=True, trial_end_at
```


### Checkout Session {#checkout-session}


```bash
curl -X POST http://localhost:8000/api/v1/checkout/tenant/subscription \
  -H "Authorization: Bearer <token>" \
  -d '{"price_id": "price_abc123"}'
```


## Webhook Handling {#webhook-handling}


Stripe sends webhooks to `POST /api/v1/stripe/webhook`. The service handles:

| Event | Action |
|-------|--------|
| `checkout.session.completed` | Activate subscription |
| `customer.subscription.updated` | Sync subscription status |
| `customer.subscription.deleted` | Downgrade to free plan |
| `customer.subscription.trial_will_end` | Send trial-ending notification |
| `invoice.payment_succeeded` | Verify active status |
| `invoice.payment_failed` | Handle payment failure |

```python
@router.post("/stripe/webhook")
async def handle_stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")
    event = stripe.Webhook.construct_event(payload, sig_header, WEBHOOK_SECRET)
    # Route to handler based on event.type
```


## Dormant Tenant Detection {#dormant-tenant-detection}


When a trial expires without payment:

1. **Celery task** `check_expired_trials` runs hourly
2. **Stripe webhook** `customer.subscription.updated` with `status=incomplete_expired`
3. Tenant set to `is_dormant=True`, `is_trial=False`

Reactivation options:
- **Add payment method**: Stripe SetupIntent flow
- **Downgrade to free**: `POST /api/v1/billing/subscription/downgrade-to-free`


## Test Mode {#test-mode}


For local development, use Stripe test keys:

```bash
STRIPE_API_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_test_...
```

Use the `fake-smtp` container for email testing (Mailhog at port 1083).
