loadMissing('organization'); $endpoints = WebhookEndpoint::query() ->where('organization_id', $visit->organization_id) ->where('is_active', true) ->get() ->filter(fn (WebhookEndpoint $endpoint) => $endpoint->subscribesTo($event)); if ($endpoints->isEmpty()) { return; } $payload = [ 'event' => $event, 'visit' => [ 'id' => $visit->id, 'public_id' => $visit->public_id, 'external_ref' => $visit->external_ref, 'source' => $visit->source, 'status' => $visit->status, 'visitor_type' => $visit->visitor_type, 'badge_code' => $visit->badge_code, 'checked_in_at' => $visit->checked_in_at?->toIso8601String(), 'checked_out_at' => $visit->checked_out_at?->toIso8601String(), 'visitor_name' => $visit->visitor?->full_name, 'host_name' => $visit->host?->name, ], 'timestamp' => now()->toIso8601String(), ]; foreach ($endpoints as $endpoint) { $this->send($endpoint, $payload); } } /** @param array $payload */ protected function send(WebhookEndpoint $endpoint, array $payload): void { $body = json_encode($payload); $headers = ['Content-Type' => 'application/json']; if ($endpoint->secret) { $headers['X-Frontdesk-Signature'] = hash_hmac('sha256', $body, $endpoint->secret); } try { Http::timeout(10)->withHeaders($headers)->withBody($body, 'application/json')->post($endpoint->url); } catch (\Throwable $e) { Log::warning('Frontdesk webhook delivery failed', [ 'endpoint_id' => $endpoint->id, 'url' => $endpoint->url, 'error' => $e->getMessage(), ]); } } }