$payload */ public function __construct( public Webhook $webhook, public string $eventType, public array $payload, public bool $force = false, ) { $this->onQueue('webhooks'); $this->logId = (string) Str::uuid(); } public function handle(SafeHttpFetcher $safeHttp, WebhookService $webhooks): void { $this->webhook->refresh(); if (! $this->force && $this->webhook->status !== Status::Enabled) { return; } $body = [ 'id' => $this->logId, 'type' => $this->eventType, 'data' => $this->payload, 'created_at' => now()->toIso8601String(), ]; $signature = $webhooks->signature($body, $this->webhook->signing_secret); $log = WebhookLog::query()->find($this->logId); if ($log) { $log->update([ 'payload' => $body, 'attempts' => $this->attempts(), 'failed_at' => null, 'response_status' => null, 'response_body' => null, 'delivered_at' => null, ]); } else { $log = new WebhookLog([ 'webhook_id' => $this->webhook->id, 'event_type' => $this->eventType, 'payload' => $body, 'attempts' => $this->attempts(), ]); $log->id = $this->logId; $log->save(); } try { $safeHttp->guardAgainstSsrf($this->webhook->endpoint); } catch (RuntimeException $e) { $log->update([ 'failed_at' => now(), 'response_body' => $e->getMessage(), ]); $log->refresh(); LogUpdated::dispatch($log); throw $e; } try { $response = Http::timeout(10) ->withUserAgent(config('trypost.user_agent')) ->withOptions(['allow_redirects' => false]) ->asJson() ->withHeaders([ 'X-Webhook-Signature' => $signature, ]) ->post($this->webhook->endpoint, $body); $responseBody = substr($response->body(), 0, 2000); if ($response->successful()) { $log->update([ 'response_status' => $response->status(), 'response_body' => $responseBody, 'delivered_at' => now(), ]); $this->webhook->update(['last_sent_at' => now()]); $this->webhook->resetConsecutiveFailures(); $log->refresh(); LogUpdated::dispatch($log); return; } $log->update([ 'response_status' => $response->status(), 'response_body' => $responseBody, 'failed_at' => now(), ]); $log->refresh(); LogUpdated::dispatch($log); throw new RuntimeException("Webhook delivery failed with status: {$response->status()}"); } catch (ConnectionException $e) { $log->update([ 'failed_at' => now(), 'response_body' => $e->getMessage(), ]); $log->refresh(); LogUpdated::dispatch($log); throw $e; } } public function failed(?Throwable $exception): void { $this->webhook->refresh(); if ($this->webhook->status !== Status::Enabled) { return; } $this->webhook->increment('consecutive_failures'); $this->webhook->refresh(); if ($this->webhook->consecutive_failures >= 5) { $this->webhook->pause(); $owner = $this->webhook->workspace?->account?->owner; if ($owner?->email) { Mail::to($owner->email)->send(new WebhookPausedMail($this->webhook)); } } } }