> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fim.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Stripe Billing

> Aktivieren Sie Stripe-Abonnements pro Benutzer (Free + Pro) mit Token-Kontingenten und Self-Service-Planverwaltung.

FIM One bietet eine vollständige Stripe-Billing-Pipeline hinter einem von Administratoren kontrollierten Feature-Flag. Private Deployments ohne Zahlungsanforderungen lassen es deaktiviert und sehen die UI nie. SaaS-Betreiber schalten einen Toggle um und erhalten sofort gehostetes Checkout, Customer Portal, Webhook-gesteuerte Abonnement-Lebenszyklen und Kontingent-Durchsetzung.

<Note>
  Billing ist **standardmäßig deaktiviert**. Neue Installationen und bestehende Self-Hosts starten beide mit `system_settings.billing_enabled = FALSE`. Keine Billing-UI wird angezeigt, bis ein Administrator sie explizit aktiviert.
</Note>

## Was Sie erhalten

* **Free + Pro Tiers** mit monatlichen Token-Kontingenten (Standard: Free 1M / Pro 5M; beide nach Aktivierung anpassbar)
* **Stripe-gehosteter Checkout** — Benutzer upgraden, ohne dass Ihre Code-Kartendaten berührt
* **Customer Portal** — Benutzer aktualisieren Zahlungsmethoden, laden Rechnungen herunter, kündigen — alles auf Stripes Benutzeroberfläche
* **Webhook-gesteuerte Lebenszyklen** — Abonnements werden automatisch bereitgestellt und erneuert; gekündigte Abos werden am Ende des Zeitraums auf Free herabgestuft
* **Quota-Durchsetzung** — Token-Nutzung pro Zeitraum verfolgt; Unterbrechung im Datenstrom mit strukturierter Upgrade-Aufforderung
* **Admin-Seiten** für Plan-CRUD und Abonnement-Überwachung

## Voraussetzungen

1. **Stripe-Konto** mit aktiviertem Live-Modus. Unternehmen mit Sitz in Singapur müssen KYC abschließen (Business UEN, Ausweis des Geschäftsführers, Bankkonto). Die Genehmigung dauert normalerweise 1–3 Tage.
2. **Stripe Live API-Schlüssel** vom Typ Restricted (empfohlen gegenüber Standard `sk_live_***` — einfacher zu widerrufen, begrenzte Berechtigungen).
3. **Webhook-Endpunkt** öffentlich erreichbar unter `<your-domain>/api/webhooks/stripe`.
4. **Bankkonto** für Auszahlungen. Multi-Währungs-Abrechnung (z. B. USD-Auszahlung auf ein USD-Konto) wird für Stripe-Konten ohne USD-Standard empfohlen, um 1,5–2 % Devisenverluste pro Transaktion zu vermeiden.

## Einrichtung

### 1. Stripe Dashboard

#### Pro-Produkt erstellen

1. **Catalog → Products → + Add product**
2. Name: `Pro`, description: `5M tokens / month, priority support`
3. Pricing: Recurring, monthly, \$20.00 USD (adjust to your pricing strategy)
4. Save → copy the resulting `price_***` ID (you will UPDATE the local `billing_plans` table with this value after activation)

#### Erstellen Sie einen eingeschränkten API-Schlüssel

1. **Developers → API keys → + Create restricted key**
2. Name: `fim-one production`
3. Permissions (minimum):
   * **Customers**: Write
   * **Subscriptions**: Write
   * **Checkout Sessions**: Write
   * **Customer portal**: Write
   * **Prices**: Read
   * **Products**: Read
4. Save → copy `rk_live_***`

#### Webhook-Endpunkt registrieren

1. **Developers → Webhooks → + Add endpoint**
2. URL: `https://<your-domain>/api/webhooks/stripe`
3. Zu empfangende Events:
   * `checkout.session.completed`
   * `customer.subscription.created`
   * `customer.subscription.updated`
   * `customer.subscription.deleted`
   * `invoice.payment_succeeded`
   * `invoice.payment_failed`
