option('dry-run'); if ($isDryRun) { $this->info('🔍 DRY RUN MODE - No emails will be sent'); } $this->info('🚀 Starting abandoned checkout reminder process...'); // Reminder-Stufen definieren: [Stufe, Stunden, Beschreibung] $reminderStages = [ ['1h', 1, '1 hour reminders'], ['24h', 24, '24 hour reminders'], ['1w', 168, '1 week reminders'], // 7 * 24 = 168 Stunden ]; $totalProcessed = 0; $totalSent = 0; foreach ($reminderStages as [$stage, $hours, $description]) { $this->info("📧 Processing {$description}..."); $checkouts = AbandonedCheckoutModel::readyForReminder($stage, $hours) ->with(['user', 'package']) ->get(); $this->info(" Found {$checkouts->count()} checkouts ready for {$stage} reminder"); foreach ($checkouts as $checkout) { try { if ($this->shouldSendReminder($checkout, $stage)) { $resumeUrl = $this->generateResumeUrl($checkout); if (! $isDryRun) { $mailLocale = $checkout->user->preferred_locale ?? config('app.locale'); Mail::to($checkout->user) ->locale($mailLocale) ->queue( new AbandonedCheckout( $checkout->user, $checkout->package, $stage, $resumeUrl ) ); $checkout->updateReminderStage($stage); $totalSent++; } else { $packageName = $checkout->package->getNameForLocale($checkout->user->preferred_locale ?? config('app.locale')); $this->line(" 📧 Would send {$stage} reminder to: {$checkout->email} for package: {$packageName}"); } $totalProcessed++; } } catch (Throwable $e) { Log::error("Failed to send {$stage} reminder for checkout {$checkout->id}: ".$e->getMessage()); $this->error(" ❌ Failed to process checkout {$checkout->id}: ".$e->getMessage()); } } } // Cleanup: Alte Checkouts löschen (älter als 30 Tage) $oldCheckouts = AbandonedCheckoutModel::where('abandoned_at', '<', now()->subDays(30)) ->where('converted', false) ->count(); if ($oldCheckouts > 0) { if (! $isDryRun) { AbandonedCheckoutModel::where('abandoned_at', '<', now()->subDays(30)) ->where('converted', false) ->delete(); $this->info("🧹 Cleaned up {$oldCheckouts} old abandoned checkouts"); } else { $this->info("🧹 Would clean up {$oldCheckouts} old abandoned checkouts"); } } $this->info('✅ Reminder process completed!'); $this->info(" Processed: {$totalProcessed} checkouts"); if (! $isDryRun) { $this->info(" Sent: {$totalSent} reminder emails"); } else { $this->info(" Would send: {$totalSent} reminder emails"); } return Command::SUCCESS; } /** * Prüft ob ein Reminder versendet werden sollte */ private function shouldSendReminder(AbandonedCheckoutModel $checkout, string $stage): bool { // Verfällt der Checkout bald? Dann kein Reminder mehr if ($checkout->isExpired()) { return false; } // User existiert noch? if (! $checkout->user) { return false; } // Package existiert noch? if (! $checkout->package) { return false; } return true; } /** * Generiert die URL zum Wiederaufnehmen des Checkouts */ private function generateResumeUrl(AbandonedCheckoutModel $checkout): string { // Für jetzt: Einfache Package-URL // Später: Persönliche Resume-Token URLs return route('buy.packages', [ 'locale' => config('app.locale', 'de'), 'packageId' => $checkout->package_id, ]); } }