/home/alixis5/integrate.sellofinance.com/src/Services
Edit: /home/alixis5/integrate.sellofinance.com/src/Services/GoogleAuthService.php (8285B)
verifier->verify($idToken);
$email = $claims->email ?? '';
$name = $claims->name ?? '';
$googleUid = $claims->sub;
if (empty($email)) {
throw new \RuntimeException('Google account has no email address', 422);
}
// 2. Try to find existing user
$appUser = $this->appUserRepo->findByEmail($email);
if ($appUser !== null) {
// Existing user — treat as login
if ($appUser->status === AppUser::STATUS_PENDING) {
$appUser = $this->appUserRepo->update($appUser->id, [
'status' => AppUser::STATUS_ACTIVE,
'emailVerified' => true,
]);
}
if (in_array($appUser->status, [AppUser::STATUS_SUSPENDED, AppUser::STATUS_LOCKED], true)) {
throw new \RuntimeException('ACCOUNT_' . strtoupper($appUser->status));
}
$this->appUserRepo->recordSuccessfulLogin($appUser->id);
$profile = $this->profileRepo->findByAppUserId($appUser->id);
$token = $this->jwt->issue([
'sub' => $appUser->id,
'accountId' => $appUser->accountId,
'profileId' => $profile?->id,
'userType' => $appUser->userType,
]);
return [
'token' => $token,
'expiresIn' => (int) $_ENV['JWT_EXPIRY_SECONDS'],
'user' => $this->buildUserResponse($appUser, $profile),
];
}
// 3. New user — register them
return $this->createGoogleUser($email, $name, $googleUid);
}
// ── Private ─────────────────────────────────────────────────────
private function createGoogleUser(string $email, string $displayName, string $googleUid): array
{
[$firstName, $lastName] = $this->splitName($displayName ?: strstr($email, '@', true) ?: 'User');
$accountId = null;
$appUserId = null;
try {
$account = $this->accountRepo->create([
'name' => trim($firstName . ' ' . $lastName),
'type' => Account::TYPE_CUSTOMER,
'emailAddress' => $email,
]);
$accountId = $account->id;
$appUser = $this->appUserRepo->create([
'firstName' => $firstName,
'lastName' => $lastName ?: $firstName,
'emailAddress' => $email,
'password' => password_hash(bin2hex(random_bytes(24)), PASSWORD_BCRYPT, ['cost' => 12]),
'userType' => AppUser::TYPE_CUSTOMER,
'status' => AppUser::STATUS_ACTIVE,
'signupType' => AppUser::SIGNUP_GOOGLE,
'preferredLanguage' => AppUser::LANG_EN,
'emailVerified' => true,
'mobileVerified' => false,
'mfaEnabled' => false,
'externalRef' => $googleUid,
'accountId' => $accountId,
]);
$appUserId = $appUser->id;
$profile = $this->profileRepo->create([
'name' => trim($firstName . ' ' . $lastName),
'appUserId' => $appUserId,
'accountId' => $accountId,
'profileType' => CustomerProfile::TYPE_INDIVIDUAL,
'status' => CustomerProfile::STATUS_DRAFT,
'onboardingStatus' => CustomerProfile::ONBOARDING_NOT_STARTED,
'preferredCurrency' => 'AED',
'creditConsentGiven' => false,
'marketingConsentGiven' => false,
'existingBankCustomer' => false,
]);
$token = $this->jwt->issue([
'sub' => $appUserId,
'accountId' => $accountId,
'profileId' => $profile->id,
'userType' => AppUser::TYPE_CUSTOMER,
]);
return [
'token' => $token,
'expiresIn' => (int) $_ENV['JWT_EXPIRY_SECONDS'],
'user' => $this->buildUserResponse($appUser, $profile),
];
} catch (EspoDuplicateException $e) {
// Duplicate on Account (email) or AppUser (email/phone).
// For Google auth, a duplicate email always means the email is already registered.
if ($appUserId) { try { $this->appUserRepo->delete($appUserId); } catch (\Throwable) {} }
if ($accountId) { try { $this->accountRepo->delete($accountId); } catch (\Throwable) {} }
throw new \RuntimeException('DUPLICATE_EMAIL', 409, $e);
} catch (EspoApiException $e) {
if ($appUserId) { try { $this->appUserRepo->delete($appUserId); } catch (\Throwable) {} }
if ($accountId) { try { $this->accountRepo->delete($accountId); } catch (\Throwable) {} }
throw new \RuntimeException($e->getMessage(), (int) $e->getCode(), $e);
}
}
private function buildUserResponse(AppUser $user, ?CustomerProfile $profile): array
{
return [
'id' => $user->id,
'accountId' => $user->accountId,
'firstName' => $user->firstName,
'lastName' => $user->lastName,
'emailAddress' => $user->emailAddress,
'phoneNumber' => $user->phoneNumber,
'emailVerified' => $user->emailVerified,
'mobileVerified' => $user->mobileVerified,
'userType' => $user->userType,
'status' => $user->status,
'preferredLanguage' => $user->preferredLanguage,
'ref' => $user->ref,
'createdAt' => $user->createdAt,
'profileCompletion' => $profile?->profileCompletenessPercent ?? 0,
'profile' => $profile ? [
'id' => $profile->id,
'onboardingStatus' => $profile->onboardingStatus,
'onboardingStep' => $profile->onboardingStep,
'profileCompletenessPercent' => $profile->profileCompletenessPercent,
'profileType' => $profile->profileType,
] : null,
];
}
/**
* Splits "Ahmed Al Mansouri" → ["Ahmed", "Al Mansouri"]
* Falls back to using the whole name as firstName if no space.
*/
private function splitName(string $name): array
{
$name = trim($name);
if ($name === '') {
return ['', ''];
}
$pos = strpos($name, ' ');
if ($pos === false) {
return [$name, ''];
}
return [substr($name, 0, $pos), substr($name, $pos + 1)];
}
}