4. Nach dem Speichern auf "Reveal signing secret" klicken → `whsec_***` kopieren

#### Multi-Währungs-Abrechnung konfigurieren (empfohlen)

Wenn sich die Standardwährung Ihres Stripe-Kontos von der Preiswahrung unterscheidet, die Sie berechnen (häufiger Fall: SGD-Konto mit USD-Abrechnung):

1. **Settings → Bank accounts and currencies → Add a settlement currency**
2. Wählen Sie die Preiswahrung (z. B. USD)
3. Fügen Sie das entsprechende Bankkonto an (z. B. ein Aspire USD Virtual Account)
4. Speichern — Stripe leitet USD-Gebühren direkt zu USD-Auszahlungen weiter, ohne Devisenumrechnung

### 2. Backend `.env`

Legen Sie diese drei Schlüssel in Ihrer Produktions-`.env` fest:

```bash theme={null}
STRIPE_SECRET_KEY=rk_live_***
STRIPE_WEBHOOK_SECRET=whsec_***
STRIPE_BILLING_RETURN_URL=https://<your-domain>/settings?tab=billing
```

Siehe [Umgebungsvariablen](/configuration/environment-variables) für die vollständige Referenz.

Starten Sie das Backend nach dem Bearbeiten von `.env` neu, damit die Schlüssel übernommen werden:

```bash theme={null}
./deploy.sh   # or: docker compose restart fim-one
```

### 3. In Admin aktivieren

1. Als Admin anmelden
2. **Admin → System Settings → Billing**
3. **Enable Stripe Billing** einschalten
4. Das Backend validiert, dass sowohl `STRIPE_SECRET_KEY` als auch `STRIPE_WEBHOOK_SECRET` vorhanden sind — wenn einer fehlt, wird 400 zurückgegeben und der Schalter bleibt AUS
5. Nur bei der ersten Aktivierung führt das Backend ein **idempotentes Setup** durch:
   * Erstellt Free + Pro Pläne (übersprungen, falls bereits vorhanden)
   * Setzt `system_settings.default_plan_id` auf die Free Plan ID
   * Füllt `users.plan_id = free.id` für alle Benutzer ohne Plan auf
   * Synchronisiert `default_token_quota` → Free Plan `monthly_token_quota`, sodass das bestehende global von Admin kontrollierte Kontingent erhalten bleibt
6. Nachfolgende Umschaltungen aus/an sind ein **reines Flag-Flip** ohne Datennebenwirkungen

### 4. Aktualisieren Sie den Pro-Plan mit Ihrem Live-Preis

Nach der Aktivierung aktualisieren Sie den vordefinierten Pro-Plan, um auf Ihren Live-Stripe-Preis zu verweisen:

* **Admin → Billing → Plans → Pro → Edit**
* Fügen Sie Ihre `price_1***` (aus Schritt 1) in `Stripe Price ID` ein
* Speichern

Oder über SQL (wenn Sie direkten Datenbankzugriff bevorzugen):

```sql theme={null}
UPDATE billing_plans
SET stripe_price_id = 'price_1***'
WHERE slug = 'pro';
```

### 5. Smoke-Test

1. Öffnen Sie `/settings?tab=billing` als normaler Benutzer
2. Klicken Sie auf **Switch to Pro**
3. Stripe Checkout wird geöffnet; füllen Sie es mit einer echten Karte mit niedrigem Betrag aus (erstatten Sie danach)
4. Webhook sollte ausgelöst werden — überprüfen Sie im Stripe Dashboard → Webhooks → aktuelle Ereignisse zeigen 2xx-Antworten
5. Abonnementzeile erscheint in der Tabelle `subscriptions`; `users.plan_id` wechselt zu `pro`
6. Die Benutzeroberfläche zeigt jetzt Pro-Plan + Schaltfläche „Manage subscription"

