buildStatusResponse(); $response = Http::timeout(5)->post($job->webhook_url, $payload); if ($response->successful()) { // Reset webhook error fields on success $job->update([ 'webhook_attempts' => 0, 'webhook_last_error' => null, 'webhook_next_retry_at' => null ]); } else { throw new \Exception("HTTP {$response->status()}: {$response->body()}"); } } catch (\Exception $e) { self::handleWebhookFailure($job, $e->getMessage()); } } private static function handleWebhookFailure(CrawlShotJob $job, string $error): void { $currentAttempts = $job->webhook_attempts ?? 0; if ($currentAttempts < 6) { $delayMinutes = self::RETRY_DELAYS[$currentAttempts]; $nextRetryAt = now()->addMinutes($delayMinutes); $job->update([ 'webhook_attempts' => $currentAttempts + 1, 'webhook_last_error' => $error, 'webhook_next_retry_at' => $nextRetryAt ]); // Schedule retry job RetryWebhookJob::dispatch($job->uuid)->delay($nextRetryAt); } else { // Max attempts reached, just update error $job->update([ 'webhook_attempts' => $currentAttempts + 1, 'webhook_last_error' => $error, 'webhook_next_retry_at' => null ]); } } }