false, 'ERROR' => 'missing_fields', 'MESSAGE' => 'UUID and OTP are required']); } $user = queryOne( "SELECT ID, FirstName, LastName, EmailAddress, IsContactVerified, IsEmailVerified, IsActive, MobileVerifyCode FROM Users WHERE UUID = ? LIMIT 1", [$userUUID] ); if (!$user) { apiAbort(['OK' => false, 'ERROR' => 'expired', 'MESSAGE' => 'Verification expired. Please request a new code.']); } // Check OTP (no magic OTP in PHP port — use DEV_OTP from send endpoint for dev testing) if ((string) $user['MobileVerifyCode'] !== (string) $otp) { apiAbort(['OK' => false, 'ERROR' => 'invalid_otp', 'MESSAGE' => 'Invalid verification code. Please try again.']); } $needsProfile = empty(trim($user['FirstName'] ?? '')); if ($needsProfile) { queryTimed("UPDATE Users SET MobileVerifyCode = '' WHERE ID = ?", [$user['ID']]); } else { queryTimed( "UPDATE Users SET MobileVerifyCode = '', IsContactVerified = 1, IsActive = 1 WHERE ID = ?", [$user['ID']] ); } $token = generateSecureToken(); queryTimed( "INSERT INTO UserTokens (UserID, Token) VALUES (?, ?)", [$user['ID'], $token] ); jsonResponse([ 'OK' => true, 'UserID' => (int) $user['ID'], 'Token' => $token, 'NeedsProfile' => $needsProfile, 'FirstName' => $user['FirstName'] ?? '', 'IsEmailVerified' => ((int) ($user['IsEmailVerified'] ?? 0)) === 1, ]);