integrationsModel = new IntegrationsModel(); $this->messagesModel = new UnipileMessagesModel(); } public function __invoke(ServerRequestInterface $request) { if (!$this->isAuthorized($request)) { return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401); } $payload = json_decode((string) $request->getBody(), true) ?: []; if ($payload === []) { return Payload::fail('Invalid webhook payload', [], 'E_VALIDATE', 400); } try { if (isset($payload['AccountStatus']) && is_array($payload['AccountStatus'])) { return $this->handleAccountStatus($payload); } return $this->handleMessageEvent($payload); } catch (\Throwable $e) { Logger::error('Failed to process Unipile webhook', ['error' => $e->getMessage()]); return Payload::fail('Failed to process webhook', [], 'E_GENERIC', 500); } } private function handleAccountStatus(array $payload) { $statusPayload = $payload['AccountStatus']; $accountId = (string) ($statusPayload['account_id'] ?? ''); $status = (string) ($statusPayload['message'] ?? ''); if ($accountId === '' || $status === '') { return Payload::fail('Invalid account status payload', [], 'E_VALIDATE', 400); } $integration = $this->integrationsModel->updateStatusByAccountId($accountId, $status, $payload); if ($integration !== null) { $this->messagesModel->storeWebhookEvent( (int) $integration['integration_id'], 'account_status', $accountId . ':' . $status, $payload, true ); } return Payload::ok(); } private function handleMessageEvent(array $payload) { $accountId = (string) ($payload['account_id'] ?? ''); $accountType = mb_strtoupper(trim((string) ($payload['account_type'] ?? ''))); $event = (string) ($payload['event'] ?? 'message_received'); if ($accountId === '' || $accountType !== 'WHATSAPP') { return Payload::ok(); } $integration = $this->integrationsModel->findByAccountId($accountId); if ($integration === null) { Logger::warning('Unipile webhook account not found', ['account_id' => $accountId, 'event' => $event]); return Payload::ok(); } $externalId = (string) ($payload['message_id'] ?? $payload['chat_id'] ?? hash('sha256', json_encode($payload))); $processed = false; if (in_array($event, ['message_received', 'message_edited', 'message_deleted', 'message_delivered', 'message_read'], true)) { $processed = $this->messagesModel->upsertMessageFromWebhook($integration, $payload) !== null; } $this->messagesModel->storeWebhookEvent((int) $integration['integration_id'], $event, $externalId, $payload, $processed); return Payload::ok(); } private function isAuthorized(ServerRequestInterface $request): bool { $expected = (string) ($_ENV['UNIPILE_WEBHOOK_SECRET'] ?? ''); if ($expected === '') { return false; } return hash_equals($expected, $request->getHeaderLine('Unipile-Auth')); } }