## Abrechnung deaktivieren

Schalten Sie den Schalter **Enable Stripe Billing** in Admin → System Settings → Billing AUS.

Wenn deaktiviert:

* Alle `/api/billing/*`-Endpunkte geben 503 zurück
* Der Webhook-Endpunkt gibt 503 zurück (Stripe wird erneut versuchen und dann im Dashboard als fehlgeschlagen angezeigt — das ist in Ordnung, Sie können den Webhook stattdessen im Stripe Dashboard deaktivieren, wenn die Abrechnung dauerhaft ausgeschaltet ist)
* Die Registerkarte **Plan & Billing** für Benutzer verschwindet
* Die Navigationsgruppe Admin → Billing ist ausgeblendet
* Die Quota-Kette überspringt die Plan-Stufe und fällt direkt auf `default_token_quota` zurück

**Daten werden beibehalten**: Vorhandene `subscriptions`-, `billing_plans`- und `users.plan_id`-Zeilen bleiben unverändert. Das erneute Aktivieren wird vom gleichen Status aus fortgesetzt, ohne dass eine Migration erforderlich ist.

## Berechnungsreferenz — Kontingent- und Token-Mathematik

Dies ist die autoritative Referenz für jede numerische Regel, die entscheidet, was ein Benutzer verbrauchen darf, wann sein Zähler zurückgesetzt wird und wie die Auflösungskette zusammengesetzt wird. Lesen Sie dies, bevor Sie Preise ändern, Kontingente anpassen, Nutzungs-Dashboards erstellen oder v2/v3-Arbeiten planen. Zukünftige, aber noch nicht ausgelieferte Regeln sind in ihrem reservierten Slot dokumentiert, damit Mitwirkende wissen, wo neue Logik eingebunden wird.

### Glossar

| Variable                              | Speicher                      | Semantik                                                         | Bereich                            |
| ------------------------------------- | ----------------------------- | ---------------------------------------------------------------- | ---------------------------------- |
| `users.token_quota`                   | pro Benutzer (Überschreibung) | Dreistellige Überschreibung; siehe Semantik unten                | `NULL`, `0` oder positive Ganzzahl |
| `users.tokens_used_this_period`       | pro Benutzer (Zähler)         | Kumulierte Tokens seit letztem Zurücksetzen                      | nicht-negative Ganzzahl            |
| `users.quota_reset_at`                | pro Benutzer (Anker)          | Spiegelt `Subscription.current_period_end` für bezahlte Benutzer | Zeitstempel                        |
| `users.plan_id`                       | pro Benutzer (FK)             | Aktiver Plan                                                     | FK `billing_plans.id`              |
| `billing_plans.monthly_token_quota`   | pro Plan                      | Obergrenze für Benutzer dieses Plans                             | nicht-negative Ganzzahl            |
| `system_settings.default_token_quota` | Singleton                     | Defensive Fallback, wenn kein Plan zutrifft                      | nicht-negative Ganzzahl            |
| `system_settings.default_plan_id`     | Singleton                     | Kostenlos-Plan-Zeiger für neue/nicht zugewiesene Benutzer        | FK oder `NULL`                     |
| `system_settings.billing_enabled`     | Singleton                     | Master-Schalter — steuert Schritt 2 der Kette                    | Boolescher Wert                    |

### Was als Token zählt

Die Token-Nutzung wird auf der LLM-Aufrufsebene erfasst, basierend auf dem `usage`-Objekt von LiteLLM bei jedem Completion.

