# Stripe Integration

> How Stripe powers Aerostack billing — plan subscriptions, wallet deposits, and marketplace creator payouts. Configure webhooks for custom setups.

Aerostack uses Stripe for all payment processing: platform plan subscriptions, wallet deposits, and marketplace creator payouts. This page explains how Stripe integrates with your Aerostack account and how to configure webhooks for self-hosted or custom setups.

---

## How Stripe is Used

| Feature | Stripe Product | Description |
|---------|---------------|-------------|
| Platform plans | Subscriptions | Monthly/yearly recurring billing for plan tiers |
| Wallet deposits | Checkout Sessions | One-time payments to fund the prepaid wallet |
| Creator payouts | Connect | Transfer marketplace earnings to creator bank accounts |
| Customer portal | Customer Portal | Self-service billing management (invoices, payment methods) |

---

## Platform Plan Checkout

When a user upgrades their plan, Aerostack creates a Stripe Checkout Session and redirects them to complete payment.

### Flow

```
1. User clicks "Upgrade to Pro" in dashboard
2. Frontend calls POST /api/billing/checkout
   Body: { tier: "pro", billing_interval: "yearly", success_url, cancel_url }
3. API creates Stripe Checkout Session with the correct Price ID
4. API returns { url: "https://checkout.stripe.com/..." }
5. Frontend redirects user to Stripe Checkout
6. User completes payment on Stripe
7. Stripe redirects to success_url with session_id
8. Frontend calls GET /api/billing/verify-session?session_id=...
9. API verifies payment and upgrades the plan immediately
10. Stripe also sends webhook for redundancy
```

### Billing Intervals

Each paid plan has two Price IDs in Stripe -- one for monthly and one for yearly:

```bash
# Monthly checkout
curl -X POST https://api.aerostack.dev/api/billing/checkout \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "pro",
    "billing_interval": "monthly",
    "success_url": "https://app.aerostack.dev/settings/billing?success=true",
    "cancel_url": "https://app.aerostack.dev/settings/billing"
  }'

# Yearly checkout (25% discount)
curl -X POST https://api.aerostack.dev/api/billing/checkout \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "pro",
    "billing_interval": "yearly",
    "success_url": "https://app.aerostack.dev/settings/billing?success=true",
    "cancel_url": "https://app.aerostack.dev/settings/billing"
  }'
```

Valid tiers: `starter`, `pro`, `business`. The `free` tier does not require Stripe checkout.

---

## Webhook Configuration

Stripe webhooks keep Aerostack in sync with subscription changes that happen outside the API (e.g. failed payments, subscription renewals, cancellations from the Stripe dashboard).

### Endpoint

```
POST https://api.aerostack.dev/api/billing/webhook
```

This endpoint:
- Requires no authentication (it is public)
- Validates the `stripe-signature` header against your webhook signing secret
- Processes the event and updates the account plan accordingly

### Setting Up Webhooks in Stripe

