LoginController.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. namespace Controllers;
  3. use Firebase\JWT\JWT;
  4. use Libs\ResponseLib;
  5. use Models\UserModel;
  6. use Services\TshieldService;
  7. use Psr\Http\Message\ServerRequestInterface;
  8. use Respect\Validation\Validator as val;
  9. use Respect\Validation\Exceptions\ValidationException;
  10. class LoginController
  11. {
  12. private UserModel $userModel;
  13. private TshieldService $tshieldService;
  14. public function __construct()
  15. {
  16. $this->userModel = new UserModel();
  17. $this->tshieldService = new TshieldService();
  18. }
  19. public function __invoke(ServerRequestInterface $request)
  20. {
  21. $body = json_decode((string) $request->getBody(), true) ?? [];
  22. try {
  23. val::key('email', val::email())
  24. ->key('password', val::stringType()->notEmpty()->length(8, null))
  25. ->assert($body);
  26. } catch (ValidationException $e) {
  27. return ResponseLib::sendFail("Validation failed: " . $e->getFullMessage(), [], "E_VALIDATE")->withStatus(401);
  28. }
  29. $email = $body['email'];
  30. $password = $body['password'];
  31. $user = $this->userModel->validateLogin($email, $password);
  32. if (!$user) {
  33. return ResponseLib::sendFail("Invalid credentials", [], "E_VALIDATE")->withStatus(401);
  34. }
  35. $kycStatus = (int)($user['user_kyc'] ?? 0);
  36. $roleId = (int)($user['role_id'] ?? 0);
  37. if ($kycStatus === 0) {
  38. if ($roleId === 1) {
  39. return ResponseLib::sendFail(
  40. 'Necessário finalizar análise PJ ou contatar o suporte.',
  41. ['reason' => 'KYC_PJ_PENDING'],
  42. 'E_KYC'
  43. )->withStatus(403);
  44. }
  45. $analysisPayload = [
  46. 'name' => $user['user_name'] ?? $user['user_email'],
  47. 'document' => $this->buildDocumentPayload($user['user_cpf'] ?? ''),
  48. 'email' => $user['user_email'],
  49. 'phone' => $user['user_phone'] ?? '',
  50. 'birthdate' => $this->formatBirthdate($user['user_birthdate'] ?? null),
  51. ];
  52. if (empty($analysisPayload['document']['documentNumber'])) {
  53. return ResponseLib::sendFail(
  54. 'CPF não cadastrado. Contate o suporte para concluir a verificação.',
  55. ['reason' => 'KYC_PF_MISSING_DOCUMENT'],
  56. 'E_KYC'
  57. )->withStatus(403);
  58. }
  59. try {
  60. $tshield = $this->tshieldService->generateIndividualLink(
  61. (int)$user['user_id'],
  62. $analysisPayload
  63. );
  64. } catch (\Throwable $e) {
  65. return ResponseLib::sendFail(
  66. 'Não foi possível gerar o link de verificação: ' . $e->getMessage(),
  67. [],
  68. 'E_EXTERNAL'
  69. )->withStatus(502);
  70. }
  71. return ResponseLib::sendFail(
  72. 'KYC pendente. Conclua a verificação pelo link disponibilizado.',
  73. [
  74. 'link' => $tshield['link'],
  75. 'numberToken' => $tshield['number'],
  76. ],
  77. 'E_KYC'
  78. )->withStatus(403);
  79. }
  80. $payload = [
  81. 'sub' => $user['user_id'],
  82. 'email' => $user['user_email'],
  83. 'iat' => time(),
  84. 'exp' => time() + 3600
  85. ];
  86. $jwt = JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256');
  87. return ResponseLib::sendOk(['token' => $jwt, 'user_id' => $user['user_id'], 'company_id' => $user['company_id']]);
  88. }
  89. private function buildDocumentPayload(?string $cpf): array
  90. {
  91. $number = preg_replace('/\D+/', '', (string)$cpf);
  92. return [
  93. 'documentNumber' => $number,
  94. 'documentType' => 'CPF',
  95. ];
  96. }
  97. private function formatBirthdate($value): ?string
  98. {
  99. if ($value === null || $value === '') {
  100. return null;
  101. }
  102. if (is_numeric($value)) {
  103. $timestamp = (int)$value;
  104. if ($timestamp > 0) {
  105. if (strlen((string)$timestamp) === 8) {
  106. $formatted = \DateTimeImmutable::createFromFormat('Ymd', (string)$timestamp);
  107. if ($formatted) {
  108. return $formatted->format('Y-m-d');
  109. }
  110. }
  111. if ($timestamp >= 1000000000) {
  112. $dt = (new \DateTimeImmutable())->setTimestamp($timestamp);
  113. return $dt->format('Y-m-d');
  114. }
  115. }
  116. }
  117. if (is_string($value) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
  118. return $value;
  119. }
  120. return (string)$value;
  121. }
  122. private function formatAddress(array $user): ?string
  123. {
  124. $parts = array_filter([
  125. $user['user_address'] ?? null,
  126. $user['user_city'] ?? null,
  127. $user['user_state'] ?? null,
  128. $user['user_zip'] ?? null,
  129. $user['user_country'] ?? null,
  130. ]);
  131. return empty($parts) ? null : implode(', ', $parts);
  132. }
  133. }