| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- <?php
- namespace Controllers;
- use Libs\Logger;
- use Libs\Payload;
- use Libs\Validator;
- use Models\IntegrationsModel;
- use Models\UserModel;
- use Psr\Http\Message\ServerRequestInterface;
- use Services\UnipileClient;
- class UnipileHostedLinkController
- {
- private UserModel $userModel;
- private IntegrationsModel $integrationsModel;
- private UnipileClient $unipileClient;
- public function __construct()
- {
- $this->userModel = new UserModel();
- $this->integrationsModel = new IntegrationsModel();
- $this->unipileClient = new UnipileClient();
- }
- public function __invoke(ServerRequestInterface $request)
- {
- $userId = (int) ($request->getAttribute('user_id') ?? 0);
- $userEmail = (string) ($request->getAttribute('user_email') ?? '');
- $body = json_decode((string) $request->getBody(), true) ?: [];
- $type = mb_strtolower(trim((string) ($body['type'] ?? 'create')));
- $integrationId = (int) ($body['integration_id'] ?? 0);
- if ($userId <= 0) {
- return Payload::fail('Unauthorized: Missing authenticated user', [], 'E_VALIDATE', 401);
- }
- $validator = (new Validator(['type' => $type]))->required('type')->in('type', ['create', 'reconnect']);
- if ($validator->fails()) {
- return Payload::fail($validator->firstError(), [], 'E_VALIDATE', 400);
- }
- if ($type === 'reconnect' && $integrationId <= 0) {
- return Payload::fail('Missing or invalid integration_id', [], 'E_VALIDATE', 400);
- }
- if (!$this->unipileClient->isConfigured()) {
- return Payload::fail('Unipile is not configured', [], 'E_GENERIC', 500);
- }
- if (trim((string) ($_ENV['UNIPILE_NOTIFY_SECRET'] ?? '')) === '') {
- return Payload::fail('UNIPILE_NOTIFY_SECRET is not configured', [], 'E_GENERIC', 500);
- }
- try {
- $companyId = $this->userModel->getCompanyIdByUserId($userId);
- if ($companyId === null) {
- return Payload::fail('User not found', [], 'E_NOT_FOUND', 404);
- }
- $operatorId = $this->integrationsModel->getOperatorIdByUserEmail($companyId, $userEmail);
- $integration = null;
- if ($type === 'reconnect') {
- $integration = $this->integrationsModel->findById($companyId, $integrationId);
- if ($integration === null) {
- return Payload::fail('Integration not found', [], 'E_NOT_FOUND', 404);
- }
- }
- $hostedPayload = [
- 'type' => $type,
- 'providers' => ['WHATSAPP'],
- 'api_url' => $this->unipileClient->apiUrl(),
- 'expiresOn' => gmdate('Y-m-d\TH:i:s.000\Z', time() + 1800),
- 'notify_url' => $this->publicUrl($request, '/v1/webhooks/unipile/hosted-auth') . '?secret=' . rawurlencode((string) ($_ENV['UNIPILE_NOTIFY_SECRET'] ?? '')),
- 'success_redirect_url' => $this->redirectUrl($request, 'UNIPILE_SUCCESS_REDIRECT_URL', '/v1/integrations/unipile/whatsapp/success'),
- 'failure_redirect_url' => $this->redirectUrl($request, 'UNIPILE_FAILURE_REDIRECT_URL', '/v1/integrations/unipile/whatsapp/failure'),
- 'name' => $this->signedName($companyId, $userId, $operatorId, $integrationId),
- ];
- if ($integration !== null) {
- $hostedPayload['account_id'] = $integration['integration_account_id'];
- }
- $response = $this->unipileClient->createHostedAuthLink($hostedPayload);
- $url = (string) ($response['url'] ?? '');
- if ($url === '') {
- Logger::warning('Unipile hosted auth response missing url', ['response' => $response]);
- return Payload::fail('Failed to create hosted auth link', [], 'E_GENERIC', 502);
- }
- return Payload::ok(['url' => $url]);
- } catch (\Throwable $e) {
- Logger::error('Failed to create Unipile hosted auth link', ['error' => $e->getMessage()]);
- return Payload::fail('Failed to create hosted auth link', [], 'E_GENERIC', 500);
- }
- }
- private function signedName(int $companyId, int $userId, int $operatorId, int $integrationId): string
- {
- $secret = (string) ($_ENV['JWT_SECRET'] ?? '');
- $payload = [
- 'company_id' => $companyId,
- 'user_id' => $userId,
- 'operator_id' => $operatorId,
- 'integration_id' => $integrationId,
- 'nonce' => bin2hex(random_bytes(12)),
- 'iat' => time(),
- ];
- $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
- if ($json === false) {
- $json = '{}';
- }
- $encoded = rtrim(strtr(base64_encode($json), '+/', '-_'), '=');
- $signature = hash_hmac('sha256', $encoded, $secret);
- return $encoded . '.' . $signature;
- }
- private function redirectUrl(ServerRequestInterface $request, string $envKey, string $fallbackPath): string
- {
- $configured = trim((string) ($_ENV[$envKey] ?? ''));
- if ($configured !== '') {
- return $configured;
- }
- return $this->publicUrl($request, $fallbackPath);
- }
- private function publicUrl(ServerRequestInterface $request, string $path): string
- {
- $configured = rtrim(trim((string) ($_ENV['APP_PUBLIC_URL'] ?? '')), '/');
- if ($configured !== '') {
- return $configured . $path;
- }
- $host = $request->getHeaderLine('Host');
- if ($host === '') {
- $host = 'localhost:8080';
- }
- return 'https://' . $host . $path;
- }
- }
|