resolveAbo(); if (! $abo) { return self::FAILURE; } $this->info('=== Abo-Recovery-Test ==='); $this->showAbo($abo, 'Ausgangszustand'); if ($this->option('fail')) { $abo->forceFill(['status' => 3, 'active' => true])->save(); $this->warn("Abo #{$abo->id} auf Status 3 (fehlgeschlagen) + aktiv gesetzt."); } if (! $this->option('no-mail')) { $this->sendMail($abo); } if ($this->option('advance')) { $this->simulateRecovery($abo); } $this->newLine(); $this->info('Fertig.'); return self::SUCCESS; } private function resolveAbo(): ?UserAbo { if ($id = $this->option('abo')) { $abo = UserAbo::find($id); if (! $abo) { $this->error("Abo mit ID {$id} nicht gefunden."); return null; } return $abo; } $abo = UserAbo::where('status', '>=', 1)->orderByDesc('id')->first(); if (! $abo) { $this->error('Kein Abo gefunden. Bitte --abo= angeben.'); return null; } $this->line("Kein --abo angegeben, nutze neuestes Abo #{$abo->id}."); return $abo; } private function sendMail(UserAbo $abo): void { $recipient = $this->option('email') ?: $abo->email; if (! $recipient) { $this->warn('Keine Empfänger-Adresse (Abo ohne E-Mail, kein --email). Mail übersprungen.'); return; } $link = (new AboPaymentFailedMail($abo))->build()->viewData['link'] ?? '(kein Link)'; $locale = $abo->shopping_user?->getLocale() ?: config('app.locale'); Mail::to($recipient)->locale($locale)->send(new AboPaymentFailedMail($abo)); $this->newLine(); $this->info('✓ "Zahlung fehlgeschlagen"-Mail versendet'); $this->table(['Feld', 'Wert'], [ ['Empfänger', $recipient], ['Sprache', $locale], ['Abo-Typ', $abo->is_for.($abo->is_for === 'me' ? ' (Berater-CRM)' : ' (Kundenportal)')], ['Pay-now-Link', $link], ]); $this->line('Tipp: Mail im Mailpit prüfen (mivita-mail.test).'); } private function simulateRecovery(UserAbo $abo): void { $this->newLine(); $this->warn('ACHTUNG: --advance überschreibt payone_userid/carddata dieses Abos mit TEST-Werten.'); $this->warn('Das echte Abbuchungs-Token geht dabei verloren – nur auf Wegwerf-/Test-Abos ausführen!'); if (! $this->option('force') && ! $this->confirm("Trotzdem Recovery-Zahlung für Abo #{$abo->id} simulieren?", false)) { $this->line('Simulation abgebrochen.'); return; } $this->warn('Simuliere erfolgreiche Recovery-Zahlung (erzeugt Test-Order/-Payment, KEIN echter Payone-Call)...'); $reference = $abo->shopping_user?->shopping_orders()->orderByDesc('id')->first(); $newUserId = (int) $abo->payone_userid + 1; // sichtbar geändertes Token $fakeCard = ['cardpan' => '411111XXXXXX'.random_int(1000, 9999), 'cardtype' => 'V', 'cardexpiredate' => '3012']; $order = ShoppingOrder::create([ 'shopping_user_id' => $abo->shopping_user_id ?: 1, 'auth_user_id' => $abo->user_id ?: ($reference->auth_user_id ?? 1), 'country_id' => $reference->country_id ?? 1, 'user_shop_id' => $reference->user_shop_id ?? 1, 'recovery_user_abo_id' => $abo->id, 'is_abo' => true, 'abo_interval' => $abo->abo_interval, 'paid' => true, 'mode' => 'test', ]); $payment = ShoppingPayment::create([ 'shopping_order_id' => $order->id, 'clearingtype' => 'cc', 'wallettype' => null, 'carddata' => $fakeCard, 'reference' => 'recovery-test-'.uniqid(), 'amount' => 19.9, 'currency' => 'EUR', 'mode' => 'test', 'is_abo' => true, 'abo_interval' => $abo->abo_interval, ]); PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'userid' => $newUserId, 'txid' => 'test-'.uniqid(), 'status' => 'APPROVED', 'txaction' => 'appointed', 'mode' => 'test', ]); AboHelper::advanceAboAfterRecoveryPayment($payment->fresh()); $this->showAbo($abo->fresh(), 'Nach Recovery-Zahlung'); $this->info('✓ Abo fortgeschrieben (Status→2, next_date gesetzt, Zahlart/Token aktualisiert).'); } private function showAbo(UserAbo $abo, string $title): void { $this->newLine(); $this->line("{$title} — Abo #{$abo->id}"); $this->table(['Feld', 'Wert'], [ ['Status', $abo->status.' ('.($abo->getStatusType() ?? '?').')'], ['Aktiv', $abo->active ? 'Ja' : 'Nein'], ['is_for', $abo->is_for], ['E-Mail', $abo->email], ['clearingtype', $abo->clearingtype ?? '-'], ['wallettype', $abo->wallettype ?? '-'], ['payone_userid', $abo->payone_userid], ['carddata (maskiert)', $abo->maskedCard() ?? '-'], ['next_date', $abo->next_date], ['last_date', $abo->last_date], ]); } }