* **Gezählt**: Prompt-Tokens + Completion-Tokens bei jedem Modellaufruf
* **Gezählt**: jeder Roundtrip in einem mehrstufigen / Tool-Use-Agent-Flow (jeder Modellaufruf ist eine separate Belastung)
* **Gezählt**: Embedding-Anfragen (KB-Ingestion, Abruf-Scoring)
* **Nicht gezählt**: eingegebene Daten, die nie an ein Modell gesendet werden (z. B. hochgeladene Dateien, die der Benutzer verwirft)
* **Nicht gezählt**: Anfragen, die fehlschlagen, bevor sie den Provider erreichen (Authentifizierungsfehler, Rate-Limit-Vorabprüfung)
* **Zwischengespeicherte Eingabe**: in v1 zum vollen Preis gezählt (kein Provider-Cache-Rabatt wird angezeigt). v2 kann zwischengespeicherte Prompt-Tokens möglicherweise separat gutschreiben.

### Three-state override semantics

`users.token_quota` ist die administrative Überschreibung pro Benutzer. Sie hat drei Bedeutungen in einer Spalte:

| Value   | Meaning                           | Use case                                                                                  |
| ------- | --------------------------------- | ----------------------------------------------------------------------------------------- |
| `NULL`  | Not set — defer to plan / default | Default state for all normal users                                                        |
| `0`     | Unlimited                         | Admin / internal accounts; "VIP gift"                                                     |
| `N > 0` | Hard cap at `N`                   | Block an abuser without canceling their paid subscription; pre-paid enterprise allocation |

Die Überschreibung hat immer Vorrang vor Plan und Standard. Sie existiert, damit Admins einzelne Benutzer über oder unter ihrem Plan-Tier positionieren können, ohne Stripe zu berühren.

### Quota resolution chain — v1 (current)

For any authenticated request, the cap is computed top-down — **first match wins**:

```
1. users.token_quota        ── NULL? skip. 0? unlimited. N>0? cap at N.
2. users.plan.monthly_token_quota   ── only when billing_enabled = TRUE
3. system_settings.default_token_quota  ── defensive fallback
4. unlimited                ── last resort if everything above is NULL
```

Step 3 is rarely reached when billing is on, because activation backfills `users.plan_id` for every user. It exists as a defense-in-depth so a misconfigured plan never silently uncaps a user.

### Periodische Zurückstellung

