503 lines
20 KiB
Markdown
503 lines
20 KiB
Markdown
# Payment Dashboard – Entwicklungsplan
|
||
|
||
**Ziel:** Integration eines Payment-Monitoring-Dashboards in mivita.care, das PAYONE-Probleme
|
||
frühzeitig sichtbar macht. Das Dashboard kombiniert manuell erfasste Incidents mit echten Daten
|
||
aus den bestehenden Tabellen (`payment_transactions`, `shopping_payments`) und den vorhandenen
|
||
PAYONE-Logs.
|
||
|
||
---
|
||
|
||
## Implementierungsstand (Stand: 13.04.2026)
|
||
|
||
| Phase | Thema | Status | Anmerkungen |
|
||
|-------|------------------------------|-------------------|-------------|
|
||
| 1 | Datenbank & Models | ✅ Umgesetzt | Migration, alle drei Models vorhanden |
|
||
| 2 | Controller & Routes | ✅ Umgesetzt | Routes in `routes/domains/crm.php`, Entwurfsdatei unter `dev/` ist veraltet |
|
||
| 3 | Views (Bootstrap/Appwork) | ✅ Umgesetzt | Index, Management, Show, Transactions, Logs + 4 Partials |
|
||
| 4 | Live-Transaktionsdaten | ✅ Umgesetzt | `transactions()` + View mit Filtern, Statistiken, Pagination |
|
||
| 5 | Log-Viewer | ✅ Umgesetzt | `?date=`-Parameter wird ausgewertet; sichere Validierung der Datumseingabe |
|
||
| 6 | Uptime-Check Artisan Command | ✅ Umgesetzt | Command `payment:check-uptime`, Schedule alle 5 Minuten, Uptime-Karten im Dashboard |
|
||
| 7 | E-Mail-Benachrichtigung | ✅ Umgesetzt | Dediziertes Mailable `PaymentIncidentAlert` (Queue), sendet bei `critical`-Incidents |
|
||
| 8 | Tests | ✅ Umgesetzt | `PaymentDashboardAccessTest`, `PaymentIncidentCrudTest`, `CheckPaymentUptimeCommandTest` |
|
||
|
||
### Zusätzlich umgesetzt (nicht in Phasen geplant)
|
||
|
||
- **Navigation:** Sidenav-Link mit gecachtem Badge für offene Incidents in `layout-sidenav.blade.php`
|
||
- **Übersetzungen:** `payment_monitor` in `de`, `en`, `es` navigation.php
|
||
- **Form Requests:** `StorePaymentIncidentRequest` + `AddIncidentActivityRequest` in `app/Http/Requests/PaymentIncident/`
|
||
- **Cache-Invalidierung:** `Cache::forget('open_incident_count')` in `store()`, `updateStatus()`, `addActivity()`
|
||
|
||
### Bekannte Abweichungen vom Plan
|
||
|
||
- **Views:** Nutzen `layouts.layout-2` statt `layout-1` (wie im Plan angegeben)
|
||
- **Entwurfsdatei** `dev/payment-dashboard/routes/payment-dashboard.php`: Veraltet — falscher Namespace (`App\Http\Controllers\PaymentDashboardController`), falsche Methodennamen (`developer` statt `index`), nur `auth`-Middleware statt `admin`
|
||
|
||
### Offene Punkte
|
||
|
||
1. **Phase 9 (Auto-Incident aus `PayoneController`):** Noch nicht begonnen — kritische Fehler (z.B. Error:2008) könnten automatisch Incidents anlegen
|
||
2. **Entwurfsdatei aufräumen:** `dev/payment-dashboard/routes/payment-dashboard.php` ggf. löschen oder als Archiv kennzeichnen
|
||
|
||
---
|
||
|
||
## Analyse des Ist-Zustands
|
||
|
||
### Bereits vorhandene Datenquellen
|
||
|
||
| Quelle | Relevanz |
|
||
|-------------------------------|-------------------------------------------------------------------|
|
||
| `payment_transactions` | Jeder PAYONE-Callback: `txaction`, `errorcode`, `errormessage`, `mode`, `transmitted_data` |
|
||
| `shopping_payments` | Jede Zahlung: `reference`, `txaction`, `clearingtype`, `amount` |
|
||
| `shopping_orders` | Bestellstatus, `paid`-Flag, Verknüpfung zu Payments |
|
||
| `storage/logs/payone.log` | Fehler-Details aus `MyLog::writeLog('payone', ...)` (Error 2001–2008) |
|
||
| `storage/logs/payment.log` | Allgemeine Zahlungsfehler |
|
||
|
||
### Bestehende PAYONE-Fehlercodes (aus `Api/PayoneController`)
|
||
|
||
| Code | Bedeutung |
|
||
|--------|------------------------------------------------|
|
||
| 2001 | Callback: Parameter unvollständig |
|
||
| 2002 | Callback: Key-Validierung fehlgeschlagen |
|
||
| 2003 | Callback: ShoppingOrder nicht gefunden |
|
||
| 2004 | Callback: ShoppingPayment nicht gefunden |
|
||
| 2005 | Callback: Payment ↔ Order Zuordnung falsch |
|
||
| 2006 | Callback: Preisabweichung |
|
||
| 2008 | Callback: Datenbank-Transaktion fehlgeschlagen |
|
||
|
||
### Wichtige Unterschiede zum Entwurf
|
||
|
||
- **Layout:** Das Dashboard nutzt das bestehende Appwork/Bootstrap-Layout (`layout-1.blade.php`),
|
||
nicht das Custom-Dark-Mode-Layout aus dem Entwurf.
|
||
- **Auth:** Middleware `admin` (admin >= 2) statt nur `auth` — kein separates Rollen-System nötig.
|
||
- **Echte Daten:** Statt rein manuelle Incidents, werden bestehende Transaktionsdaten direkt
|
||
eingebunden (Live-Tab). Manuelle Incidents bleiben als Eskalationswerkzeug erhalten.
|
||
- **Logs:** Der `payone`-Log-Kanal wird direkt im Dashboard lesbar gemacht.
|
||
|
||
---
|
||
|
||
## Phasen-Übersicht
|
||
|
||
| Phase | Titel | Aufwand | Priorität |
|
||
|-------|------------------------------|----------|-----------|
|
||
| 1 | Datenbank & Models | ~1h | Muss |
|
||
| 2 | Controller & Routes | ~2h | Muss |
|
||
| 3 | Views (Bootstrap/Appwork) | ~3h | Muss |
|
||
| 4 | Live-Daten: Transaktionen | ~2h | Hoch |
|
||
| 5 | Log-Viewer | ~1h | Hoch |
|
||
| 6 | Uptime-Check Artisan Command | ~2h | Mittel |
|
||
| 7 | E-Mail-Benachrichtigung | ~1h | Mittel |
|
||
| 8 | Tests | ~2h | Muss |
|
||
|
||
---
|
||
|
||
## Phase 1: Datenbank & Models ✅
|
||
|
||
### Migration (aus dem Entwurf übernehmen, leicht angepasst)
|
||
|
||
**Datei:** `database/migrations/YYYY_MM_DD_000001_create_payment_incidents_table.php`
|
||
|
||
Drei neue Tabellen:
|
||
- `payment_incidents` — manuell erfasste Störungen/Incidents
|
||
- `incident_activities` — Kommunikationsverlauf pro Incident (Notizen, Tickets, Anrufe)
|
||
- `provider_uptime_logs` — automatische Uptime-Checks (Phase 6)
|
||
|
||
Anpassungen zum Entwurf:
|
||
- `provider`-Enum zunächst auf `payone` fokussiert (andere Provider ergänzen, wenn aktiv genutzt)
|
||
- `notes`-Feld ergänzen in `payment_incidents` für freie interne Kommentare
|
||
- Index auf `detected_at` und `provider` für Performance bei größeren Datenmengen
|
||
|
||
### Models
|
||
|
||
**`app/Models/PaymentIncident.php`** — aus dem Entwurf übernehmen, ergänzen um:
|
||
- `scopeOpen()` / `scopePayone()` / `scopeLastDays(int $days)` für häufige Abfragen
|
||
- `getTypeIconAttribute()` für die View-Darstellung
|
||
|
||
**`app/Models/IncidentActivity.php`** — direkt aus dem Entwurf übernehmen
|
||
|
||
**`app/Models/ProviderUptimeLog.php`** — neu erstellen für Phase 6
|
||
|
||
---
|
||
|
||
## Phase 2: Controller & Routes ✅
|
||
|
||
### Controller-Struktur
|
||
|
||
```
|
||
app/Http/Controllers/Admin/PaymentDashboardController.php
|
||
```
|
||
|
||
Platzierung unter `Admin/`, konsistent mit `PaymentSalesController.php`.
|
||
|
||
#### Actions
|
||
|
||
| Method | URL | Beschreibung |
|
||
|-----------|-----------------------------------------|--------------------------------------------|
|
||
| `index` | GET `/admin/payment-dashboard` | Entwickler-Ansicht: alle Incidents + Stats |
|
||
| `management` | GET `/admin/payment-dashboard/management` | GF-Ansicht: Ampel-Karten, kein Bearbeiten |
|
||
| `show` | GET `/admin/payment-dashboard/{incident}` | Incident-Detail mit Timeline |
|
||
| `store` | POST `/admin/payment-dashboard` | Neuen Incident anlegen |
|
||
| `addActivity` | POST `/admin/payment-dashboard/{incident}/activity` | Aktivität hinzufügen |
|
||
| `updateStatus` | PATCH `/admin/payment-dashboard/{incident}/status` | Status ändern |
|
||
| `transactions` | GET `/admin/payment-dashboard/transactions` | Live-PAYONE-Transaktionen (Phase 4) |
|
||
| `logs` | GET `/admin/payment-dashboard/logs` | PAYONE-Log-Viewer (Phase 5) |
|
||
|
||
#### Stats-Methoden im Controller
|
||
|
||
Zusätzlich zu den Stats aus dem Entwurf:
|
||
- `getPayoneTransactionStats()` — Fehlerquote der letzten 7/30 Tage aus `payment_transactions`
|
||
- `getFailedPayments()` — alle Payments mit `txaction = 'failed'` der letzten 30 Tage
|
||
- `getErrorDistribution()` — Häufigkeit der Fehlercodes 2001–2008 aus den Logs
|
||
|
||
### Routes
|
||
|
||
In `routes/web.php` ergänzen (nicht als separates File, analog zu bestehender Struktur):
|
||
|
||
```php
|
||
Route::prefix('admin/payment-dashboard')
|
||
->name('admin.payment-dashboard.')
|
||
->middleware(['auth', 'admin'])
|
||
->group(function () {
|
||
Route::get('/', [PaymentDashboardController::class, 'index'])->name('index');
|
||
Route::get('/management', [PaymentDashboardController::class, 'management'])->name('management');
|
||
Route::get('/transactions', [PaymentDashboardController::class, 'transactions'])->name('transactions');
|
||
Route::get('/logs', [PaymentDashboardController::class, 'logs'])->name('logs');
|
||
Route::get('/{incident}', [PaymentDashboardController::class, 'show'])->name('show');
|
||
Route::post('/', [PaymentDashboardController::class, 'store'])->name('store');
|
||
Route::post('/{incident}/activity', [PaymentDashboardController::class, 'addActivity'])->name('activity.store');
|
||
Route::patch('/{incident}/status', [PaymentDashboardController::class, 'updateStatus'])->name('status.update');
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 3: Views (Bootstrap/Appwork) ✅
|
||
|
||
### Layout-Anpassung
|
||
|
||
**Kein** Custom-Dark-Mode-Layout. Stattdessen: `@extends('layouts.layout-1')`, analog zu
|
||
anderen Admin-Views (z.B. `resources/views/admin/payment/salesvolume.blade.php`).
|
||
|
||
### View-Struktur
|
||
|
||
```
|
||
resources/views/admin/payment-dashboard/
|
||
├── index.blade.php # Entwickler-Ansicht
|
||
├── management.blade.php # GF-Ansicht
|
||
├── show.blade.php # Incident-Detail
|
||
├── transactions.blade.php # Live-Transaktionen (Phase 4)
|
||
├── logs.blade.php # Log-Viewer (Phase 5)
|
||
└── _partials/
|
||
├── stats-cards.blade.php
|
||
├── incident-table.blade.php
|
||
├── activity-timeline.blade.php
|
||
└── create-incident-modal.blade.php
|
||
```
|
||
|
||
### UI-Komponenten
|
||
|
||
**Entwickler-Ansicht (`index.blade.php`):**
|
||
- Stat-Karten oben: Offene Incidents | In Bearbeitung | PAYONE-Fehler (30 Tage) | Betroffener Umsatz
|
||
- Tabs: "Incidents" | "Live-Transaktionen" | "PAYONE Logs"
|
||
- Tabelle: Alle Incidents mit Status-Badge, Schwere-Farbe, Dauer, Quick-Status-Update
|
||
- Floating Button: "Neuen Incident anlegen" → Bootstrap-Modal
|
||
|
||
**GF-Ansicht (`management.blade.php`):**
|
||
- Ampel-Karten: Grün/Gelb/Rot je nach offenen Incidents und Schwere
|
||
- Sehr einfach, keine Bearbeitungsfunktionen
|
||
- Tagesaktuelle Zusammenfassung
|
||
|
||
**Incident-Detail (`show.blade.php`):**
|
||
- Kopfbereich: Titel, Provider-Badge, Status-Badge, Severity-Indikator
|
||
- Timeline: Chronologische Aktivitätsliste mit Icon je Typ
|
||
- Formulare: Aktivität hinzufügen, Status ändern
|
||
- Link zur betroffenen Bestellung (falls `ticket_number` eine Bestell-ID ist)
|
||
|
||
---
|
||
|
||
## Phase 4: Live-Transaktionsdaten ✅
|
||
|
||
Das wertvollste Feature: Echtzeit-Einblick in PAYONE-Transaktionen aus bestehenden Tabellen.
|
||
|
||
### Tab "Live-Transaktionen" in der Entwickler-Ansicht
|
||
|
||
Datenquelle: `payment_transactions` JOIN `shopping_payments` JOIN `shopping_orders`
|
||
|
||
**Anzeigen:**
|
||
- Alle Transaktionen der letzten 7 Tage, gefiltert nach `txaction`
|
||
- Fehlgeschlagene Transaktionen (`txaction = 'failed'`) hervorgehoben in Rot
|
||
- Fehlercodes und Fehlermeldungen aus `errorcode` / `errormessage` / `customermessage`
|
||
- `mode`-Feld: unterscheidet Test vs. Live-Modus (wichtig für Debugging)
|
||
- `transmitted_data` JSON: aufklappbar für Detailinspektion
|
||
|
||
**Filter-Optionen:**
|
||
- Nach `txaction`: `appointed`, `pending`, `paid`, `failed`
|
||
- Nach Zeitraum: Heute / Letzte 7 Tage / Letzte 30 Tage
|
||
- Nach Modus: Test / Live
|
||
|
||
**Stat-Block oben:**
|
||
- Erfolgsrate (paid / gesamt) der letzten 24h
|
||
- Anzahl `failed` Transaktionen heute
|
||
- Letzte `failed` Transaktion: vor X Minuten
|
||
- Verteilung der `clearingtype` (wlt, cc, elv…)
|
||
|
||
### Neue Methode im Controller
|
||
|
||
```php
|
||
private function getTransactionStats(int $days = 7): array
|
||
{
|
||
$since = now()->subDays($days);
|
||
return [
|
||
'total' => PaymentTransaction::where('created_at', '>=', $since)->count(),
|
||
'failed' => PaymentTransaction::where('txaction', 'failed')->where('created_at', '>=', $since)->count(),
|
||
'paid' => PaymentTransaction::where('txaction', 'paid')->where('created_at', '>=', $since)->count(),
|
||
'errors' => PaymentTransaction::whereNotNull('errorcode')->where('created_at', '>=', $since)
|
||
->select('errorcode', 'errormessage', DB::raw('count(*) as count'))
|
||
->groupBy('errorcode', 'errormessage')
|
||
->orderByDesc('count')
|
||
->get(),
|
||
'last_failed' => PaymentTransaction::where('txaction', 'failed')->latest()->first(),
|
||
];
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 5: Log-Viewer ✅
|
||
|
||
### Tab "PAYONE Logs" in der Entwickler-Ansicht
|
||
|
||
Liest direkt aus `storage/logs/payone.log` (aktuellste Datei bei daily rotation).
|
||
|
||
**Anzeigen:**
|
||
- Letzte 100 Log-Einträge (konfigurierbar)
|
||
- Farbliche Markierung nach Level: `error` (rot), `warning` (gelb), `info` (blau), `notice` (grau)
|
||
- Suche/Filter nach Fehlercode (z.B. "Error:2003")
|
||
- Zeitstempel, Log-Level, Nachricht, JSON-Payload (aufklappbar)
|
||
|
||
**Implementierung:**
|
||
|
||
```php
|
||
public function logs(): View
|
||
{
|
||
$logPath = storage_path('logs/payone-' . now()->format('Y-m-d') . '.log');
|
||
$entries = [];
|
||
|
||
if (file_exists($logPath)) {
|
||
$lines = array_reverse(file($logPath));
|
||
foreach (array_slice($lines, 0, 200) as $line) {
|
||
if (preg_match('/\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}[^\]]*)\] \w+\.(\w+): (.+)/', $line, $m)) {
|
||
$entries[] = [
|
||
'timestamp' => $m[1],
|
||
'level' => $m[2],
|
||
'message' => $m[3],
|
||
];
|
||
}
|
||
}
|
||
}
|
||
|
||
return view('admin.payment-dashboard.logs', compact('entries'));
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 6: Artisan Command – Uptime-Check ✅
|
||
|
||
### `php artisan payment:check-uptime`
|
||
|
||
**Datei:** `app/Console/Commands/CheckPaymentUptime.php`
|
||
|
||
Prüft erreichbare PAYONE-Endpunkte (Status-API oder bekannte öffentliche URLs) und legt bei
|
||
Ausfall automatisch einen Incident an.
|
||
|
||
```php
|
||
// Prüft: PAYONE Server-Status-Seite oder einen konfigurierbaren Health-Endpoint
|
||
// Speichert Ergebnis in provider_uptime_logs
|
||
// Bei Ausfall: erstellt PaymentIncident mit severity = 'critical', type = 'outage'
|
||
// Bei Wiederherstellung: setzt offene Outage-Incidents auf 'resolved'
|
||
```
|
||
|
||
**Scheduling in `app/Console/Kernel.php`:**
|
||
|
||
```php
|
||
$schedule->command('payment:check-uptime')->everyFiveMinutes();
|
||
```
|
||
|
||
**Konfiguration in `.env` / `config/services.php`:**
|
||
|
||
```
|
||
PAYONE_HEALTH_CHECK_URL=https://api.pay1.de/post-gateway/
|
||
PAYONE_HEALTH_CHECK_ENABLED=true
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 7: E-Mail-Benachrichtigung ✅
|
||
|
||
Bei neu eröffnetem Critical-Incident: automatische Mail via bestehenden `MyLog`-Mechanismus.
|
||
|
||
```php
|
||
// In PaymentDashboardController::store()
|
||
if ($validated['severity'] === 'critical') {
|
||
MyLog::writeLog(
|
||
'payment',
|
||
'error',
|
||
'Kritischer Zahlungs-Incident eröffnet: ' . $validated['title'],
|
||
$validated,
|
||
true // sendet Mail an config('app.exception_mail')
|
||
);
|
||
}
|
||
```
|
||
|
||
Alternativ: Dedizierte Mailable `App\Mail\PaymentIncidentAlert` für bessere Darstellung.
|
||
|
||
---
|
||
|
||
## Phase 8: Tests ✅ (vollständig)
|
||
|
||
**Feature-Tests:**
|
||
|
||
```
|
||
tests/Feature/PaymentDashboard/
|
||
├── PaymentDashboardAccessTest.php # Auth, Admin-Middleware
|
||
├── PaymentIncidentCrudTest.php # Create, Status-Update, Aktivität
|
||
├── PaymentDashboardStatsTest.php # Korrekte Stats aus Testdaten
|
||
└── CheckPaymentUptimeCommandTest.php # Artisan Command (Phase 6)
|
||
```
|
||
|
||
**Testszenarien:**
|
||
- Nicht eingeloggter User → Redirect
|
||
- Eingeloggter User ohne Admin (admin < 2) → 403
|
||
- Admin (admin >= 2) → Zugriff auf Entwickler-Ansicht
|
||
- Incident anlegen: Pflichtfelder, korrekte Aktivität wird auto-erstellt
|
||
- Status auf "resolved" setzen → `resolved_at` wird gesetzt
|
||
- Stats-Methoden liefern korrekte Werte mit Testdaten
|
||
|
||
---
|
||
|
||
## Implementierungsreihenfolge (empfohlen)
|
||
|
||
```
|
||
Phase 1 (Migration + Models)
|
||
→ Phase 2 (Routes + Controller-Grundstruktur)
|
||
→ Phase 3 (Views mit Bootstrap, ohne Echtdaten)
|
||
→ Phase 4 (Live-Transaktionsdaten einbauen)
|
||
→ Phase 5 (Log-Viewer)
|
||
→ Phase 8 (Tests für Phasen 1–5)
|
||
→ Phase 6 (Uptime-Check, optional)
|
||
→ Phase 7 (Benachrichtigung, optional)
|
||
```
|
||
|
||
---
|
||
|
||
## Datenbankzugriff auf bestehende Daten – kein Code-Eingriff nötig
|
||
|
||
Phase 4 liest nur lesend aus bestehenden Tabellen. Es sind **keine Änderungen** an:
|
||
- `Api/PayoneController` (PAYONE-Callback-Handler)
|
||
- `ShoppingPayment`, `PaymentTransaction` (Models)
|
||
- bestehenden Migrations
|
||
|
||
---
|
||
|
||
## Navigation (`layout-sidenav.blade.php`)
|
||
|
||
**Datei:** `resources/views/layouts/includes/layout-sidenav.blade.php`
|
||
|
||
Der Eintrag kommt als letztes Item im bestehenden `admin/payments`-Untermenü (nach "Steuerberater",
|
||
aktuell Zeile ~372). **Kein** eigener Top-Level-Eintrag — das Dashboard gehört thematisch zu Payments.
|
||
|
||
```blade
|
||
<li class="sidenav-item{{ Request::is('admin/payment-dashboard*') ? ' active' : '' }}">
|
||
<a href="{{ route('admin.payment-dashboard.index') }}" class="sidenav-link">
|
||
<i class="sidenav-icon ion ion-md-alert"></i>
|
||
<div>{{ __('navigation.payment_monitor') }}</div>
|
||
@php $openIncidentCount = Cache::remember('open_incident_count', 60, fn() =>
|
||
\App\Models\PaymentIncident::whereIn('status', ['open','in_progress','waiting_provider'])->count()
|
||
); @endphp
|
||
@if ($openIncidentCount > 0)
|
||
<div class="pl-1 ml-auto">
|
||
<div class="badge badge-danger">{{ $openIncidentCount }}</div>
|
||
</div>
|
||
@endif
|
||
</a>
|
||
</li>
|
||
```
|
||
|
||
Der Badge ist gecacht (60 Sekunden) um N+1-Queries bei jedem Seitenaufruf zu vermeiden.
|
||
Der Cache wird im Controller bei Incident-Änderungen mit `Cache::forget('open_incident_count')` invalidiert.
|
||
|
||
---
|
||
|
||
## Übersetzungsschlüssel
|
||
|
||
In alle drei Sprachdateien ergänzen:
|
||
|
||
**`resources/lang/de/navigation.php`**
|
||
```php
|
||
'payment_monitor' => 'Payment Monitor',
|
||
```
|
||
|
||
**`resources/lang/en/navigation.php`**
|
||
```php
|
||
'payment_monitor' => 'Payment Monitor',
|
||
```
|
||
|
||
**`resources/lang/es/navigation.php`**
|
||
```php
|
||
'payment_monitor' => 'Monitor de Pagos',
|
||
```
|
||
|
||
---
|
||
|
||
## Form Request Klassen
|
||
|
||
Laut Projektkonventionen: keine Inline-Validierung im Controller.
|
||
|
||
```
|
||
app/Http/Requests/PaymentIncident/
|
||
├── StorePaymentIncidentRequest.php # Validierung für store()
|
||
└── AddIncidentActivityRequest.php # Validierung für addActivity()
|
||
```
|
||
|
||
---
|
||
|
||
## Auto-Incident aus `PayoneController` (Phase 9, optional)
|
||
|
||
Der wertvollste Ausbauschritt: Kritische Fehler im PAYONE-Callback-Handler legen automatisch
|
||
einen Incident an, ohne manuelle Erfassung.
|
||
|
||
**Eingriff in `app/Http/Controllers/Api/PayoneController.php`:**
|
||
|
||
```php
|
||
// Bei Error:2008 (DB-Rollback) → sofort Critical-Incident
|
||
} catch (\Exception $e) {
|
||
\DB::rollBack();
|
||
MyLog::writeLog('payone', 'error', 'Error:2008 ...', [...]);
|
||
|
||
// NEU: Automatischer Critical-Incident
|
||
PaymentIncident::firstOrCreate(
|
||
['type' => 'payment_failure', 'status' => 'open', 'provider' => 'payone',
|
||
'detected_at' => now()->startOfHour()], // deduplication per Stunde
|
||
['title' => 'Automatisch: DB-Fehler bei PAYONE-Callback (Error:2008)',
|
||
'severity' => 'critical', 'ticket_number' => $data['txid'] ?? null]
|
||
);
|
||
}
|
||
```
|
||
|
||
`firstOrCreate` mit Stunden-Deduplication verhindert Duplikate bei mehrfachen Fehlern.
|
||
|
||
---
|
||
|
||
## Offene Fragen vor Start
|
||
|
||
1. **Welcher Admin-User ist "Alois"?** Hat er `admin >= 2`? Falls nicht → eigene schlanke
|
||
Route ohne `admin`-Middleware, geschützt durch separaten Login oder feste User-ID-Prüfung.
|
||
unter Superadmin!
|
||
|
||
2. **PAYONE Health-Endpoint:** Welche URL soll der Uptime-Check prüfen?
|
||
(Phase 6 — kann vorerst übersprungen werden)
|
||
|
||
3. **Mollie/Stripe/PayPal aktiv?** Falls nein → Provider-Enum im ersten Schritt auf
|
||
`payone` + `other` reduzieren, spätere Erweiterung bleibt möglich.
|
||
|
||
4. **Cache-Invalidierung:** `Cache::forget('open_incident_count')` muss in
|
||
`store()`, `updateStatus()` und dem Artisan Command aufgerufen werden.
|