Files
aza/AzA march 2026/deploy/STRIPE_LIVE_SETUP.md
2026-03-25 22:03:39 +01:00

248 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AZA Stripe Live-Setup — Vollständige Anleitung
Stand: 14. März 2026
---
## 1. Ist-Analyse: Was existiert im Code
### Zwei Stripe-Integrationen (parallel)
| Datei | Env-Prefix | Methode | Status |
|---|---|---|---|
| `stripe_routes.py` | `STRIPE_*` (kein Prefix) | **Lookup-Keys** (`aza_basic_monthly` etc.) | **Primär / Aktiv** |
| `license_server.py` | `AZA_STRIPE_*` | Direkte Price-IDs | Legacy / Parallel |
**Wichtig:** `stripe_routes.py` ist der aktive Pfad. Die Landing-Page (`web/index.html`)
ruft `/stripe/create_checkout_session` auf und übergibt einen `lookup_key`.
### Erwartete Lookup-Keys im Code
| Lookup-Key | Plan | Policy |
|---|---|---|
| `aza_basic_monthly` | Basic (1 User, 2 Geräte) | Checkout auf Landing-Page |
| `aza_basic_yearly` | Basic (1 User, 2 Geräte) | Noch nicht auf Landing-Page |
| `aza_team_monthly` | Team (3 User, 2 Geräte/User) | Checkout auf Landing-Page |
| `aza_team_yearly` | Team (3 User, 2 Geräte/User) | Noch nicht auf Landing-Page |
### Aktuelle Preise auf der Landing-Page (`web/index.html`)
| Plan | Preis | Lookup-Key |
|---|---|---|
| AZA Praxis (empfohlen) | CHF 89 / Monat | `aza_basic_monthly` |
| AZA Team | CHF 199 / Monat | `aza_team_monthly` |
### Fehlend auf der Landing-Page
- **Jahreslizenz** (17 % günstiger) ist NICHT dargestellt
- Nur monatliche Preise sind sichtbar
- Kein Umschalter Monatlich/Jährlich
---
## 2. Env-Variablen-Diskrepanz (KRITISCH)
### `stripe_routes.py` liest:
```
STRIPE_SECRET_KEY ← deploy/.env setzt diese ✓
STRIPE_WEBHOOK_SECRET ← deploy/.env setzt diese ✓
STRIPE_SUCCESS_URL ← deploy/.env setzt diese ✓
STRIPE_CANCEL_URL ← deploy/.env setzt diese ✓
STRIPE_PORTAL_RETURN_URL ← deploy/.env setzt diese ✓
```
### `license_server.py` liest:
```
AZA_STRIPE_SECRET_KEY ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_WEBHOOK_SECRET ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_PRICE_BASIC ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_PRICE_TEAM ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_PRICE_BASIC_YEARLY ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_PRICE_TEAM_YEARLY ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_SUCCESS_URL ← deploy/.env setzt diese NICHT ✗
AZA_STRIPE_CANCEL_URL ← deploy/.env setzt diese NICHT ✗
```
### Zusätzlich in `license_server.py` (Zeile 629, 867):
```python
webhook_secret = os.getenv("STRIPE_WEBHOOK_SECRET") # ohne AZA_
secret_key = os.getenv("STRIPE_SECRET_KEY") or os.getenv("STRIPE_API_KEY") # ohne AZA_
```
→ Inkonsistenz innerhalb derselben Datei.
### Konsequenz
- `stripe_routes.py` funktioniert mit `deploy/.env`**OK**
- `license_server.py` Checkout-Endpoint bekommt leere Price-IDs → **Broken**
- Aber: Landing-Page nutzt `/stripe/create_checkout_session` (= `stripe_routes.py`) → **OK**
- Der `license_server.py` Checkout ist ein Parallel-Pfad, der aktuell nicht aufgerufen wird
### Empfehlung
Für Go-Live: `stripe_routes.py` ist der korrekte Pfad. Kein Umbau nötig.
Die `license_server.py` Env-Vars mit `AZA_*` Prefix können später bereinigt werden.
---
## 3. Was im Stripe-Dashboard angelegt werden muss
### Produkte und Preise
Gehe zu: **Stripe Dashboard → Produkte → + Produkt hinzufügen**
#### Produkt 1: AZA Desktop Praxis
| Feld | Wert |
|---|---|
| Name | AZA Desktop Praxis |
| Beschreibung | Medizinische KI-Software für Einzelpraxen. Alle 6 Module. |
Preise für dieses Produkt:
| Preis | Intervall | Lookup-Key (WICHTIG!) |
|---|---|---|
| CHF 89.00 | Monatlich | `aza_basic_monthly` |
| CHF 886.00 | Jährlich | `aza_basic_yearly` |
#### Produkt 2: AZA Desktop Team (optional, für später)
| Feld | Wert |
|---|---|
| Name | AZA Desktop Team |
| Beschreibung | Für Gemeinschaftspraxen. Bis zu 3 Benutzer. |
Preise für dieses Produkt:
| Preis | Intervall | Lookup-Key (WICHTIG!) |
|---|---|---|
| CHF 199.00 | Monatlich | `aza_team_monthly` |
| CHF 1'982.00 | Jährlich | `aza_team_yearly` |
**KRITISCH:** Beim Erstellen jedes Preises den **Lookup-Key** setzen!
In Stripe: Preis erstellen → Erweiterte Optionen → Lookup Key → exakt den Key eingeben.
---
## 4. Webhook-Setup
### Webhook-Endpunkt
| Feld | Wert |
|---|---|
| URL | `https://app.aza-medwork.ch/stripe/webhook` |
| Oder (falls auf Hostpoint) | `https://aza-medwork.ch/stripe/webhook` |
**Hinweis:** Der Webhook-Endpunkt muss auf dem Server laufen, auf dem `stripe_routes.py`
läuft. Aktuell ist das der **Hetzner-Server** (Docker/Caddy). Erst relevant, wenn
der Backend-Server auf Hetzner live ist.
Für den Start über WooCommerce/Hostpoint: WooCommerce Stripe Plugin hat
seinen eigenen Webhook — der wird automatisch konfiguriert.
### Benötigte Webhook-Events
| Event | Zweck |
|---|---|
| `checkout.session.completed` | Neue Subscription registrieren, Lizenz anlegen |
| `customer.subscription.updated` | Statusänderungen (Verlängerung, Pause, Planwechsel) |
| `customer.subscription.deleted` | Kündigung, Lizenz deaktivieren |
### Webhook-Secret
Nach dem Erstellen des Webhooks zeigt Stripe ein `whsec_...` Secret an.
Dieses muss in `deploy/.env` als `STRIPE_WEBHOOK_SECRET` eingetragen werden.
---
## 5. Success/Cancel-Flow — Prüfung
### Success-Flow ✓
- URL: `https://aza-medwork.ch/billing/success?session_id={CHECKOUT_SESSION_ID}`
- Endpunkt: `backend_main.py``@app.get("/billing/success")`
- Liest `session_id` aus Query-Parameter
- Ruft `stripe.checkout.Session.retrieve(session_id)` auf
- Zeigt Bestätigungsseite mit Download-Link und Anleitung
- **Funktioniert korrekt** (lokal verifiziert)
### Cancel-Flow ✓
- URL: `https://aza-medwork.ch/billing/cancel`
- Endpunkt: `backend_main.py``@app.get("/billing/cancel")`
- Zeigt Info-Seite "Checkout abgebrochen" mit Link zurück
- **Funktioniert korrekt** (lokal verifiziert)
### Portal-Return ✓
- URL: `https://aza-medwork.ch/`
- Für Stripe Billing Portal (Abo verwalten, kündigen)
---
## 6. Benötigte Live-Secrets für deploy/.env
```bash
# 1. Stripe Secret Key (Dashboard → Developers → API keys → Secret key)
STRIPE_SECRET_KEY=sk_live_XXXXXXXXXXXXXXXXXXXXXX
# 2. Stripe Webhook Secret (Dashboard → Developers → Webhooks → Signing secret)
STRIPE_WEBHOOK_SECRET=whsec_XXXXXXXXXXXXXXXXXXXXXX
# 3. API-Token für Backend-Zugriff (selbst generieren, z.B. openssl rand -hex 32)
MEDWORK_API_TOKENS=<TOKEN_1>,<TOKEN_2>
```
Die übrigen Werte in `deploy/.env` sind bereits korrekt gesetzt:
- `STRIPE_SUCCESS_URL`
- `STRIPE_CANCEL_URL`
- `STRIPE_PORTAL_RETURN_URL`
- `AZA_DOMAIN`
- `ACME_EMAIL`
---
## 7. Offene Punkte / Landing-Page-Update nötig
### web/index.html muss aktualisiert werden:
1. **Jahreslizenz hinzufügen**: Monatlich/Jährlich-Umschalter mit 17 % Ersparnis
2. **Praxis-Monatsplan**: CHF 89 → `aza_basic_monthly` (bereits vorhanden ✓)
3. **Praxis-Jahresplan**: CHF 886 → `aza_basic_yearly` (fehlt ✗)
4. **Team-Plan**: Entscheiden, ob für v1.0 angeboten oder ausblenden
---
## 8. Zwei Wege zum Verkauf (Entscheidung nötig)
### Weg A: WooCommerce Subscriptions (Hostpoint)
- Alles auf der bestehenden WordPress-Seite
- WooCommerce Subscriptions Plugin nötig (kostenpflichtig, ca. $200/Jahr)
- WooCommerce Stripe Gateway macht Webhooks automatisch
- Kein eigener Backend-Server nötig für den Verkauf
- Download-Auslieferung über WooCommerce
- **Vorteil:** Kein Hetzner-Deploy nötig für den Start
### Weg B: Eigener Stripe-Flow (Hetzner)
- Landing-Page + `stripe_routes.py` + Backend auf Hetzner
- Voller Kontrolle über den Checkout-Flow
- Webhook an eigenen Server
- **Nachteil:** Hetzner muss erst deployed werden
### Empfehlung für sofortigen Start
**Weg A (WooCommerce)** für den Verkauf. Weg B später für Browser-AZA.
---
## Zusammenfassung
### Vorhanden ✓
- Stripe-Integration im Code komplett (`stripe_routes.py`)
- Checkout-Session-Erstellung mit Lookup-Keys
- Webhook-Handler für 3 Events (checkout.completed, subscription.updated/deleted)
- Idempotente Event-Verarbeitung (Deduplizierung)
- Lizenz-Datenbank (SQLite) mit Upsert-Logik
- Success/Cancel-Seiten implementiert
- Billing-Portal-Integration
- Env-Variablen-Template (`deploy/.env.example`)
### Fehlt ✗
- **Live Stripe-Produkte + Preise** im Stripe-Dashboard (mit korrekten Lookup-Keys)
- **Live Stripe Secret Key** in `deploy/.env`
- **Live Webhook Secret** in `deploy/.env`
- **Jahreslizenz auf Landing-Page** (nur monatlich dargestellt)
- **Entscheidung: WooCommerce vs. eigener Flow** für den sofortigen Start
- **Hetzner-Deploy** (nur nötig für Weg B)