> ## Documentation Index
> Fetch the complete documentation index at: https://docs.one.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 enthält eine vollständige Stripe-Billing-Pipeline hinter einem Instance-Access-Modell. Private Deployments lassen es auf **No subscriptions** (Keine Abonnements) und zeigen nie Zahlungs-UI. Betreiber, die einen Katalog möchten, wählen **Included + paid** (Enthalten + bezahlt) oder **Paid only** (Nur bezahlt) und erhalten gehostete Checkout, Customer Portal, Webhook-gesteuerte Abonnement-Lebenszyklen und Kontingentdurchsetzung.

<Note>
  Die Software-Standardeinstellung ist **No subscriptions** (`access_model = off`, `default_token_quota = 0` bedeutet unbegrenzt). Neue Installationen und bestehende Self-Hosts starten dort. Keine Billing-UI wird angezeigt, bis ein Admin eine Stripe-Einstellung wählt.
</Note>

## Was Sie erhalten

* **Drei Zugriffsmodi**: keine Abos, enthalten + kostenpflichtig oder nur kostenpflichtig
* **Stripe-gehosteter Checkout** — Benutzer führen ein Upgrade durch, ohne dass Ihre Code-Kartendaten berührt
* **Kundenportal** — Benutzer aktualisieren Zahlungsmethoden, laden Rechnungen herunter, kündigen — alles auf der Stripe-Benutzeroberfläche
* **Webhook-gesteuerte Lebenszyklen** — Abos werden automatisch bereitgestellt und erneuert; stornierte kostenpflichtige Benutzer kehren am Ende des Zeitraums zur kostenlosen Stufe zurück (oder werden in reinen Bezahlmodellen nicht berechtigt)
* **Kontingentdurchsetzung** — Token-Nutzung pro Zeitraum verfolgt; Unterbrechung während des Streams 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. Melden Sie sich als Admin an
2. **Admin → System Settings → Access and billing**
3. Wählen Sie eine Posture aus (die Instanz befindet sich immer in genau einer):
   * **No subscriptions** (Software-Standard) — Stripe wird nicht verwendet. Token-Zugriff ist das Standard-Monatskontingent (`0` = unbegrenzt).
   * **Included + paid** — Stripe erforderlich. Neue Benutzer landen auf dem Included-Tier; sie können upgraden. Stornierte bezahlte Benutzer kehren am Ende des Abrechnungszeitraums zum Included-Tier zurück.
   * **Paid only** — Stripe erforderlich. Kein Included-Tier. Benutzer müssen sich abonnieren, bevor sie Modelle aufrufen können. Bestehende Benutzer des Included-Tiers behalten ihr Kontingent, wenn Sie von Included + paid wechseln.
4. Das Backend validiert, dass sowohl `STRIPE_SECRET_KEY` als auch `STRIPE_WEBHOOK_SECRET` vorhanden sind, wenn Sie eine Stripe-Posture wählen — wenn einer fehlt, wird 400 zurückgegeben
5. Bei der ersten Aktivierung einer Stripe-Posture führt das Backend ein **idempotentes Setup** durch:
   * Included + paid: erstellt den Included-Plan (`slug=free`) + eine Pro-Vorlage mit einer leeren Price ID; setzt `default_plan_id`; füllt Benutzer ohne Plan auf
   * Paid only: erstellt nur die Pro-Vorlage; führt keine Auffüllung durch
   * Kopiert `default_token_quota` auf den Included-Plan, wenn dieser Plan erstellt wird (`0` bleibt `0`, unbegrenzt)
6. Das Zurückschalten auf **No subscriptions** ist ein **reines Flag-Flip** ohne Datennebenwirkungen. Der Standard-Included-Plan kann nicht gelöscht werden, während Included + paid aktiv ist.

### 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 access_model is freemium or paid_only
3. paid_only + no plan      ── unentitled (stop; do not fall through)
4. system_settings.default_token_quota  ── used when access_model is off
5. unlimited                ── last resort if everything above is unset
```

Step 4 is the instance-wide cap for **No subscriptions**. Under **Paid only**, a user with no plan is unentitled (chat returns 402) even if `default_token_quota` is 0.

### 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 zur Bereitstellung — alle nach der Installation anpassbar, außer wo vermerkt.

| Variable                                           | Standard                                                              | Anpassbar über                                        |
| -------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------- |
| `system_settings.access_model`                     | `off`                                                                 | Admin → System Settings → Access and billing          |
| `billing_plans.monthly_token_quota` (included)     | kopiert von `default_token_quota` (`0` = unbegrenzt)                  | Admin → System Settings → Default Monthly Token Quota |
| `billing_plans.monthly_token_quota` (Pro template) | `5,000,000`                                                           | Admin → Billing → Plans → Pro → Edit                  |
| `system_settings.default_token_quota`              | `0` (unbegrenzt; synchronisiert mit dem included plan unter freemium) | Admin → System Settings → Default Monthly Token Quota |
| `system_settings.billing_enabled`                  | `FALSE` (abgeleitet von `access_model != off`)                        | Admin → System Settings → Access and billing          |
| Pro list price                                     | `$20.00 USD / month`                                                  | Stripe Dashboard (price object)                       |
| Stripe webhook events subscribed                   | 6                                                                     | Stripe Dashboard → Webhooks                           |
| Stripe price cache TTL                             | `5 minutes`                                                           | hardcoded in `stripe_client.py`                       |
| Subscription lifecycle cron                        | stündlich                                                             | APScheduler in `web/main.py`                          |
| Free-tier reset cron                               | stündlich (Kalendermonatsgrenze)                                      | 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>
