presseportale/docs/PHASE-9-FLOW-UND-TARIFE-PLAN.md
Kevin Adametz 8d8d957884 Doku: Status-Sync 11./12.06., Decision-Update Preisstruktur und Phase-9-Plan
- Decision-Update Preisstruktur & Veroeffentlichungs-Flow aufgenommen
  (Launch-Tarife, Slot-Verbrauch bei Veroeffentlichung, Submit-Gate,
  Launch-Credits) inkl. Klarstellung 12.06.: Gelb geht direkt live,
  keine manuelle Pruef-Queue, nur Rot wird abgelehnt
- Alle Status-Dokumente auf den Code-Stand gezogen: README-Index,
  STATUS-ABGLEICH (KI-Pipeline, Bilder/Lizenzen, Pricing), Checkliste
  (KI- und Titelbild-Bloecke, Launch-To-dos), Admin-User,
  user-zusammenhaenge (Datenmodell-Delta), Entwicklungsplan KI-Pruefung
  (Phase 0 abgehakt, Decision-Abgleich)
- Ueberschriebene Tarif-Abschnitte in Konzept-Update 1/2 und
  Relaunch-Konzept mit Superseded-/IST-Hinweisen markiert
- Neues Plan-Dokument PHASE-9-FLOW-UND-TARIFE-PLAN.md (9A-9J)
- Phase-8-Roadmap-Doku (20-PHASE-8-USER-PANEL.md) + PROGRESS-Eintraege

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:20:22 +00:00

224 lines
10 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.

