A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
297 lines
7.6 KiB
Markdown
297 lines
7.6 KiB
Markdown
# Billing Service
|
|
|
|
Go-Microservice fuer Stripe-basiertes Subscription Management mit Task-basierter Abrechnung.
|
|
|
|
## Uebersicht
|
|
|
|
Der Billing Service verwaltet:
|
|
- Subscription Lifecycle (Trial, Active, Canceled)
|
|
- Task-basierte Kontingentierung (1 Task = 1 Einheit)
|
|
- Carryover-Logik (Tasks sammeln sich bis zu 5 Monate an)
|
|
- Stripe Integration (Checkout, Webhooks, Portal)
|
|
- Feature Gating und Entitlements
|
|
|
|
## Quick Start
|
|
|
|
### Voraussetzungen
|
|
|
|
- Go 1.21+
|
|
- PostgreSQL 14+
|
|
- Docker (optional)
|
|
|
|
### Lokale Entwicklung
|
|
|
|
```bash
|
|
# 1. Dependencies installieren
|
|
go mod download
|
|
|
|
# 2. Umgebungsvariablen setzen
|
|
export DATABASE_URL="postgres://user:pass@localhost:5432/breakpilot?sslmode=disable"
|
|
export JWT_SECRET="your-jwt-secret"
|
|
export STRIPE_SECRET_KEY="sk_test_..."
|
|
export STRIPE_WEBHOOK_SECRET="whsec_..."
|
|
export BILLING_SUCCESS_URL="http://localhost:3000/billing/success"
|
|
export BILLING_CANCEL_URL="http://localhost:3000/billing/cancel"
|
|
export INTERNAL_API_KEY="internal-api-key"
|
|
export TRIAL_PERIOD_DAYS="7"
|
|
export PORT="8083"
|
|
|
|
# 3. Service starten
|
|
go run cmd/server/main.go
|
|
|
|
# 4. Tests ausfuehren
|
|
go test -v ./...
|
|
```
|
|
|
|
### Mit Docker
|
|
|
|
```bash
|
|
# Service bauen und starten
|
|
docker compose up billing-service
|
|
|
|
# Nur bauen
|
|
docker build -t billing-service .
|
|
```
|
|
|
|
## Architektur
|
|
|
|
```
|
|
billing-service/
|
|
├── cmd/server/main.go # Entry Point
|
|
├── internal/
|
|
│ ├── config/config.go # Konfiguration
|
|
│ ├── database/database.go # DB Connection + Migrations
|
|
│ ├── models/models.go # Datenmodelle
|
|
│ ├── middleware/middleware.go # JWT Auth, CORS, Rate Limiting
|
|
│ ├── services/
|
|
│ │ ├── subscription_service.go # Subscription Management
|
|
│ │ ├── task_service.go # Task Consumption
|
|
│ │ ├── entitlement_service.go # Feature Gating
|
|
│ │ ├── usage_service.go # Usage Tracking (Legacy)
|
|
│ │ └── stripe_service.go # Stripe API
|
|
│ └── handlers/
|
|
│ ├── billing_handlers.go # API Endpoints
|
|
│ └── webhook_handlers.go # Stripe Webhooks
|
|
├── Dockerfile
|
|
└── go.mod
|
|
```
|
|
|
|
## Task-basiertes Billing
|
|
|
|
### Konzept
|
|
|
|
- **1 Task = 1 Kontingentverbrauch** (unabhaengig von Seitenanzahl, Tokens, etc.)
|
|
- **Monatliches Kontingent**: Plan-abhaengig (Basic: 30, Standard: 100, Premium: Fair Use)
|
|
- **Carryover**: Ungenutzte Tasks sammeln sich bis zu 5 Monate an
|
|
- **Max Balance**: `monthly_allowance * 5` (z.B. Basic: max 150 Tasks)
|
|
|
|
### Task Types
|
|
|
|
```go
|
|
TaskTypeCorrection = "correction" // Korrekturaufgabe
|
|
TaskTypeLetter = "letter" // Brief erstellen
|
|
TaskTypeMeeting = "meeting" // Meeting-Protokoll
|
|
TaskTypeBatch = "batch" // Batch-Verarbeitung
|
|
TaskTypeOther = "other" // Sonstige
|
|
```
|
|
|
|
### Monatswechsel-Logik
|
|
|
|
Bei jedem API-Aufruf wird geprueft, ob ein Monat vergangen ist:
|
|
1. `last_renewal_at` pruefen
|
|
2. Falls >= 1 Monat: `task_balance += monthly_allowance`
|
|
3. Cap bei `max_task_balance`
|
|
4. `last_renewal_at` aktualisieren
|
|
|
|
## API Endpoints
|
|
|
|
### User Endpoints (JWT Auth)
|
|
|
|
| Methode | Endpoint | Beschreibung |
|
|
|---------|----------|--------------|
|
|
| GET | `/api/v1/billing/status` | Aktueller Billing Status |
|
|
| GET | `/api/v1/billing/plans` | Verfuegbare Plaene |
|
|
| POST | `/api/v1/billing/trial/start` | Trial starten |
|
|
| POST | `/api/v1/billing/change-plan` | Plan wechseln |
|
|
| POST | `/api/v1/billing/cancel` | Abo kuendigen |
|
|
| GET | `/api/v1/billing/portal` | Stripe Portal URL |
|
|
|
|
### Internal Endpoints (API Key)
|
|
|
|
| Methode | Endpoint | Beschreibung |
|
|
|---------|----------|--------------|
|
|
| GET | `/api/v1/billing/entitlements/:userId` | Entitlements abrufen |
|
|
| GET | `/api/v1/billing/entitlements/check/:userId/:feature` | Feature pruefen |
|
|
| GET | `/api/v1/billing/tasks/check/:userId` | Task erlaubt? |
|
|
| POST | `/api/v1/billing/tasks/consume` | Task konsumieren |
|
|
| GET | `/api/v1/billing/tasks/usage/:userId` | Task Usage Info |
|
|
|
|
### Webhook
|
|
|
|
| Methode | Endpoint | Beschreibung |
|
|
|---------|----------|--------------|
|
|
| POST | `/api/v1/billing/webhook` | Stripe Webhooks |
|
|
|
|
## Plaene und Preise
|
|
|
|
| Plan | Preis | Tasks/Monat | Max Balance | Features |
|
|
|------|-------|-------------|-------------|----------|
|
|
| Basic | 9.90 EUR | 30 | 150 | Basis-Features |
|
|
| Standard | 19.90 EUR | 100 | 500 | + Templates, Batch |
|
|
| Premium | 39.90 EUR | Fair Use | 5000 | + Team, Admin, API |
|
|
|
|
### Fair Use Mode (Premium)
|
|
|
|
Im Premium-Plan:
|
|
- Keine praktische Begrenzung
|
|
- Tasks werden trotzdem getrackt (fuer Monitoring)
|
|
- Balance wird nicht dekrementiert
|
|
- `CheckTaskAllowed` gibt immer `true` zurueck
|
|
|
|
## Datenbank
|
|
|
|
### Wichtige Tabellen
|
|
|
|
```sql
|
|
-- Task-basierte Nutzung pro Account
|
|
CREATE TABLE account_usage (
|
|
account_id UUID UNIQUE,
|
|
plan VARCHAR(50),
|
|
monthly_task_allowance INT,
|
|
max_task_balance INT,
|
|
task_balance INT,
|
|
last_renewal_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- Einzelne Task-Records
|
|
CREATE TABLE tasks (
|
|
id UUID PRIMARY KEY,
|
|
account_id UUID,
|
|
task_type VARCHAR(50),
|
|
consumed BOOLEAN,
|
|
created_at TIMESTAMPTZ
|
|
);
|
|
```
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
# Alle Tests
|
|
go test -v ./...
|
|
|
|
# Mit Coverage
|
|
go test -cover ./...
|
|
|
|
# Nur Models
|
|
go test -v ./internal/models/...
|
|
|
|
# Nur Services
|
|
go test -v ./internal/services/...
|
|
|
|
# Nur Handlers
|
|
go test -v ./internal/handlers/...
|
|
```
|
|
|
|
## Stripe Integration
|
|
|
|
### Webhooks
|
|
|
|
Konfiguriere im Stripe Dashboard:
|
|
```
|
|
URL: https://your-domain.com/api/v1/billing/webhook
|
|
Events:
|
|
- checkout.session.completed
|
|
- customer.subscription.created
|
|
- customer.subscription.updated
|
|
- customer.subscription.deleted
|
|
- invoice.paid
|
|
- invoice.payment_failed
|
|
```
|
|
|
|
### Lokales Testing
|
|
|
|
```bash
|
|
# Stripe CLI installieren
|
|
brew install stripe/stripe-cli/stripe
|
|
|
|
# Webhook forwarding
|
|
stripe listen --forward-to localhost:8083/api/v1/billing/webhook
|
|
|
|
# Test Events triggern
|
|
stripe trigger checkout.session.completed
|
|
stripe trigger invoice.paid
|
|
```
|
|
|
|
## Umgebungsvariablen
|
|
|
|
| Variable | Beschreibung | Beispiel |
|
|
|----------|--------------|----------|
|
|
| `DATABASE_URL` | PostgreSQL Connection String | `postgres://...` |
|
|
| `JWT_SECRET` | JWT Signing Secret | `your-secret` |
|
|
| `STRIPE_SECRET_KEY` | Stripe Secret Key | `sk_test_...` |
|
|
| `STRIPE_WEBHOOK_SECRET` | Webhook Signing Secret | `whsec_...` |
|
|
| `BILLING_SUCCESS_URL` | Checkout Success Redirect | `http://...` |
|
|
| `BILLING_CANCEL_URL` | Checkout Cancel Redirect | `http://...` |
|
|
| `INTERNAL_API_KEY` | Service-to-Service Auth | `internal-key` |
|
|
| `TRIAL_PERIOD_DAYS` | Trial Dauer in Tagen | `7` |
|
|
| `PORT` | Server Port | `8083` |
|
|
|
|
## Error Handling
|
|
|
|
### Task Limit Reached
|
|
|
|
```json
|
|
{
|
|
"error": "TASK_LIMIT_REACHED",
|
|
"message": "Dein Aufgaben-Kontingent ist aufgebraucht.",
|
|
"current_balance": 0,
|
|
"plan": "basic"
|
|
}
|
|
```
|
|
|
|
HTTP Status: `402 Payment Required`
|
|
|
|
### No Subscription
|
|
|
|
```json
|
|
{
|
|
"error": "NO_SUBSCRIPTION",
|
|
"message": "Kein aktives Abonnement gefunden."
|
|
}
|
|
```
|
|
|
|
HTTP Status: `403 Forbidden`
|
|
|
|
## Frontend Integration
|
|
|
|
### Task Usage anzeigen
|
|
|
|
```typescript
|
|
// Response von GET /api/v1/billing/status
|
|
interface TaskUsageInfo {
|
|
tasks_available: number; // z.B. 45
|
|
max_tasks: number; // z.B. 150
|
|
info_text: string; // "Aufgaben verfuegbar: 45 von max. 150"
|
|
tooltip_text: string; // "Aufgaben koennen sich bis zu 5 Monate ansammeln."
|
|
}
|
|
```
|
|
|
|
### Task konsumieren
|
|
|
|
```typescript
|
|
// Vor jeder KI-Aktion
|
|
const response = await fetch('/api/v1/billing/tasks/check/' + userId);
|
|
const { allowed, message } = await response.json();
|
|
|
|
if (!allowed) {
|
|
showUpgradeDialog(message);
|
|
return;
|
|
}
|
|
|
|
// Nach erfolgreicher KI-Aktion
|
|
await fetch('/api/v1/billing/tasks/consume', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ user_id: userId, task_type: 'correction' })
|
|
});
|
|
```
|