| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- <?php
- namespace Controllers;
- use Libs\Logger;
- use Libs\Payload;
- use Models\IntegrationsModel;
- use Psr\Http\Message\ServerRequestInterface;
- class UnipileHostedAuthWebhookController
- {
- private IntegrationsModel $integrationsModel;
- public function __construct()
- {
- $this->integrationsModel = new IntegrationsModel();
- }
- public function __invoke(ServerRequestInterface $request)
- {
- if (!$this->isAuthorized($request)) {
- return Payload::fail('Unauthorized', [], 'E_VALIDATE', 401);
- }
- $body = json_decode((string) $request->getBody(), true) ?: [];
- $status = (string) ($body['status'] ?? '');
- $accountId = (string) ($body['account_id'] ?? '');
- $name = (string) ($body['name'] ?? '');
- if ($status === '' || $accountId === '' || $name === '') {
- return Payload::fail('Invalid callback payload', [], 'E_VALIDATE', 400);
- }
- $identity = $this->decodeName($name);
- if ($identity === null) {
- return Payload::fail('Invalid callback identity', [], 'E_VALIDATE', 400);
- }
- try {
- $integration = $this->integrationsModel->upsertWhatsappIntegration(
- (int) $identity['company_id'],
- (int) $identity['user_id'],
- (int) $identity['operator_id'],
- $accountId,
- $status,
- $body
- );
- return Payload::ok(['integration' => $this->integrationsModel->formatIntegration($integration)]);
- } catch (\Throwable $e) {
- Logger::error('Failed to process Unipile hosted auth callback', ['error' => $e->getMessage()]);
- return Payload::fail('Failed to process callback', [], 'E_GENERIC', 500);
- }
- }
- private function isAuthorized(ServerRequestInterface $request): bool
- {
- $expected = (string) ($_ENV['UNIPILE_NOTIFY_SECRET'] ?? '');
- if ($expected === '') {
- return false;
- }
- $provided = (string) ($request->getQueryParams()['secret'] ?? '');
- if ($provided === '') {
- $provided = $request->getHeaderLine('Unipile-Notify-Secret');
- }
- return hash_equals($expected, $provided);
- }
- private function decodeName(string $name): ?array
- {
- $parts = explode('.', $name, 2);
- if (count($parts) !== 2) {
- return null;
- }
- [$encoded, $signature] = $parts;
- $secret = (string) ($_ENV['JWT_SECRET'] ?? '');
- $expected = hash_hmac('sha256', $encoded, $secret);
- if (!hash_equals($expected, $signature)) {
- return null;
- }
- $padded = str_pad(strtr($encoded, '-_', '+/'), strlen($encoded) % 4 === 0 ? strlen($encoded) : strlen($encoded) + 4 - strlen($encoded) % 4, '=', STR_PAD_RIGHT);
- $json = base64_decode($padded, true);
- if ($json === false) {
- return null;
- }
- $payload = json_decode($json, true);
- if (!is_array($payload)) {
- return null;
- }
- foreach (['company_id', 'user_id', 'operator_id'] as $key) {
- if (!isset($payload[$key]) || (int) $payload[$key] < 0) {
- return null;
- }
- }
- return $payload;
- }
- }
|