1. Go to [Stripe Dashboard > Developers > Webhooks](https://dashboard.stripe.com/webhooks)
2. Click **Add endpoint**
3. Enter the URL: `https://api.aerostack.dev/api/billing/webhook`
4. Select these events:
   - `checkout.session.completed`
   - `customer.subscription.updated`
   - `customer.subscription.deleted`
   - `invoice.payment_succeeded`
   - `invoice.payment_failed`
5. Click **Add endpoint**
6. Copy the **Signing secret** (`whsec_...`) and configure it in your Aerostack deployment

  **Use Snapshot payload, not Thin.** When configuring the webhook in Stripe, ensure the payload type is set to "Snapshot" (default). Thin payloads require an additional API call to retrieve the full event, which is not supported.

---

## Customer Portal

The Stripe Customer Portal lets users manage their subscription without leaving the Aerostack app:

- View and download invoices
- Update payment methods
- Switch billing intervals (monthly/yearly)
- Cancel subscription

### Get Portal URL

```bash
curl "https://api.aerostack.dev/api/billing/portal?return_url=https://app.aerostack.dev/settings/billing" \
  -H "Authorization: Bearer YOUR_JWT"
```

**Response:**

```json
{
  "url": "https://billing.stripe.com/p/session/..."
}
```

Redirect the user to this URL. After they finish managing their billing, Stripe redirects them back to the `return_url`.

  The Stripe Customer Portal must be configured in your Stripe Dashboard. Go to **Settings > Customer portal** and enable the features you want (invoice history, payment method updates, subscription cancellation, etc.).

---

## Wallet Deposits via Stripe

Wallet deposits use Stripe Checkout Sessions in "payment" mode (one-time payment, not subscription).

### Flow

```
1. User clicks "Add Funds" in dashboard
2. Frontend calls POST /api/balance/deposit
   Body: { amount_cents: 2500, success_url, cancel_url }
3. API creates Stripe Checkout Session (mode: "payment")
4. API returns { url: "https://checkout.stripe.com/..." }
5. User completes payment
6. Stripe redirects to success_url with session_id
7. Frontend calls GET /api/balance/verify?session_id=...
8. API verifies payment and credits the wallet
```

### Deposit Limits

| Constraint | Value |
|-----------|-------|
| Minimum deposit | $5.00 (500 cents) |
| Maximum deposit | $500.00 (50,000 cents) |

---

## Stripe Connect for Creators

Marketplace creators earn revenue when consumers use their paid tools, functions, and agents. Payouts are processed via Stripe Connect.

### How it Works

1. **Creator publishes** a paid tool to the marketplace
2. **Consumer calls** the tool, and wallet is debited
3. **Marketplace fee** is retained by Aerostack
4. **Remainder credited** to the creator's developer balance
5. **Payout** transferred to the creator's bank via Stripe Connect

### Onboarding

Before receiving payouts, creators must complete Stripe Connect onboarding:

1. Navigate to **Settings > Payouts** in the Aerostack Dashboard
2. Click **Connect with Stripe**
3. Complete Stripe's identity verification and banking setup
4. Once verified, payouts are enabled

### Payout Configuration

| Setting | Options |
|---------|---------|
| Payout frequency | Manual, Weekly, Monthly |
| Minimum payout | $10.00 |
| Currency | USD |

---

## Verify Session After Redirect

After any Stripe checkout redirect (plan upgrade or wallet deposit), verify the session to ensure the payment was processed:

### Plan Upgrade Verification

```bash
curl "https://api.aerostack.dev/api/billing/verify-session?session_id=cs_live_..." \
  -H "Authorization: Bearer YOUR_JWT"
```

**Response:**

```json
{
  "tier": "pro"
}
```

### Wallet Deposit Verification

```bash
curl "https://api.aerostack.dev/api/balance/verify?session_id=cs_live_..." \
  -H "Authorization: Bearer YOUR_JWT"
```

**Response:**

```json
{
  "deposited": true,
  "amount_cents": 2500
}
```

  Session verification is idempotent. Calling it multiple times with the same `session_id` will not duplicate the plan upgrade or wallet deposit.

---

## Redirect URL Security

All Stripe redirect URLs (`success_url`, `cancel_url`, `return_url`) are validated against an allowlist:

| Allowed Origin | Purpose |
|----------------|---------|
| `https://app.aerostack.dev` | Production dashboard |
| `https://aerostack-admin.pages.dev` | Cloudflare Pages deploys |
| `https://*.aerostack-admin.pages.dev` | Preview deploys |
| `http://localhost:5173` | Local development |

Requests with redirect URLs on other origins are rejected with a `400` error. This prevents open redirect attacks.

---

## Testing with Stripe Test Mode

During development, use Stripe test mode keys (`sk_test_...`) instead of live keys. Test mode:

- Uses test credit card numbers (e.g. `4242 4242 4242 4242`)
- Does not charge real money
- Sends webhooks to your test endpoint
- Creates test subscriptions that behave like real ones

  Never use live Stripe keys (`sk_live_...`) in development. Always use test keys locally and configure live keys only in production.

---

## Troubleshooting

### Webhook Not Receiving Events

1. Verify the webhook URL is correct: `https://api.aerostack.dev/api/billing/webhook`
2. Check the webhook signing secret is set: `aerostack secrets list`
3. In Stripe Dashboard > Webhooks, check the event delivery logs for errors
4. Ensure payload type is "Snapshot" (not "Thin")

### Plan Not Updating After Checkout

1. Call `GET /api/billing/verify-session?session_id=...` to force verification
2. Check Stripe Dashboard for the checkout session status
3. If the webhook failed, the verify endpoint handles plan updates as a fallback

### Wallet Balance Not Credited

1. Call `GET /api/balance/verify?session_id=...` to force verification
2. Ensure the checkout session completed successfully in Stripe
3. Check for duplicate verification (the endpoint is idempotent)
