Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
296
billing-service/README.md
Normal file
296
billing-service/README.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# 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' })
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user