* Für bezahlte Benutzer spiegelt `quota_reset_at` `Subscription.current_period_end` wider. Der `invoice.payment_succeeded` Webhook-Handler setzt `tokens_used_this_period = 0` und verschiebt `quota_reset_at` bei jeder erfolgreichen Verlängerung auf das neue Periodenende.
* Für kostenlose Benutzer (kein Stripe-Abonnement) setzt ein stündlicher Cron `tokens_used_this_period` auf 0 an einer Kalendermonatsgrenze, die am Plan-Zuweisungsdatum verankert ist.
* Planänderungen in der Mitte einer Periode setzen den Zähler **nicht** zurück — nur Verlängerungen tun dies. Dies verhindert Quota-Cycling-Exploits („abonnieren → Pro-Quota nutzen → kündigen → erneut abonnieren").

### Mid-stream enforcement

* Pre-flight check at chat-call entry: cheapest path, blocks requests the user can't afford to start.
* During streaming, the running token count is re-evaluated on every chunk. Crossing the cap closes the stream with a **structured terminator frame**, not a network error.
* The frontend interprets the terminator and surfaces `<QuotaExceededDialog>` with a deep link to `/settings?tab=billing`.
* Non-streaming responses return HTTP `402` with body `{ code: "QUOTA_EXCEEDED", reset_at, upgrade_url }`.

### Billing-deaktiviert-Fallback

Wenn `system_settings.billing_enabled = FALSE`:

* Schritt 2 der Kette wird übersprungen — die Kette reduziert sich auf `override → default → unlimited`.
* `/api/billing/*` und `/api/webhooks/stripe` geben `503` zurück.
* Die Registerkarte `Plan & Billing` für Benutzer und die Admin → Billing-Navigationsgruppe sind ausgeblendet.
* Alle Abrechnungsdaten (Abonnements, Pläne, `users.plan_id`) bleiben erhalten — das erneute Aktivieren setzt den gleichen Status fort, ohne Migration erforderlich zu sein.

### Reserviert: quota chain v2 — Team-Plätze

<Note>Noch nicht ausgeliefert. Hier dokumentiert, damit die v2-Arbeit einen bekannten Landeplatz hat.</Note>

Wenn der Team-Plan ausgeliefert wird:

* `Subscription.quantity` führt die Platzanzahl (Stripe-nativ).
* Der effektive Plan eines Benutzers wird durch Team-Mitgliedschaft aufgelöst, bevor auf seinen persönlichen Plan zurückgegriffen wird:
  ```
  effective_plan = team.plan if team_member(user) else user.plan
  ```
* Quota ist **pro Platz** (jedes Team-Mitglied erhält ein vollständiges `monthly_token_quota`), nicht ein gemeinsamer Pool. Gemeinsame Pools führen zu First-Come-First-Served-Erschöpfung und sind kundenfeindlich.
* Override-Semantik bleibt unverändert — Team-Administratoren können einzelne Mitglieder weiterhin hart begrenzen über `users.token_quota = N`, was in der Kette über dem Team-Plan liegt.

### Reserved: quota chain v3 — native Org allocation (no Stripe)

<Note>Noch nicht veröffentlicht. Reserviert für On-Prem- / Enterprise-Deployments, die Kontingente intern zuweisen, ohne pro Benutzer über Stripe zu bezahlen.</Note>

* Neue Tabelle `org_quota_allocations(user_id, monthly_token_quota, org_id)` verteilt ein übergeordnetes Budget auf Mitglieder.
* Zuweisungen sind **pro Benutzer, kein gemeinsamer Pool** — jedes Mitglied hat ein klares individuelles SLA.
* Aktualisierte Chain:
  ```
  override → max(plan_quota, org_allocation) → default → unlimited
  ```
* `max()`, nicht `sum()`. Ein bezahlter Pro-Benutzer erhält nie weniger als das, wofür er bezahlt hat, auch wenn sein Org-Administrator eine niedrige Zuweisung festlegt. Das über Stripe bezahlte Kontingent ist unverletzlich.

### Reserviert: Pay-per-use-Gutschriftsaldo (v3 separate Dimension)

<Note>Noch nicht ausgeliefert. Eine separate Achse vom obigen Schema — Gutschriften sind eine einmalige Aufstockung, keine Abonnement-Stufe.</Note>

* Neue Tabelle `user_credits(user_id, balance_cents, currency)` — finanziert über Stripe Checkout `mode='payment'`.
* Verbrauchsreihenfolge: **Abonnement-Kontingent zuerst**, dann Gutschriftsaldo (nur nach Erschöpfung des Abonnements beginnt die Gutschrift zu sinken).
* Gutschriftsaldo ist nicht erstattbar (Industriestandard für Prepaid).
* UI zeigt beide Balken: `Subscription quota: 4.2M / 5M used` + `Credits: $7.40 remaining`.

### Standardwerte

Standardwerte bei der Bereitstellung — alle nach der Installation anpassbar, außer wo angegeben.

| Variable                                   | Standard                                              | Anpassbar über                        |
| ------------------------------------------ | ----------------------------------------------------- | ------------------------------------- |
| `billing_plans.monthly_token_quota` (Free) | `1,000,000`                                           | Admin → Billing → Plans → Free → Edit |
| `billing_plans.monthly_token_quota` (Pro)  | `5,000,000`                                           | Admin → Billing → Plans → Pro → Edit  |
| `system_settings.default_token_quota`      | `1,000,000` (synchronisiert mit Free bei Aktivierung) | Admin → System Settings → Quotas      |
| `system_settings.billing_enabled`          | `FALSE`                                               | Admin → System Settings → Billing     |
| Pro-Listenpreis                            | `$20.00 USD / Monat`                                  | Stripe Dashboard (price object)       |
| Stripe-Webhook-Events abonniert            | 6                                                     | Stripe Dashboard → Webhooks           |
| Stripe-Preis-Cache-TTL                     | `5 Minuten`                                           | hardcoded in `stripe_client.py`       |
| Subscription-Lifecycle-Cron                | stündlich                                             | APScheduler in `web/main.py`          |
| Free-Tier-Reset-Cron                       | stündlich (Kalendermonats-Grenze)                     | APScheduler in `web/main.py`          |

## Preismodell

V1 ist ein pauschales Abonnement. Free + Pro, monatliche Abrechnung, nur USD.

**Nicht im Umfang von v1** (absichtlich, auf die Roadmap verschoben):

* Team-Plan (Stripe-Plätze / `subscription.quantity`)
* Jährliche Abrechnung
* Multi-Währungs-Präsentation
* Gutscheine / Promo-Codes
* Steuerbehandlung (Stripe Tax-Integration — erfordert separate Compliance-Überprüfung)
* Nutzungsbasierte Messung / Überschussgebühren
* Pay-per-Use-Guthaben (einmalige Aufstockung)

Siehe die [Roadmap](/roadmap) für die nächsten geplanten Schritte.

## Fehlerbehebung

<Accordion title="Toggle wird nicht aktiviert — 400-Fehler">
  Der Aktivierungsendpunkt erfordert, dass sowohl `STRIPE_SECRET_KEY` als auch `STRIPE_WEBHOOK_SECRET` gesetzt sind. Bestätigen Sie, dass sie in `.env` vorhanden sind und dass das Backend nach der Bearbeitung neu gestartet wurde.
</Accordion>

<Accordion title="Webhook gibt 503 zurück">
  Entweder ist die Abrechnung deaktiviert (Toggle ist AUS), oder die Anfragensignaturverifizierung ist fehlgeschlagen (nicht übereinstimmender `STRIPE_WEBHOOK_SECRET`). Überprüfen Sie Stripe Dashboard → Webhooks → aktuelle Ereignisse auf den tatsächlichen Fehlertext.
</Accordion>

<Accordion title="Benutzer abonniert, sieht aber immer noch kostenlosen Plan">
  Der `checkout.session.completed`-Webhook hat Ihr Backend nicht erreicht. Überprüfen Sie, dass die Endpunkt-URL im Stripe Dashboard genau mit `<your-domain>/api/webhooks/stripe` übereinstimmt, einschließlich des nachfolgenden Pfads. Überprüfen Sie die letzten Webhook-Zustellungen auf Fehler.
</Accordion>

<Accordion title="Pro-Benutzer sieht die Test-Modus-Preis-ID">
  Die Seed-Migration schreibt eine Test-Modus-Preis-ID. Nach der Aktivierung der Produktionsabrechnung aktualisieren Sie den Pro-Plan, um Ihre Live-`price_1***` über Admin → Abrechnung → Pläne → Pro → Bearbeiten oder über direktes SQL UPDATE zu verwenden.
</Accordion>

<Accordion title="Quittungen sind mit Stripe-Standardeinstellungen gekennzeichnet">
  Konfigurieren Sie Ihr Geschäfts-Branding im Stripe Dashboard → Einstellungen → Branding. Fügen Sie Ihr Logo, Ihren Geschäftsnamen (z. B. „FIM Labs Pte. Ltd.") und Ihre Adresse hinzu. Stripe wendet diese auf alle automatisch generierten Quittungen und Rechnungen an.
</Accordion>

<Accordion title="Devisenverluste bei Auszahlungen">
  Wenn sich die Standardwährung Ihres Stripe-Kontos von Ihrer Abrechnungswährung unterscheidet, konvertiert Stripe bei jeder Auszahlung (1,5–2% Spread). Fügen Sie eine entsprechende Abrechnungswährung unter Einstellungen → Bankkonten und Währungen hinzu, verknüpfen Sie ein Bankkonto mit derselben Währung, und Stripe leitet Zahlungen mit derselben Währung ohne Umrechnung weiter.
</Accordion>
