mivita/app/Console/Kernel.php
Kevin 68d230d4e5 Stripe: Kundendaten, Rechnungsnummer, manueller Zahlungseingang
Rueckmeldung der Buchhaltung und des Stripe-Calls (dev/2026-08-26-stripe/):
Stripe kannte bisher nur die E-Mail des Kunden, Zahlungen waren im Dashboard
weder ueber die Bestell- noch ueber die Rechnungsnummer auffindbar, und ein
Zahlungseingang mit falschem Verwendungszweck zwang die Buchhaltung dazu, die
Rechnung von Hand zu schreiben.

- SE1: Der Checkout haengt einen Stripe-Customer mit Name, E-Mail und
  Rechnungsanschrift an die Session (customer statt customer_email); die ID
  liegt an shopping_users und wird wiederverwendet. Namensformat "Vorname
  Nachname (Firma)", reine Firmenkunden unter der Firma. Faellt die
  Customer-Anlage aus, laeuft der Checkout auf dem bisherigen Weg weiter.
  DSGVO-Loeschungen entfernen jetzt auch das Customer-Objekt; die Charges
  bleiben als Buchungsbeleg bestehen.
- SE2: StripeMetadataBuilder als einzige Quelle fuer Metadaten und die
  description (Bestellnummer, Referenz, Kundenname) - damit greift die
  Dashboard-Suche, die bisher nichts Lesbares zu fassen bekam.
- SE3: Die Rechnungsnummer entsteht erst nach der Zahlung und wird daher per
  Job am PaymentIntent nachgetragen (Vorab-Reservierung wuerde Luecken im
  Nummernkreis erzeugen). stripe:backfill-invoice-metadata traegt sie
  rueckwirkend ab dem Go-Live 2026-07-10 nach.
- SE4: Neuer Admin-Button "Zahlungseingang erfassen" an jeder unbezahlten
  Bestellung - bisher gab es ihn nur bei Rechnungskauf, und er setzte nur
  paid, ohne Rechnung, Punkte, Abo-Aktivierung oder Mail. Der neue Weg laeuft
  durch paymentStatusPaidAction, verlangt den Verwendungszweck als Beleg und
  laesst einen abweichenden Betrag nur mit ausdruecklicher Bestaetigung zu.
- S6.4 (aus dem Juli-Plan vorgezogen): stripe:sync-payment-status gleicht
  offene Zahlungen aktiv gegen die API ab. Aufgefallen beim Testlauf - ein nie
  zugestellter Webhook war bisher nicht reparierbar, das Geld lag bei Stripe
  waehrend die Bestellung auf offen stand. Laeuft stuendlich im Scheduler.
- 110 Tests (Pest) fuer alle Pfade, Stripe-Client durchgehend gemockt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 09:04:28 +00:00

111 lines
4.3 KiB
PHP

<?php
namespace App\Console;
use App\Console\Commands\BusinessStore;
use App\Console\Commands\BusinessStoreOptimized;
use App\Console\Commands\CheckPaymentsAccount;
use App\Console\Commands\CheckPaymentUptime;
use App\Console\Commands\DhlUpdateTracking;
use App\Console\Commands\UserCleanup;
use App\Console\Commands\UserMakeAboOrder;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
BusinessStore::class,
BusinessStoreOptimized::class,
CheckPaymentUptime::class,
CheckPaymentsAccount::class,
UserMakeAboOrder::class,
UserCleanup::class,
DhlUpdateTracking::class,
];
/**
* Define the application's command schedule.
*
* @return void
*/
protected function schedule(Schedule $schedule)
{
// Uptime-Check: PAYONE-Erreichbarkeit alle 5 Minuten prüfen
$schedule->command('payment:check-uptime')
->everyFiveMinutes()
->withoutOverlapping()
->runInBackground();
// Job 1: Überprüft täglich um 02:00 Uhr die Zahlungskonten.
$schedule->command('payments:check-accounts')->dailyAt('02:00');
// Jobs 2, 3, 4: Die Befehle aus deinem alten Shell-Skript.
// Werden nacheinander täglich zu unterschiedlichen Zeiten ausgeführt,
// um die Serverlast zu verteilen.
$schedule->command('business:store-optimized 0 0')->dailyAt('03:00');
$schedule->command('user:cleanup')->dailyAt('03:30');
$schedule->command('user:make_abo_order')->dailyAt('04:00');
// Stripe: liegengebliebene Webhook-Events nachverarbeiten (Cron-Race /
// verzögerter Stripe-Retry; Stripe CLI auf dem Test-Server retryt gar nicht).
$schedule->command('stripe:process-pending-events')
->everyTenMinutes()
->withoutOverlapping()
->runInBackground();
// Stripe: offene Zahlungen aktiv gegen die API abgleichen. Fängt den Fall
// ab, dass ein Webhook NIE zugestellt wurde (Endpoint down, falsches
// Konto am Listener, Stripe gibt Retries nach ~3 Tagen auf) — dann läge
// das Geld bei Stripe, während die Bestellung auf "offen" steht.
// Läuft über denselben, idempotenten Paid-Pfad wie ein echter Webhook.
$schedule->command('stripe:sync-payment-status --pending --days=3')
->hourly()
->withoutOverlapping()
->runInBackground();
// Abo-Einmalprodukte: Reminder-Mail vor der Ausführung (Einmalprodukte-Fenster).
// BEWUSST INAKTIV bis zum Abschluss des Review-Gates (Feature aktuell nur VIP
// im Sales Center, Portal ausgeblendet). Nach Freigabe einkommentieren:
$schedule->command('abo:send-execution-reminder')->dailyAt('06:00');
// Abo-Chart-Snapshots: vergangene Monate einfrieren (nach allen Abo-Jobs)
$schedule->command('abo:store-chart-snapshots')->dailyAt('04:30');
// Backoffice-Statistik-Snapshots: abgeschlossene Monate fuer VIP-Statistiken einfrieren
$schedule->command('backoffice:store-statistics-snapshots')->dailyAt('04:45');
// VP-Punkte des Sponsors: fehlende Registrierungs-Buchungen des laufenden
// Monats nachziehen (Callback-Aussetzer), vor business:store-optimized um 03:00
$schedule->command('sales:repair-sponsor-vp')->dailyAt('02:30');
// Incentive: Punkteberechnung täglich nach business:store-optimized
$schedule->command('incentive:calculate')->dailyAt('05:00');
// Cleanup old log files weekly (keeps logs for 30 days)
$schedule->command('logs:cleanup --days=30')->weekly()->sundays()->at('05:00');
// DHL Tracking Update: Stündlich mit status-basierten Intervallen und Batch-API
$schedule->command('dhl:update-tracking --days=30 --send-emails')
->hourly()
->withoutOverlapping()
->runInBackground();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}