# Phase 9 · Veröffentlichungs-Flow (Launch) & Tarif-Modul
Stand: 2026-06-12 — **in Umsetzung** (Block 1: 9A9C zuerst, dann Review-Stopp,
dann Block 2: 9D9J).
Vorgänger: Phase 8 (User-Panel-Konsolidierung) + KI-Prüf-Pipeline (beide abgeschlossen).
Verbindliche Entscheidungen: [`docs/Decision-Update Preisstruktur & Veröffentlichungs-Flow.md`](./Decision-Update%20Preisstruktur%20&%20Ver%C3%B6ffentlichungs-Flow.md)
Abgleich-Doku: [`docs/STATUS-ABGLEICH-USER-PANEL.md`](./STATUS-ABGLEICH-USER-PANEL.md)
---
## 0. Worum es geht
Phase 9 setzt das Decision-Update vom 11./12.06.2026 um — in zwei Blöcken:
1. **Block 1 — Veröffentlichungs-Flow (9A9C)**: Die Flow-Regeln, die
unabhängig vom Tarif-Modul gelten und auf denen das Tarif-Modul aufsetzt.
Funktioniert vollständig mit dem vorhandenen Quota-Stub.
2. **Block 2 — Tarif-Modul (9D9I)**: Zahlung, Tarife, Einzel-PM, Tageslimit
und die drei Launch-Credit-Posten. Löst den Quota-Stub ab.
**Leitplanken aus dem Decision-Update:**
- Gelb geht zum Launch **direkt live** wie Grün — keine manuelle Prüf-Queue.
Nur Rot wird abgelehnt (Meldung mit Begründung an den Autor).
- Der PM-Slot zählt **bei Veröffentlichung** runter, nicht bei der Prüfung.
Rot verbraucht keinen Slot.
- „Speichern" bleibt immer frei; „Speichern & zur Prüfung einreichen" ist
hinter eine aktive Buchung gegated (der Button konvertiert, er verschwindet
nicht).
- Kein Re-Check zum Launch: eine Einreichung = eine Prüfung = (bei Gelb/Grün)
eine Veröffentlichung. Vorab-Prüfung/Redigieren sind Phase 2.
---
## 1. Sub-Päckchen-Übersicht
| ID | Thema | Größe | Risiko |
|---|---|---|---|
| **9A** | Gelb-Routing auf Direkt-Live umstellen (Routing, Scheduler, Tests) | S | gering |
| **9B** | Slot-Verbrauch von Einreichung auf Veröffentlichung umstellen (Rot = kein Slot) | M | mittel (Idempotenz) |
| **9C** | Submit-Gate-Schnittstelle (`hasActiveBooking()`-Stub, Modal-Hinweis, Server-Guard) | M | gering |
| — | **Review-Stopp mit User** | | |
| **9D** | Tarif-Datenmodell: Pläne, Subscriptions, Einzel-PM-Käufe; Quota-Stub ablösen | L | hoch (Datenmodell) |
| **9E** | Stripe-Anbindung (Laravel Cashier — **Dependency-Freigabe nötig**) | L | mittel |
| **9F** | Tarif-Seite + Checkout-UI (Raster, Einzel-PM-Block, „2 Monate gratis", Enterprise-Hinweis) | M | gering |
| **9G** | Tageslimit je Tier (Business 2 / Pro 3 / Agency 5; gilt auch für Extra-PMs) | S | gering |
| **9H** | Einzel-PM-Kauf (19 €) + Einzel→Abo-Brücke (Anrechnung 30 Tage) | M | mittel |
| **9I** | Launch-Credits: Extra-PM, Boost (nur Grün), Veröffentlichungsnachweis-PDF | L | mittel |
| **9J** | Abschluss: Tests, Pint, Build, Doku-Sync, PROGRESS-Eintrag | S | keine |
Nach jedem Päckchen Review-Stopp mit dem User; vor 9D ein größerer
(Datenmodell-Entscheidungen + Cashier-Freigabe).
---
## 2. Block 1 — Veröffentlichungs-Flow
### 9A · Gelb-Routing auf Direkt-Live
**Entscheidung (12.06.2026)**: Rot = nicht veröffentlichbar (rechtlich/
inhaltlich) → Ablehnung mit Meldung. Gelb/Grün = veröffentlichbar → geht in
der ersten Phase direkt online. Gelb bleibt als interne Markierung erhalten
(nicht boostbar, Admin-Signal), löst aber keine manuelle Prüfung aus.
**Anpassungen:**
- `PressReleaseService::routeByClassification()`: Gelb durchläuft denselben
Auto-Publish-Pfad wie Grün (`autoPublishGreen()` → generalisiert zu
`autoPublishApproved()`); Verzögerungsfenster
(`scoring.classification.green_delay_minutes`) gilt für beide.
- `PublishScheduledPressReleases`: Kandidaten-Query von
`classification = green` auf `classification IN (green, yellow)`.
- Admin-Review-Queue bleibt als **Fallback** bestehen: unklassifizierte PMs
(Job noch nicht gelaufen / KI-Ausfall ohne Fallback-Ergebnis) bleiben in
`review` und sind manuell behandelbar. KI-Badge und Klassifikations-Filter
im Admin bleiben unverändert.
**Tests:** `PressReleaseClassificationJobTest` (Gelb-sofort → published,
Gelb-geplant → bleibt review bis Termin), `PressReleaseSchedulingTest`
(gelbe fällige PM wird publiziert).
### 9B · Slot-Verbrauch bei Veröffentlichung
**Regel:** Der Slot zählt genau einmal pro PM, beim **ersten** Übergang zu
`published`. Rot abgelehnte PMs verbrauchen nichts.
**Anpassungen:**
- `submitForReview()`: Increment von `press_release_quota_used_this_month`
**entfernen**. Stattdessen Guard: Einreichen erfordert
`pressReleaseQuotaRemaining() > 0` (sonst würde eine grüne PM ohne
verfügbaren Slot veröffentlicht).
- `publish()`: Increment beim Statuswechsel auf `published`, idempotent —
nur wenn die PM zuvor noch nie veröffentlicht war (Prüfung über
`press_release_status_logs`, kein neues Schema-Feld). Zählt auf den
PM-Eigentümer (`user_id`).
- Veröffentlichungs-Modal: Text von „wird bei Einreichung verbraucht" auf
„wird bei Veröffentlichung verbraucht; abgelehnte PMs kosten keinen Slot".
**Tests:** Submit verbraucht keinen Slot; Publish (Admin, Auto-Publish,
Scheduler) verbraucht genau einen; Rot → kein Verbrauch; Archivieren +
erneutes Publizieren zählt nicht doppelt; Submit bei 0 Rest-Slots blockiert.
### 9C · Submit-Gate-Schnittstelle
**Ziel:** Das Gate aus dem Decision-Update §5.1, gebaut gegen eine schmale
Schnittstelle, die zunächst ein Stub bedient und in 9D/9E vom Tarif-Modul
implementiert wird — Modal und Service müssen dann nicht mehr angefasst werden.
**Anpassungen:**
- `User::hasActiveBooking(): bool` — Launch-Schnittstelle. Stub-Verhalten
über `config/billing.php` (`billing.enforce_booking`, Default `false`):
solange das Tarif-Modul fehlt, gibt die Methode `true` zurück; mit
aktiviertem Flag (und später echter Subscription-Prüfung) greift das Gate.
- Einreichungs-Modal (`press-release-submit-modal`): ohne aktive Buchung
zeigt das Modal statt des Prüf-Flows einen Buchungs-Hinweis mit CTA
(„Buchung erforderlich" → Tarif-Seite). Der Button bleibt sichtbar.
- Server-Guard: `submitForReview()` wirft ohne aktive Buchung eine Exception
(UI allein reicht nicht); API-Submit-Route antwortet mit **402**.
**Tests:** Gate aus (Default) → Verhalten unverändert; Gate an →
Modal-Hinweis statt Checkboxen, `submitForReview` wirft, API gibt 402.
---
## 3. Block 2 — Tarif-Modul (nach Review-Stopp)
### 9D · Tarif-Datenmodell
- Tabellen (Arbeitsstand, final beim Review-Stopp vor 9D):
`plans` (Starter/Business/Pro/Agency: Preis mtl./jährl., PMs/Monat,
Tageslimit), `subscriptions` (User, Plan, Zyklus, Status, Periodenstart/-ende),
`single_purchases` (Einzel-PM, Extra-PM, Boost, PDF — Typ, Preis, Status,
`applied_to_press_release_id`).
- `User::hasActiveBooking()` prüft echte Subscription oder offenen Einzel-PM-Kauf.
- Slot-Logik wechselt von `users.press_release_quota` auf Plan-Kontingent +
Periodenzähler; Stub-Spalten werden nach Migration entfernt.
- Kontingent-Anzeige (Modal, Editor) liest aus der neuen Quelle —
Schnittstelle `pressReleaseQuotaRemaining()` bleibt stabil.
### 9E · Stripe (Laravel Cashier)
- **Vor Start freizugeben:** `laravel/cashier` als neue Dependency.
- Checkout für Abo (monatlich/jährlich) und Einmalzahlung (Einzel-PM, Credits).
- Webhooks (Subscription-Status, Zahlungsausfall) + lokale Spiegelung.
- Rechnungen an bestehende `invoices`-Struktur anbinden (Klärung beim Review).
### 9F · Tarif-Seite + Checkout-UI
- Raster mit 4 Tiers; Einzel-PM als separater No-Abo-Block (nicht als
billigste Spalte); Enterprise als dezenter Sales-Hinweis unter der Tabelle.
- Jahrespreis kommuniziert als „2 Monate gratis".
- Einstieg aus dem Submit-Gate-Hinweis (9C) und aus „Buchungen & Add-ons".
### 9G · Tageslimit
- `plans.daily_limit` (Starter ohne Limit); Prüfung beim Veröffentlichen
(nicht beim Einreichen), zählt veröffentlichte PMs des Users pro Kalendertag
(Europe/Berlin); gilt auch für Extra-PMs. Überschreitung → PM bleibt in
`review` mit Hinweis, Veröffentlichung am Folgetag durch den Scheduler.
### 9H · Einzel-PM + Abo-Brücke
- Einzel-PM-Kauf 19 € → genau eine Einreichung/Veröffentlichung.
- Brücke: Abo-Abschluss innerhalb 30 Tagen rechnet 19 € auf den ersten
Monat an (Stripe-Coupon oder Rabatt-Position).
### 9I · Launch-Credits
- Credit-Wallet (1 Credit = 1 € Listenpreis, Pakete mit Volumenrabatt).
- Posten: **Extra-PM** (Kontingent voll → einzelne PM nachkaufen),
**Boost** (nur für grün klassifizierte PMs, nachträglich),
**Veröffentlichungsnachweis-PDF**.
- Kein Dofollow-Backlink-Verkauf (bewusst ausgeschlossen).
### 9J · Abschluss
- Volle Suite grün, Pint clean, `npm run build` clean.
- Doku-Sync: `STATUS-ABGLEICH`, `checkliste-user-backend.md`, dieses Dokument,
`PROGRESS.md`-Eintrag.
---
## 4. Was außerhalb von Phase 9 bleibt
- Vorab-KI-Prüfung, Redigieren/Re-Check-Loop, Prüfzähler, Credit-Overflow
(Decision-Update §7 — Phase 2)
- Score-Feinstufung für Boost („nur Geprüft/Hochwertig boostbar")
- Magic-Link-Flow, Statistik-/Abrechnungs-Tabs, Anhänge-Reaktivierung,
Trust-Score, Notice-and-Action
---
## 5. Risiken & Annahmen
- **Idempotenz Slot-Verbrauch (9B)**: Prüfung über Status-Logs statt neuem
Feld — bei Alt-Daten mit unvollständigen Logs schlimmstenfalls ein
doppelter Zähler; akzeptabel für den Stub, wird mit 9D-Periodenzähler
sauber.
- **Gate-Stub (9C)**: `enforce_booking=false` als Default hält das System
bis zum Tarif-Modul voll funktionsfähig; das Flag erlaubt Tests und
frühe Aktivierung.
- **9D/9E Datenmodell + Cashier**: größter Block, eigener Review-Stopp davor;
Stub-Ablösung (`press_release_quota`-Spalten entfernen) erst nach
verifizierter Migration.
- **Rechtstexte** (Einreichungs-Modal) sind weiterhin Platzhalter —
anwaltliche Prüfung läuft parallel, unabhängig von Phase 9.
- **Betrieb**: Queue-Worker für `classification` in Produktion bleibt
Go-Live-Voraussetzung (unabhängig von Phase 9).
---
## 6. Akzeptanzkriterien Phase 9 gesamt
- [ ] Gelb klassifizierte PMs gehen ohne manuelle Prüfung live (sofort/Termin)
- [ ] Rot verbraucht keinen Slot; Slot zählt genau einmal, bei Veröffentlichung
- [ ] Einreichen ohne aktive Buchung zeigt Buchungs-Hinweis (UI) und wird
serverseitig abgelehnt (Gate aktiviert)
- [ ] Tarife buchbar (4 Tiers, monatlich/jährlich), Einzel-PM kaufbar
- [ ] Tageslimit greift je Tier, auch für Extra-PMs
- [ ] Extra-PM, Boost (nur Grün) und PDF-Nachweis als Credits kaufbar
- [ ] Quota-Stub vollständig abgelöst, `pressReleaseQuotaRemaining()` stabil
- [ ] Tests grün, Pint clean, Build clean, Doku synchron