| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- <?php
- namespace Controllers;
- use Libs\Logger;
- use Libs\Payload;
- use Models\IntegrationsModel;
- use Models\UnipileMessagesModel;
- use Psr\Http\Message\ServerRequestInterface;
- class UnipileWebhookController
- {
- private IntegrationsModel $integrationsModel;
- private UnipileMessagesModel $messagesModel;
- public function __construct()
- {
- $this->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'));
- }
- }
|