23-01-2026
This commit is contained in:
parent
a939cd51ef
commit
a8b395e20d
248 changed files with 29342 additions and 4805 deletions
|
|
@ -12,6 +12,7 @@ use App\Services\AboHelper;
|
|||
use App\Models\UserAboOrder;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UserMakeAboOrder extends Command
|
||||
{
|
||||
|
|
@ -87,12 +88,17 @@ class UserMakeAboOrder extends Command
|
|||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Suche nach fälligen Abos für Datum', ['date' => $dateNow]);
|
||||
|
||||
// Prüfe auf bereits verarbeitete Abos am heutigen Tag (Duplikatsprüfung)
|
||||
$userAbos = UserAbo::where('next_date', '=', $dateNow)
|
||||
->where('active', true)
|
||||
->where('status', '=', 2) //abo_okay
|
||||
->whereDoesntHave('user_abo_orders', function ($query) use ($dateNow) {
|
||||
$query->whereDate('created_at', $dateNow);
|
||||
})
|
||||
->get();
|
||||
|
||||
$count = $userAbos->count();
|
||||
\Log::channel('abo_order')->info("UserMakeAboOrder: {$count} fällige Abos gefunden");
|
||||
\Log::channel('abo_order')->info("UserMakeAboOrder: {$count} fällige Abos gefunden (ohne bereits verarbeitete)");
|
||||
$this->info("Gefundene fällige Abos: {$count}");
|
||||
|
||||
foreach ($userAbos as $userAbo) {
|
||||
|
|
@ -104,7 +110,38 @@ class UserMakeAboOrder extends Command
|
|||
$this->info("Verarbeite Abo: {$userAbo->id} (PayoneUserid: {$userAbo->payone_userid})");
|
||||
|
||||
try {
|
||||
$shoppingOrder = $this->makeOrder($userAbo);
|
||||
// Locking-Mechanismus: Verhindert Race Conditions bei paralleler Ausführung
|
||||
$shoppingOrder = DB::transaction(function () use ($userAbo, $dateNow) {
|
||||
// Lock das Abo für Update, um Race Conditions zu vermeiden
|
||||
$lockedAbo = UserAbo::where('id', $userAbo->id)
|
||||
->where('next_date', '=', $dateNow)
|
||||
->where('active', true)
|
||||
->where('status', '=', 2) //abo_okay
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if (!$lockedAbo) {
|
||||
\Log::channel('abo_order')->warning('UserMakeAboOrder: Abo wurde bereits verarbeitet oder ist nicht mehr aktiv', [
|
||||
'abo_id' => $userAbo->id
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Nochmalige Prüfung auf Duplikat innerhalb der Transaktion
|
||||
$existingOrder = UserAboOrder::where('user_abo_id', $lockedAbo->id)
|
||||
->whereDate('created_at', $dateNow)
|
||||
->first();
|
||||
|
||||
if ($existingOrder) {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo wurde bereits heute verarbeitet', [
|
||||
'abo_id' => $lockedAbo->id,
|
||||
'existing_order_id' => $existingOrder->shopping_order_id
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->makeOrder($lockedAbo);
|
||||
}, 3); // 3 Versuche bei Deadlocks
|
||||
|
||||
if ($shoppingOrder) {
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Bestellung erstellt', [
|
||||
|
|
@ -163,6 +200,11 @@ class UserMakeAboOrder extends Command
|
|||
$response = $userOrder->makePayment();
|
||||
$this->info('makePayment response: ' . json_encode($response));
|
||||
|
||||
// Prüfe ob Response ein Array ist (kann auch Objekt sein)
|
||||
if (is_object($response)) {
|
||||
$response = (array) $response;
|
||||
}
|
||||
|
||||
if (!isset($response['status'])) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Ungültige Zahlungsantwort', [
|
||||
'abo_id' => $userAbo->id,
|
||||
|
|
@ -170,6 +212,10 @@ class UserMakeAboOrder extends Command
|
|||
'response' => $response
|
||||
]);
|
||||
$this->error("Ungültige Zahlungsantwort für Abo {$userAbo->id}");
|
||||
|
||||
// Bei fehlender Status-Information: Abo nicht aktualisieren, damit es beim nächsten Lauf erneut versucht wird
|
||||
// Aber Bestellung speichern für Nachverfolgung
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Ungültige Zahlungsantwort - kein Status');
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +226,7 @@ class UserMakeAboOrder extends Command
|
|||
'response' => $response
|
||||
]);
|
||||
$this->info("Zahlung erfolgreich für Abo {$userAbo->id}");
|
||||
// Nur bei erfolgreicher Zahlung: next_date aktualisieren
|
||||
$this->updateAbo($userAbo, $shoppingOrder, 1);
|
||||
} elseif ($response['status'] === 'ERROR') {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Zahlungsfehler', [
|
||||
|
|
@ -196,24 +243,39 @@ class UserMakeAboOrder extends Command
|
|||
$response
|
||||
);
|
||||
|
||||
$this->updateAbo($userAbo, $shoppingOrder, 3);
|
||||
// Bei Zahlungsfehler: Status setzen, aber next_date NICHT aktualisieren
|
||||
// Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 3, $response);
|
||||
|
||||
$shoppingPayment = $userOrder->getShoppingPayment();
|
||||
$data = [
|
||||
'mode' => $shoppingPayment->mode,
|
||||
'txaction' => 'error',
|
||||
'send_link' => false,
|
||||
'payment_error' => $response,
|
||||
];
|
||||
if ($shoppingPayment) {
|
||||
$data = [
|
||||
'mode' => $shoppingPayment->mode,
|
||||
'txaction' => 'error',
|
||||
'send_link' => false,
|
||||
'payment_error' => $response,
|
||||
];
|
||||
|
||||
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, $data);
|
||||
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, $data);
|
||||
}
|
||||
} elseif ($response['status'] === 'PENDING' || $response['status'] === 'REDIRECT') {
|
||||
// Pending/Redirect Status: Bestellung speichern, aber Abo nicht aktualisieren
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Zahlung ausstehend/weiterleitung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $response['status']
|
||||
]);
|
||||
$this->info("Zahlung ausstehend für Abo {$userAbo->id}: {$response['status']}");
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Zahlung ausstehend: ' . $response['status']);
|
||||
} else {
|
||||
// Unbekannter Status: Bestellung speichern, aber Abo nicht aktualisieren
|
||||
\Log::channel('abo_order')->warning('UserMakeAboOrder: Unbekannter Zahlungsstatus', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $response['status']
|
||||
]);
|
||||
$this->warn("Unbekannter Zahlungsstatus für Abo {$userAbo->id}: {$response['status']}");
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Unbekannter Status: ' . $response['status']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Ausnahme bei der Bestellungserstellung', [
|
||||
|
|
@ -222,13 +284,19 @@ class UserMakeAboOrder extends Command
|
|||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
$this->error("Ausnahme bei Abo {$userAbo->id}: " . $e->getMessage());
|
||||
|
||||
// Bei Exception: Bestellung speichern falls vorhanden, aber Abo nicht aktualisieren
|
||||
if ($shoppingOrder) {
|
||||
$this->updateAboOnError($userAbo, $shoppingOrder, 'Exception: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $shoppingOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Abo nach einer Bestellung
|
||||
* Aktualisiert das Abo nach einer erfolgreichen Bestellung
|
||||
* Aktualisiert next_date für den nächsten Abo-Zyklus
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param mixed $shoppingOrder
|
||||
|
|
@ -237,7 +305,7 @@ class UserMakeAboOrder extends Command
|
|||
*/
|
||||
private function updateAbo($userAbo, $shoppingOrder, $status = 1)
|
||||
{
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo', [
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo nach erfolgreicher Zahlung', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $status
|
||||
|
|
@ -245,34 +313,98 @@ class UserMakeAboOrder extends Command
|
|||
|
||||
$this->info("Aktualisiere Abo: {$userAbo->id} mit Status {$status}");
|
||||
|
||||
$updateData = [
|
||||
'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval),
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
if ($status !== 1) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
try {
|
||||
$userAbo->update($updateData);
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder, $status) {
|
||||
$updateData = [
|
||||
'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval),
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
]);
|
||||
if ($status !== 1) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo erfolgreich aktualisiert', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'next_date' => $updateData['next_date']
|
||||
]);
|
||||
$userAbo->update($updateData);
|
||||
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $status,
|
||||
'paid' => false,
|
||||
]);
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo erfolgreich aktualisiert', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'next_date' => $updateData['next_date']
|
||||
]);
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler beim Aktualisieren des Abos', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
$this->error("Fehler beim Aktualisieren des Abos {$userAbo->id}: " . $e->getMessage());
|
||||
throw $e; // Re-throw für besseres Error-Handling
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Abo bei Fehlern - OHNE next_date zu aktualisieren
|
||||
* Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
*
|
||||
* @param UserAbo $userAbo
|
||||
* @param mixed $shoppingOrder
|
||||
* @param int|string $status Status-Code oder Fehlermeldung
|
||||
* @param array|null $errorResponse Optionale Fehlerantwort von Payment
|
||||
* @return void
|
||||
*/
|
||||
private function updateAboOnError($userAbo, $shoppingOrder, $status, $errorResponse = null)
|
||||
{
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo bei Fehler (ohne next_date)', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'order_id' => $shoppingOrder->id,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
$this->info("Aktualisiere Abo bei Fehler: {$userAbo->id} (Status: {$status})");
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($userAbo, $shoppingOrder, $status) {
|
||||
// Nur last_date aktualisieren, next_date bleibt unverändert
|
||||
// Damit wird das Abo beim nächsten Cron-Lauf erneut versucht
|
||||
$updateData = [
|
||||
'last_date' => now(),
|
||||
];
|
||||
|
||||
// Status nur setzen wenn es ein numerischer Wert ist
|
||||
if (is_numeric($status)) {
|
||||
$updateData['status'] = $status;
|
||||
}
|
||||
|
||||
$userAbo->update($updateData);
|
||||
|
||||
// UserAboOrder mit Fehlerstatus speichern
|
||||
$orderStatus = is_numeric($status) ? $status : 3; // Default zu 3 (abo_hold) wenn String
|
||||
UserAboOrder::create([
|
||||
'user_abo_id' => $userAbo->id,
|
||||
'shopping_order_id' => $shoppingOrder->id,
|
||||
'status' => $orderStatus,
|
||||
'paid' => false,
|
||||
]);
|
||||
|
||||
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo bei Fehler aktualisiert (next_date unverändert)', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'next_date' => $userAbo->next_date,
|
||||
'status' => $status
|
||||
]);
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler beim Aktualisieren des Abos bei Fehler', [
|
||||
'abo_id' => $userAbo->id,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
$this->error("Fehler beim Aktualisieren des Abos {$userAbo->id}: " . $e->getMessage());
|
||||
// Bei Fehler hier nicht re-throw, damit der Hauptprozess fortgesetzt werden kann
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue