Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
427 kB
9
Indexable
Never
package com.nsi.storefront.service.coreapi;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.netsol.fg.usermgmt.intf.User;
import com.netsol.fg.usermgmt.intf.UserProfile;
import com.netsol.passwordplus.intf.ReasonType;
import com.nsi.storefront.beans.*;
import com.nsi.storefront.beans.lister.SFWalletItem;
import com.nsi.storefront.business.*;
import com.nsi.storefront.business.utils.SFProductUtils;
import com.nsi.storefront.constants.*;
import com.nsi.storefront.events.SFUsersMerged;
import com.nsi.storefront.exception.SFCCGException;
import com.nsi.storefront.exception.SFException;
import com.nsi.storefront.exception.SFFGException;
import com.nsi.storefront.exception.SFInvalidDataException;
import com.nsi.storefront.helpers.*;
import com.nsi.storefront.security.SFAccessController;
import com.nsi.storefront.security.SFPermissionRequest;
import com.nsi.storefront.service.SFESBCommonService;
import com.nsi.storefront.service.SFPasswordPlusService;
import com.nsi.storefront.service.coreapi.beans.SFCoreRequestInfo;
import com.nsi.storefront.service.coreapi.json.SFJsonObject;
import com.nsi.storefront.service.microservice.IServiceUsesAcctmgrMsvcJwt;
import com.nsi.storefront.service.microservice.beans.SFMsvcResponse;
import com.nsi.storefront.service.microservice.beans.SFMsvcResponseInfo;
import com.nsi.storefront.service.microservice.util.SFMicroServiceUtil;
import com.nsi.storefront.service.utils.*;
import com.nsi.storefront.sort.*;
import com.nsi.storefront.statistics.SFMethodStatistics;
import com.nsi.storefront.utils.*;
import com.nsi.storefront.viewitem.SFViewItem;
import com.nsi.storefront.viewitem.SFViewItemFactory;
import com.nsi.storefront.xmlschema.logging.types.LogEntryTypeEnum;
import com.nsi.storefront.xmlschema.security.types.ResourceEnum;
import com.nsi.storefront.xmlschema.security.types.RoleEnum;
import com.nsi.storefront.xmlschema.security.types.TaskEnum;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.util.CollectionUtils;
import registrar.fulfill.gateway.valuetypeimpl.util.ClientInitializer;
import registrar.fulfill.util.typedef.*;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;

import static com.nsi.storefront.service.coreapi.SFCoreAPIConstants.*;


/**
 * API Service implementation for user operations
 *
 * @author jdigruttola
 */
public class SFUserAPIService extends SFAbstractAPIService implements IServiceUsesAcctmgrMsvcJwt {
    private static final String COMPONENT = "SFUserAPIService";
    private static final int PASSWORD_NOT_RESET = 4100;
    public static final String MASKED_PASSWORD = "xxx";
    public static final String DATE_FORMAT = "MM/dd/yyyy";
    public static final String EXPIRED_DISPLAYNAME = "Expired ";
    public static final String EXPIRES_ON_DISPLAYNAME = "Expires ";
    public static final String AUTO_RENEWS_ON_DISPLAYNAME = "AutoRenews ";
    public static final String FG_LEGAL_LOCK_ON		= "Y";
    public static final String INVALID_TOKEN = "Invalid-Token";

    private SFUserAccountManager userAccountManager;
    private SFHelperManager helperManager;
    private SFAcctMgmtConfig acctMgmtConfig;
    private SFAlertManager alertManager;
    private SFShoppingCart shoppingCart;
    private SFAccessController accessController;
    protected SFStatus status;
    private SFPaidSearchCouponUtil paidSearchCouponUtil;
    private SFPasswordPlusService passwordPlusService;
    private SFUserBean userBeanPassword;
    private SFSessionTrackingInfo sessionTrackingInfo;
    private SFUpdateUserInfoValidationUtil sfUpdateUserInfoValidationUtil;
    private SFUpdateAccountHolderInfoUtil updateAccountHolderInfoUtil;
    private SFUserMergeService userMergeService;
    private SFForgotUserNameUtil sfForgotUserNameUtil;
    private SFCountryList  countryList;
    private SFAccountAPIService accountApiService;
    private SFAMLoginHomePageUtil amLoginHomePageUtil;
    private SFCalcLowUserLeftNavTabsInfo sfCalcLowUserLeftNavTabsInfo;
    private SFCalcLargeUserLeftNavTabsInfo sfCalcLargeUserLeftNavTabsInfo;
    private SFQuickLinksHolder quickLinksHolder;
    private SFProductManager productManager;
    private SFDomainSearchService domainSearchService;
    private List<String> amAlternativeDomains = new ArrayList<>();


    //Bean to store fromUser data during 2FA authentication
	private SFUserBean mergeUserBeanData = null;

    /**
     * Enum to define each method/operation this service supports
     *
     * @author jdigruttola
     */
    enum Method implements SFCoreMethod {
        GET_PAYMENTS_FOR_ACCOUNT("getPaymentsForAccount"),
        GET_ACCOUNTS("getAccounts"),
        GET_ACCOUNTS_LIST("getAccountsList"),
        GET_ACCOUNTS_LIST_LITE("getAccountsListLite"),
        IS_USER_ID_AVAILABLE("isUserIdAvailable"),
        IS_USER_LOGGED_IN("isUserLoggedIn"),
        HAS_MULTIPLE_ACCOUNTS("hasMultipleAccounts"),
        CREATE_ACCOUNT("createAccount"),
        VALIDATE_ADDRESS("validateAddress"),
        SELECT_ACCOUNT("selectAccount"),
        CREATE_USER("createUser"),
        GET_CUSTOMER_NAME("getCustomerName"),
        GET_LOGIN_DATA("getLoginData"),
        GET_COUNTRY_LIST("getCountryList"),
		VALIDATE_UK_IPS_TAG("validateUKIPSTag"),
        GET_US_STATES("getUSStates"),
        GET_CA_PROVINCES("getCanadaProvinces"),
        AUTHENTICATE_USER("authenticateUser"),
        AUTHENTICATE_PASSWORD_PLUS("authenticatePasswordPlus"),
        DOM_REG_USER_DETAILS("domRegUserDetails"),
        GET_USER_INFO("getUserInfo"),
    	GET_USER_INFO_FULL_DETAILS("getUserInfoFullDetails"),
    	CHECK_WEBLOCKED_DOMAINS_FOUND("checkUserHasDomainWithWeblock"),
        UPDATE_USER_PROFILE_INFO("updateUserProfileInfo"),
        SAVE_TRUSTED_PHONE_NUMBER("saveTrustedPhoneNumber"),
        VERIFY_TRUSTED_PHONE_NUMBER("verifyTrustedPhoneNumber"),
		CHECK_VERIFY_TRUSTED_PHONE_NUMBER("checkVerifyTrustedPhoneNumber"),
        UPDATE_PIN("updatePin"),
        UPDATE_TWOFACTOR_AUTHENTICATION("updateTwoFactorAuthentication"),
    	VERIFY_SMSCODE("verifySmsCode"),
    	VERIFY_SMSCODE_FOR_MERGE("verifySmsCodeForMerge"),
        CREATE_RECOVERY_KEY("createRecoveryKey"),
        VERIFY_RECOVERY_KEY("verifyRecoveryKey"),
        VERIFY_RECOVERY_KEY_FOR_MERGE_USER("verifyRecoveryKeyForMergeUser"),
    	AUTHENTICATE_USER_FOR_MERGE("authenticateUserForMerge"),
    	MERGE_USER("mergeUser"),
    	UPDATE_USER_CREDENTIAL_INFO("updateUserCredentialInfo"),
    	UPDATE_SKIP_PIN_SETUP_COUNT("updateSkipPinSetupCount"),
    	AUTHENTICATE_USER_LOGIN("authenticateUserLogin"),
    	FORGOT_USERNAME("forgotUserName"),
    	GET_PRODUCTS_LIST("getProductsList"),
        FORGOT_PASSWORD("forgotPassword"),
    	RESET_PASSWORD("resetPassword"),
    	VERIFY_EMAIL_GIVEN_USERLOGINNAME("verifyEmailGivenUserLoginName"),
    	SEND_SMSCODE("sendSmsCode"),
        SEND_SMSCODE_FOR_MERGE_USER("sendSmsCodeForMergeUser"),
    	VERIFY_PERMISSION("verifyPermission"),
    	GET_MSVC_VERSION("getMsvcVersion"),
    	GET_DATA_FOR_FORGOT_PASSWORD("getDataForForgotPassword"),
    	CHECK_EDIT_CREDIT_CARD_PERMISSION("checkEditCreditCardPermission"),
    	CHECK_EDIT_BILLING_INFO_PERMISSION("checkEditBillingInfoPermission"),
        GET_LEFT_NAV_BAR_TABS_INFO("getLeftNavBarTabsInfo"),
        STORE_REMINDER_DISMISSAL("storeReminderDismissal"),
        CHECK_USER_ACTION_PERMISSION("checkUserActionPermission"),
        GET_ALL_ELIGIBLE_CONTACTS_FOR_WHOIS("getAllEligibleContactsForWhois"),
        GET_USER_ROLE("getUserRole"),
        CHECK_USER_HAS_REQUEST_AUTHCODE_PERMISSION("checkUserHasRequestAuthCodePermission"),
        GET_WALLET_ITEMS_FOR_USER("getWalletItemsForUser"),
        GET_QUICK_LINKS_INFO("getQuickLinksInfo"),
        SAVE_QUICK_LINKS_EVENT("saveQuickLinksEvent"),
        START_VERIFY_EMAIL("startVerifyEmail"),
    	CHECK_VERIFY_EMAIL("checkVerifyEmail"),
    	GET_AM_ALTERNATIVE_DOMAINS("getAMAlternativeDomains"),
        CHECK_FOR_VAT_APPLICABLE_COUNTRY( "checkForVatApplicableCountry"),
        AUTHENTICATE_WITH_JWT_TOKEN_FOR_SSO( "authenticateWithJwtTokenForSSO");

        Method(String name) {
            this.name = name;
        }

        private String name;

        public String getName() {
            return name;
        }
    }

    public SFUserAPIService() {
        populateMethods(Method.values());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JSONObject process(JSONObject request) throws Exception
    {
        final String method = COMPONENT + ".process - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = null;
        try {
            SFJsonObject payLoad = new SFJsonObject(request);
            String methodName = getMethod(payLoad);
            if (StringUtils.isNotEmpty(methodName)) {
                switch (Method.valueOf(methodName)) {
                    case GET_PAYMENTS_FOR_ACCOUNT:
                        result = processGetPaymentsForAccount(payLoad);
                        break;
                    case GET_ACCOUNTS:
                        result = processGetAccounts();
                        break;
                    case GET_ACCOUNTS_LIST:
                    	result = processGetAccountsList(payLoad);
                        break;
                    case GET_ACCOUNTS_LIST_LITE:
                        result = processGetAccountsListLite(payLoad);
                        break;
                    case IS_USER_ID_AVAILABLE:
                        result = processIsUserIdAvailable(payLoad);
                        break;
                    case IS_USER_LOGGED_IN:
                        result = processIsUserLoggedIn();
                        break;
                    case HAS_MULTIPLE_ACCOUNTS:
                        result = processHasMultipleAccounts();
                        break;
                    case CREATE_ACCOUNT:
                        result = processCreateAccount(payLoad);
                        break;
                    case VALIDATE_ADDRESS:
                        result = processValidateAddress(payLoad);
                        break;
                    case SELECT_ACCOUNT:
                        result = processSelectAccount(payLoad);
                        break;
                    case CREATE_USER:
                        processCreateUser(payLoad);
                        break;
                    case GET_CUSTOMER_NAME:
                        result = processGetCustomerName();
                        break;
                    case GET_LOGIN_DATA:
                        result = processGetLoginData(payLoad);
                        break;
                    case GET_COUNTRY_LIST:
                        result = processGetCountriesList();
                        break;
					case VALIDATE_UK_IPS_TAG:
						result = processValidateUKIPSTags(payLoad);
						break;
					case GET_US_STATES:
                        result = processGetUsStates();
                        break;
                    case GET_CA_PROVINCES:
                        result = processGetCaProvinces();
                        break;
                    case AUTHENTICATE_USER:
                        result = processAuthenticateUser(payLoad);
                        break;
                    case AUTHENTICATE_PASSWORD_PLUS:
                        result = processAuthenticatePasswordPlus(payLoad);
                        break;
                    case DOM_REG_USER_DETAILS:
                        processDomRegUserDetails(payLoad);
                        break;
                    case GET_USER_INFO:
                        result = processGetUserInfo();
                        break;
                    case GET_USER_INFO_FULL_DETAILS:
                        result = processGetUserInfoFullDetails();
                        break;
                    case CHECK_WEBLOCKED_DOMAINS_FOUND:
                        result = processCheckUserHasDomainWithWeblock();
                        break;
                    case UPDATE_USER_PROFILE_INFO:
                        result = updateUserProfileInfo(payLoad);
                        break;
                    case SAVE_TRUSTED_PHONE_NUMBER:
                        result = saveTrustedPhoneNumber(payLoad);
                        break;
                    case VERIFY_TRUSTED_PHONE_NUMBER:
                        result = verifyTrustedPhoneNumber(payLoad);
                        break;
					case CHECK_VERIFY_TRUSTED_PHONE_NUMBER:
						result = checkVerifyTrustedPhoneNumber(payLoad);
						break;
                    case UPDATE_PIN:
                        result = updatePin(payLoad);
                        break;
                    case UPDATE_TWOFACTOR_AUTHENTICATION:
                        result = updateTwoFactorAuthenticationStatus(payLoad);
                        break;
                    case VERIFY_SMSCODE:
                        result = verifySmsCode(payLoad);
                        break;
                    case VERIFY_SMSCODE_FOR_MERGE:
                    	result = verifySmsCodeForMerge(payLoad);
                    	break;
                    case CREATE_RECOVERY_KEY:
                        result = createRecoveryKey(payLoad);
                        break;
                    case VERIFY_RECOVERY_KEY:
                        result = verifyRecoveryKey(payLoad);
                        break;
                    case VERIFY_RECOVERY_KEY_FOR_MERGE_USER:
                        result = verifyRecoveryKeyForMergeUser(payLoad);
                        break;
                    case AUTHENTICATE_USER_FOR_MERGE:
                        result = processAuthenticateUserForMerge(payLoad);
                        break;
                    case MERGE_USER:
                        result = processMergeUser(payLoad);
                        break;
                    case UPDATE_USER_CREDENTIAL_INFO:
                        result = performUpdateUserCredentialInfo(payLoad);
                        break;
                    case UPDATE_SKIP_PIN_SETUP_COUNT:
                        result = performUpdateSkipPinSetupCount(payLoad);
                        break;
                    case AUTHENTICATE_USER_LOGIN:
                        result = processAuthenticateUserLogin(payLoad);
                        break;
                    case FORGOT_USERNAME:
                        result = forgotUsername(payLoad);
                        break;
                    case GET_PRODUCTS_LIST:
                    	result = processGetProductsList(payLoad);
                    	break;
                    case FORGOT_PASSWORD:
                        result = processForgotPassword(payLoad);
                        break;
                    case RESET_PASSWORD:
                    	result = resetPassword(payLoad);
                    	break;
                    case VERIFY_EMAIL_GIVEN_USERLOGINNAME:
                    	result = verifyEmailGivenUserLoginName(payLoad);
                    	break;
                    case SEND_SMSCODE:
                    	result = processSendSmsCode(payLoad);
                    	break;
                    case SEND_SMSCODE_FOR_MERGE_USER:
                    	result = processSendSmsCodeForMergeUser(payLoad);
                    	break;
                    case VERIFY_PERMISSION:
                    	result = verifyPermission(payLoad);
                    	break;
                    case GET_MSVC_VERSION:
                    	result = getMsvcVersion(payLoad);
                    	break;
                    case GET_DATA_FOR_FORGOT_PASSWORD:
                    	result = processGetDataForForgotPassword(payLoad);
                    	break;
                    case CHECK_EDIT_CREDIT_CARD_PERMISSION:
                    	result = processCheckEditCreditCardPermission(payLoad);
                    	break;
                    case CHECK_EDIT_BILLING_INFO_PERMISSION:
                    	result = processCheckEditBillingInfoPermission(payLoad);
                    	break;
                    case GET_LEFT_NAV_BAR_TABS_INFO:
                        result = processGetLeftNavBarTabsInfoMethod(payLoad);
                        break;
                    case STORE_REMINDER_DISMISSAL:
                        result = processStoreReminderDismissal(payLoad);
                        break;
                    case CHECK_USER_ACTION_PERMISSION:
                        result = processCheckUserActionPermission(payLoad);
                        break;
                    case GET_ALL_ELIGIBLE_CONTACTS_FOR_WHOIS:
    					result = processGetAllEligibleContactsForWhois(payLoad);
    					break;
                    case GET_USER_ROLE:
                        result = processGetUserRole(payLoad);
                        break;
                    case CHECK_USER_HAS_REQUEST_AUTHCODE_PERMISSION:
                        result = processCheckUserHasRequestAuthCodePermission(payLoad);
                        break;
                    case GET_WALLET_ITEMS_FOR_USER:
                        result = processGetWalletItemsForUser(payLoad);
                        break;
                    case GET_QUICK_LINKS_INFO:
                        result = processGetQuickLinksInfo(payLoad);
                        break;
                    case SAVE_QUICK_LINKS_EVENT:
                        result = processSaveQuickLinksEvent(payLoad);
                        break;
                    case START_VERIFY_EMAIL:
                        result = processStartVerifyEmail(payLoad);
                        break;
                    case CHECK_VERIFY_EMAIL:
                        result = processCheckVerifyEmail(payLoad);
                        break;
                    case GET_AM_ALTERNATIVE_DOMAINS:
                    	result = processGetAMAlternativeDomains(payLoad);
                    	break;
                    case CHECK_FOR_VAT_APPLICABLE_COUNTRY:
                        result = processCheckForVatApplicableCountry(payLoad);
                        break;
                    case AUTHENTICATE_WITH_JWT_TOKEN_FOR_SSO:
                        result = processAuthenticateWithJwtTokenForSSO(payLoad);
                        break;

                }
            }
        } catch (JSONException e) {
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "JSONException while processing: " + request, e);
            addError(SFAPIServiceResponseError.JSON_EXCEPTION, e.getMessage());
        }
        stat.stop(methStatId, true);
        return super.process(result);
    }

    /***
     * This method is used to verify the recovery key for merge user 2fa authentication which is entered as a part of the request
     * @param payLoad - holds the merge user recovery key for verification.
     * @return jsonOUtObj - holds output json
     * @throws SFException - throws user defined Exception
     */
    private JSONObject verifyRecoveryKeyForMergeUser(SFJsonObject payLoad) throws SFException, JSONException {
        JSONObject jsonOutObj = new JSONObject();
        final String method = COMPONENT + ".verifyRecoveryKeyForMergeUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        String failureReason = null;

        boolean isVerifySuccessful = false;

        String jwtToken = null;

        String fromUserId = null;
        String fromUserFirstName = null;
        String fromUserLastName = null;
        String fromUserEmail = null;
        String fromUserPhone = null;
        String fromUserStreetAddress = null;
        String fromUserStreetAddress2 = null;
        String fromUserCity = null;
        String fromUserState = null;
        String fromUserCountry = null;
        String fromUserZip = null;

        String toUserId = null;
        String toUserFirstName = null;
        String toUserLastName = null;
        String toUserEmail = null;
        String toUserPhone = null;
        String toUserStreetAddress = null;
        String toUserStreetAddress2 = null;
        String toUserCity = null;
        String toUserState = null;
        String toUserCountry = null;
        String toUserZip = null;

        // check first user logged in
        SFUser currentUser = userAccountManager.getUserBean().getUser();

        if (currentUser.getUserId() == -1) {
            failureReason = "loginRequired";
            addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
            jsonOutObj.put(FAILURE_REASON, failureReason);
            stat.stop(methStatId, false);
            return jsonOutObj;
        }

        try {
            String recoveryKey = payLoad.getField(USER_DATA_RECOVERY_KEY_PATH);

            if (null == mergeUserBeanData || mergeUserBeanData.getUserId() == -1 || SFStringUtil.isEmpty(mergeUserBeanData.getUser().getUserLoginName())) {
                failureReason = "merge user details Unavailable";
            }
            else if(SFStringUtil.isEmpty(recoveryKey)){
                //avoid setting as an error, as agreed with angular team,
                //they will interpret response
                failureReason = "The recovery key is a required field";
            }

            if(SFStringUtil.isEmpty(failureReason)) {

                SFUser fromUser = mergeUserBeanData.getUser();

                JSONObject data = new JSONObject();
                JSONObject userData = new JSONObject();
                JSONObject sessionData = new JSONObject();

                userData.put("userLogInName", fromUser.getUserLoginName());
                userData.put("recoveryKey", recoveryKey);
                userData.put("originatorId", fromUser.getOriginatorId());

                SFFraudProtection fraud = fromUser.getFraudProtection();
                if (null != fraud.getBrowserId()) {
                    sessionData.put("browserId", fraud.getBrowserId());
                } else {
                    sessionData.put("browserId", SFStringUtil.EMPTY);
                }

                if (null != fraud.getCookieId()) {
                    sessionData.put("cookie", fraud.getCookieId());
                } else {
                    sessionData.put("cookie", SFStringUtil.EMPTY);
                }

                if (null != fraud.getHostName()) {
                    sessionData.put("hostName", fraud.getHostName());
                } else {
                    sessionData.put("hostName", SFStringUtil.EMPTY);
                }

                if (null != fraud.getIpAddress()) {
                    sessionData.put("ipAddress", fraud.getIpAddress());
                } else {
                    sessionData.put("ipAddress", SFStringUtil.EMPTY);
                }

                if (null != fraud.getSessionId()) {
                    sessionData.put("sessionId", fraud.getSessionId());
                } else {
                    sessionData.put("sessionId", SFStringUtil.EMPTY);
                }

                data.put("userData", userData);
                data.put("sessionData", sessionData);

                jwtToken = getMicroserviceJwtToken(payLoad);

                SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "verifyRecoveryKey", "/sf/api/security/authenticateSFLoginWithRecoveryKey", jwtToken);
                SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

                if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
                    String jsonDataOut = responseObj.getData();
                    jsonOutObj = new JSONObject(jsonDataOut);

                    if (jsonOutObj.has(IS_VALID)) {
                        boolean isValid = jsonOutObj.getBoolean(IS_VALID);
                        if (isValid) {
                            isVerifySuccessful = true;
                            SFLogger.printInfo(
                                    method + " - merge user verification successful using recoveryKey, marking merge user user as being authenticated." + mergeUserBeanData.getUserId());

                            //will treat the "currentUser" as the user currently in the session
                            //and the "from user" as the user that the person just entered into the UI
                            //set fromUser and toUser data
                            fromUserId = String.valueOf(fromUser.getUserId());
                            toUserId = String.valueOf(currentUser.getUserId());
                            if (!SFStringUtil.isEmpty(fromUserId) && !SFStringUtil.isEmpty(toUserId)) {
                                // add fromUser
                                fromUserFirstName = fromUser.getFirstName();
                                fromUserLastName = fromUser.getLastName();
                                fromUserEmail = fromUser.getEmail();
                                fromUserPhone = fromUser.getPhoneNum();
                                SFAddress fromUserAddress = fromUser.getAddress();
                                if (fromUserAddress != null) {
                                    fromUserStreetAddress = fromUserAddress.getStreetAddress();
                                    fromUserStreetAddress2 = fromUserAddress.getStreetAddress2();
                                    fromUserCity = fromUserAddress.getCity();
                                    fromUserState = fromUserAddress.getState();
                                    fromUserCountry = fromUserAddress.getCountry();
                                    fromUserZip = fromUserAddress.getZip();
                                }
                                // add toUser
                                toUserFirstName = currentUser.getFirstName();
                                toUserLastName = currentUser.getLastName();
                                toUserEmail = currentUser.getEmail();
                                toUserPhone = currentUser.getPhoneNum();
                                SFAddress toUserAddress = currentUser.getAddress();
                                if (toUserAddress != null) {
                                    toUserStreetAddress = toUserAddress.getStreetAddress();
                                    toUserStreetAddress2 = toUserAddress.getStreetAddress2();
                                    toUserCity = toUserAddress.getCity();
                                    toUserState = toUserAddress.getState();
                                    toUserCountry = toUserAddress.getCountry();
                                    toUserZip = toUserAddress.getZip();
                                }
                            }
                        } else {
                            failureReason = "MergeUserVerificationFailed";
                            SFLogger.printInfo(
                                    method + " - merge user verification failed using recoveryKey, marking user as not being authenticated." + mergeUserBeanData.getUserId());
                            int count = mergeUserBeanData.getFalseRecoveryKeyCount();
                            count++;
                            mergeUserBeanData.setFalseRecoveryKeyCount(count);
                            jsonOutObj.put(FALSE_RECOVERY_KEY_COUNT, count);
                        }
                    } else {
                        throw new SFException(
                                "the isValid param from the verify recovery key api for merge user was not found in the data returned -" + mergeUserBeanData.getUserId());
                    }
                } else {
                    failureReason = "MergeUserVerificationFailed";
                    // not going to increment the error count since it
                    // seemed to fail due to a bug
                    int count = mergeUserBeanData.getFalseRecoveryKeyCount();
                    jsonOutObj.put(FALSE_RECOVERY_KEY_COUNT, count);
                }
            }
        } catch (Exception ex) {
            failureReason = "General-SystemUnavailable";

            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                    method + "Error : " + ex);
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            jsonOutObj.put(FAILURE_REASON, failureReason);
            //avoid setting as an error, as agreed with angular team,
            //they will interpret response
            //addError(SFAPIServiceResponseError.ERROR_VERIFY_RECOVERY_KEY, failureReason);
            stat.stop(methStatId, false);
        }
        else{
            stat.stop(methStatId,true);
        }

        jsonOutObj.put(AUTHENTICATED, isVerifySuccessful);
        if (isVerifySuccessful) {
            // add from user
            jsonOutObj.put(FROM_USER_ID, fromUserId);
            jsonOutObj.put(FROM_USER_FIRST_NAME, fromUserFirstName);
            jsonOutObj.put(FROM_USER_LAST_NAME, fromUserLastName);
            jsonOutObj.put(FROM_USER_EMAIL, fromUserEmail);
            jsonOutObj.put(FROM_USER_PHONE, fromUserPhone);
            jsonOutObj.put(FROM_USER_STREET_ADDRESS, fromUserStreetAddress);
            jsonOutObj.put(FROM_USER_STREET_ADDRESS_2, fromUserStreetAddress2);
            jsonOutObj.put(FROM_USER_CITY, fromUserCity);
            jsonOutObj.put(FROM_USER_STATE, fromUserState);
            jsonOutObj.put(FROM_USER_COUNTRY, fromUserCountry);
            jsonOutObj.put(FROM_USER_ZIP, fromUserZip);

            // add to user
            jsonOutObj.put(TO_USER_ID, toUserId);
            jsonOutObj.put(TO_USER_FIRST_NAME, toUserFirstName);
            jsonOutObj.put(TO_USER_LAST_NAME, toUserLastName);
            jsonOutObj.put(TO_USER_EMAIL, toUserEmail);
            jsonOutObj.put(TO_USER_PHONE, toUserPhone);
            jsonOutObj.put(TO_USER_STREET_ADDRESS, toUserStreetAddress);
            jsonOutObj.put(TO_USER_STREET_ADDRESS_2, toUserStreetAddress2);
            jsonOutObj.put(TO_USER_CITY, toUserCity);
            jsonOutObj.put(TO_USER_STATE, toUserState);
            jsonOutObj.put(TO_USER_COUNTRY, toUserCountry);
            jsonOutObj.put(TO_USER_ZIP, toUserZip);
        }

        return jsonOutObj;
    }

    /***
	 * This method is used to verify merge user (second user)Sms Code when 2fa
	 * is enabled.
	 * 
	 * @param payLoad
	 *            - payload holds the 6-digit sms code and AuthyId.
	 * @return
	 * @throws SFException
	 * @throws JSONException
	 */
	private JSONObject verifySmsCodeForMerge(SFJsonObject payLoad) throws SFException, JSONException {
		final String method = COMPONENT + ".verifySmsCodeForMerge - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject jsonOutObj = new JSONObject();
		JSONObject data = new JSONObject();

		String failureReason = null;
		boolean isVerifySuccessful = false;
		
		String fromUserId = null;
        String fromUserFirstName = null;
        String fromUserLastName = null;
        String fromUserEmail = null;
        String fromUserPhone = null;
        String fromUserStreetAddress = null;
        String fromUserStreetAddress2 = null;
        String fromUserCity = null;
        String fromUserState = null;
        String fromUserCountry = null;
        String fromUserZip = null;
		
		String toUserId = null;
		String toUserFirstName = null;
        String toUserLastName = null;
        String toUserEmail = null;
        String toUserPhone = null;
        String toUserStreetAddress = null;
        String toUserStreetAddress2 = null;
        String toUserCity = null;
        String toUserState = null;
        String toUserCountry = null;
        String toUserZip = null;

		// check first user logged in
		SFUser currentUser = userAccountManager.getUserBean().getUser();
		if (currentUser.getUserId() == -1) {
			failureReason = "loginRequired";
		} else {
			try {

				if (mergeUserBeanData != null && null != mergeUserBeanData.getUser()
						&& -1 != mergeUserBeanData.getUserId()) {
					int mergeUserId = mergeUserBeanData.getUserId();
					String authyId = mergeUserBeanData.getAuthyId();
					if (SFStringUtil.isEmpty(authyId)) {
						failureReason = "The Merge user authentication may not have been successful, since the authyId was not found for the user : "+mergeUserId;
					} else {

						data.put(AUTHY_ID, authyId);
						data.put(CODE, payLoad.getField(SMS_VERIFICATION_CODE));

						data.put("ipAddress", determineIpAddress());
						String sessId = getHashedSessionId();
						if (sessId == null) {
							sessId = determineSessionIdWithoutHashing();
						}
						data.put("sessionId", sessId);
						String sUserId = Integer.toString(mergeUserId);
						data.put("originatorId", sUserId);
						data.put("personOrgId", sUserId);

						JSONObject requestWrapper = new JSONObject();
						JSONObject requestObj = new JSONObject();
						JSONObject requestInfo = new JSONObject();

						requestInfo.put("userId", mergeUserId);
						requestInfo.put("userPassword", getUserPassword(mergeUserId));
						requestInfo.put(CLIENT_IPADDRESS, determineIpAddress());

						requestObj.put("requestInfo", requestInfo);
						requestObj.put("data", data);

						requestWrapper.put("request", requestObj);

						StringBuilder methodNameSb = new StringBuilder();
						methodNameSb.append(MS_VERIFY_SMSCODE_METHOD);
						methodNameSb.append(" - personOrgId(" + mergeUserId + ")");
						methodNameSb.append(" - " + determineSessionIdWithoutHashing());

						// call the acctmgr microservice
						SFMsvcResponse responseObj = SFMicroServiceUtil.getAcctmgrMsvcResponse(requestWrapper,
								methodNameSb.toString(), "user", MS_VERIFY_SMSCODE_API_CALL, getMicroserviceJwtToken(payLoad));

						SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

						if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {

							String jsonDataOut = responseObj.getData();
							jsonOutObj = new JSONObject(jsonDataOut);

							if (jsonOutObj.has(STATUS_ATTR)) {
								String smsStatus = jsonOutObj.getString(STATUS_ATTR);
								if ("success".equalsIgnoreCase(smsStatus)) {
									isVerifySuccessful = true;
									SFLogger.printInfo(
											method + " - sms code verification successful, merge user 2FA authenticated now! "+mergeUserId);
									
									//will treat the "currentUser" as the user currently in the session
						        	//and the "from user" as the user that the person just entered into the UI
									//set fromUser and toUser data
									SFUser fromUser = mergeUserBeanData.getUser();
									fromUserId = String.valueOf(fromUser.getUserId());
									toUserId = String.valueOf(currentUser.getUserId());
									if (!SFStringUtil.isEmpty(fromUserId) && !SFStringUtil.isEmpty(toUserId)) {
										// add fromUser
										fromUserFirstName = fromUser.getFirstName();
										fromUserLastName = fromUser.getLastName();
										fromUserEmail = fromUser.getEmail();
										fromUserPhone = fromUser.getPhoneNum();
										SFAddress fromUserAddress = fromUser.getAddress();
										if (fromUserAddress != null) {
											fromUserStreetAddress = fromUserAddress.getStreetAddress();
											fromUserStreetAddress2 = fromUserAddress.getStreetAddress2();
											fromUserCity = fromUserAddress.getCity();
											fromUserState = fromUserAddress.getState();
											fromUserCountry = fromUserAddress.getCountry();
											fromUserZip = fromUserAddress.getZip();
										}
										// add toUser
										toUserFirstName = currentUser.getFirstName();
										toUserLastName = currentUser.getLastName();
										toUserEmail = currentUser.getEmail();
										toUserPhone = currentUser.getPhoneNum();
										SFAddress toUserAddress = currentUser.getAddress();
										if (toUserAddress != null) {
											toUserStreetAddress = toUserAddress.getStreetAddress();
											toUserStreetAddress2 = toUserAddress.getStreetAddress2();
											toUserCity = toUserAddress.getCity();
											toUserState = toUserAddress.getState();
											toUserCountry = toUserAddress.getCountry();
											toUserZip = toUserAddress.getZip();
										}
									}
								} else {
									failureReason = "mergeUser2FAVerificationFailed";
									SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
											method + " - merge user 2FA verification failed! "+mergeUserId);

									String failedCodeCount = jsonOutObj.getString("failedVerificationCodeCount");

									jsonOutObj.put(FALSE_VERIFICATION_CODE_COUNT, failedCodeCount);
									mergeUserBeanData.setFalseVerificationCodeCount(Integer.parseInt(failedCodeCount));
								}
							} else {
								throw new SFException(
										"the status from the verify sms api was not found in the data returned");
							}
						} else {
							failureReason = "mergeUser2FAVerificationFailed";
							SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
									method + " - merge user 2FA verification failed! "+mergeUserId);
							// not going to increment the error count since it
							// seemed to fail due to a bug
							int count = mergeUserBeanData.getFalseVerificationCodeCount();
							jsonOutObj.put(FALSE_VERIFICATION_CODE_COUNT, count);

							List<String> msvcErrors = responseObj.getResponseInfo().getErrors();
							if (!CollectionUtils.isEmpty(msvcErrors)) {
								StringBuffer errorBuf = new StringBuffer();
								for (String error : msvcErrors) {
									addError(error);
									errorBuf.append(error);
								}

								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM,
										COMPONENT + method + "Microservice Error : " + errorBuf.toString());
							}
						}
					}
				} else {
					failureReason = "The Merge user authentication may not have been successful, since merge user authenticated data wasn't found";
				}
			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex);
			}

		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			// avoid setting as an error, as agreed with angular team,
			// they will interpret response
			// addError(SFAPIServiceResponseError.ERROR_VERIFY_SMS_CODE,
			// failureReason);
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		jsonOutObj.put(IS_VALID, isVerifySuccessful);
		jsonOutObj.put(AUTHENTICATED, isVerifySuccessful);
		if (isVerifySuccessful) {
			// add from user
			jsonOutObj.put(FROM_USER_ID, fromUserId);
			jsonOutObj.put(FROM_USER_FIRST_NAME, fromUserFirstName);
			jsonOutObj.put(FROM_USER_LAST_NAME, fromUserLastName);
			jsonOutObj.put(FROM_USER_EMAIL, fromUserEmail);
			jsonOutObj.put(FROM_USER_PHONE, fromUserPhone);
			jsonOutObj.put(FROM_USER_STREET_ADDRESS, fromUserStreetAddress);
			jsonOutObj.put(FROM_USER_STREET_ADDRESS_2, fromUserStreetAddress2);
			jsonOutObj.put(FROM_USER_CITY, fromUserCity);
			jsonOutObj.put(FROM_USER_STATE, fromUserState);
			jsonOutObj.put(FROM_USER_COUNTRY, fromUserCountry);
			jsonOutObj.put(FROM_USER_ZIP, fromUserZip);

			// add to user
			jsonOutObj.put(TO_USER_ID, toUserId);
			jsonOutObj.put(TO_USER_FIRST_NAME, toUserFirstName);
			jsonOutObj.put(TO_USER_LAST_NAME, toUserLastName);
			jsonOutObj.put(TO_USER_EMAIL, toUserEmail);
			jsonOutObj.put(TO_USER_PHONE, toUserPhone);
			jsonOutObj.put(TO_USER_STREET_ADDRESS, toUserStreetAddress);
			jsonOutObj.put(TO_USER_STREET_ADDRESS_2, toUserStreetAddress2);
			jsonOutObj.put(TO_USER_CITY, toUserCity);
			jsonOutObj.put(TO_USER_STATE, toUserState);
			jsonOutObj.put(TO_USER_COUNTRY, toUserCountry);
			jsonOutObj.put(TO_USER_ZIP, toUserZip);
		}
		return jsonOutObj;
	}

	/***
	 * This api is used to get alternative recommendation domains for AM Home
	 * page usage Alternative Domain Ad.
	 * 
	 * @param payLoad
	 * 
	 * @return
	 * @throws JSONException
	 * @throws SFException
	 */
	private JSONObject processGetAMAlternativeDomains(SFJsonObject payLoad) throws SFException, JSONException {
		final String method = COMPONENT + ".processGetAMAlternativeDomains - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject jsonOutObj = new JSONObject();
		List<String> spinnedDomains = new ArrayList<>();

		String failureReason = null;
		boolean isErrorSpecified = false;
		int accountId = 0;
		String accuntRole = EMPTY;

		try {

			SFUser currentUser = userAccountManager.getUserBean().getUser();
			if (currentUser.getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} else {
				List<SFAccount> accountsList = getAccounts();
				if (null != accountsList && accountsList.size() > 0) {
					for (SFAccount sAccount : accountsList) {
						accountId = sAccount.getAccountId();
						List<String> randomActiveDomains = getAccountRandomActiveDomainNames(sAccount);
						List<String> alternativeDomains = searchForAlternativeDomains(randomActiveDomains,
								SFConfig.getInstance().getAlternativeOfferDomainTlds());

						// Spin alternative domains
						spinnedDomains = spinDomains(alternativeDomains);
						if (spinnedDomains.size() == 3) {
							break;
						}
					}
				}

				RoleEnum role = userAccountManager.getRelation(currentUser.getUserId(), accountId);
				accuntRole = SFUtils.getAccountRoleDisplayName(role);
			}
		} catch (Exception ex) {
			failureReason = "General-SystemUnavailable" + ex.getMessage();
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ", ex);
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if (!isErrorSpecified) {
				addError(SFAPIServiceResponseError.ERROR_GET_AM_ALTERNATIVE_DOMAINS, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		jsonOutObj.put("accountRole", accuntRole);
		jsonOutObj.put(ACCOUNT_ID_ATTR, accountId);
		jsonOutObj.put("alternativeDomains", spinnedDomains);

		stat.stop(methStatId, true);
		return jsonOutObj;
	}

	/***
	 * This api (used by PEGA) is to send a one time pin to the trusted email address of the
	 * customer.
	 * 
	 * @param payLoad
	 *            - payload holds the trusted email.
	 * @return
	 * @throws SFException
	 */
	private JSONObject processStartVerifyEmail(SFJsonObject payLoad) throws SFException, JSONException {

		final String method = COMPONENT + ".processStartVerifyEmail - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject jsonOutObj = new JSONObject();

		String jwtToken = null;

		String failureReason = null;
		boolean isErrorSpecified = false;

		try {

			SFUser currentUser = userAccountManager.getUserBean().getUser();

			if (currentUser.getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} else {

				String trustedEmail = payLoad.getField(USER_DATA_TRUSTED_EMAIL_PATH);
				/***
				 * Validate if the trusted email and then go ahead.
				 */
				if (trustedEmail.equalsIgnoreCase(currentUser.getEmail())) {

					JSONObject data = new JSONObject();
					data.put("email", trustedEmail);
					data.put("channel", "sms");
					data.put("ipAddress", determineIpAddress());
					String sessionId = getHashedSessionId();
					if (sessionId == null) {
						sessionId = determineSessionIdWithoutHashing();
					}
					data.put("sessionId", sessionId);
					int userId = currentUser.getUserId();
					String sUserId = Integer.toString(userId);
					data.put("originatorId", sUserId);
					data.put("personOrgId", sUserId);

					SFLogger.printInfo(method + " - attempt to verify trusted email.");

					jwtToken = getMicroserviceJwtToken(payLoad);

					SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "startVerifyEmail",
							"/sf/api/security/startVerifyEmail", jwtToken);

					SFMsvcResponseInfo sfMsvcResponseInfoForVerify = responseObj.getResponseInfo();

					if ("Success".equalsIgnoreCase(sfMsvcResponseInfoForVerify.getStatus())) {
						String jsonDataOut = responseObj.getData();
						jsonOutObj = new JSONObject(jsonDataOut);
					} else {
						failureReason = "verification failed due to error";
					}

				} else {
					failureReason = "Invalid trusted email";
					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
							method + "Invalid trusted email (" + trustedEmail + ")");
				}
			}

		} catch (Exception ex) {
			failureReason = "General-SystemUnavailable" + ex.getMessage();
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ", ex);
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if (!isErrorSpecified) {
				addError(SFAPIServiceResponseError.ERROR_VERIFY_TRUSTED_EMAIL, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		stat.stop(methStatId, true);
		return jsonOutObj;
	}

	/***
	 * This api (used by PEGA) is to verify a one time pin which was sent to the trusted email
	 * address of the customer.
	 *
	 * Make sure call startVerifyEmail first before calling this checkVerifyEmail.
	 * 
	 * @param payLoad
	 *            - payload holds the email and code.
	 * @return
	 * @throws SFException
	 */
	private JSONObject processCheckVerifyEmail(SFJsonObject payLoad) throws SFException, JSONException {

		final String method = COMPONENT + ".processCheckVerifyEmail - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject jsonOutObj = new JSONObject();
		JSONObject data = new JSONObject();

		String failureReason = null;

		String jwtToken = null;

		boolean isVerifySuccessful = false;
		boolean isErrorSpecified = false;

		SFUser currentUser = userAccountManager.getUserBean().getUser();
		if (currentUser.getUserId() == -1) {
			// avoid setting as an error, as agreed with angular team,
			// they will interpret response
			failureReason = "loginRequired";

		} else {
			try {

				String email = payLoad.getField(USER_DATA_TRUSTED_EMAIL_PATH);
				String code = payLoad.getField(SMS_VERIFICATION_CODE);

				if (SFStringUtil.isEmpty(email)) {
					failureReason = "emailRequired";
					addError(SFAPIServiceResponseError.REQUEST_INVALID_DATA, "trustedEmail");
					isErrorSpecified = true;
				}

				if (!isErrorSpecified && SFStringUtil.isEmpty(code)) {
					failureReason = "codeRequired";
					addError(SFAPIServiceResponseError.REQUEST_INVALID_DATA, "code");
					isErrorSpecified = true;
				}

				if (!isErrorSpecified && !email.equalsIgnoreCase(currentUser.getEmail())) {
					failureReason = "invalid trusted email";
					addError(SFAPIServiceResponseError.REQUEST_INVALID_DATA, "trusted email");
					isErrorSpecified = true;
				}
				
				if (!isErrorSpecified) {
					data.put(EMAIL_ATTR, email);
					data.put(CODE, code);

					data.put("ipAddress", determineIpAddress());
					String sessId = getHashedSessionId();
					if (sessId == null) {
						sessId = determineSessionIdWithoutHashing();
					}
					data.put("sessionId", sessId);
					int userId = currentUser.getUserId();
					String sUserId = Integer.toString(userId);
					data.put("originatorId", sUserId);
					data.put("personOrgId", sUserId);

					jwtToken = getMicroserviceJwtToken(payLoad);

					// call the acctmgr microservice
					SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "checkVerifyEmail",
							"/sf/api/security/checkVerifyEmail", jwtToken);

					SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

					if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {

						String jsonDataOut = responseObj.getData();
						jsonOutObj = new JSONObject(jsonDataOut);

						if (jsonOutObj.has(STATUS_ATTR)) {
							String smsStatus = jsonOutObj.getString(STATUS_ATTR);
							if ("approved".equalsIgnoreCase(smsStatus.trim())) {
								isVerifySuccessful = true;
								SFLogger.printInfo(method + " - trusted email verification successful!");
							} else {
								failureReason = "verificationFailed" +sfMsvcResponseInfo.getErrors();
								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
										method + " - trusted email verification failed!" +sfMsvcResponseInfo.getErrors());
							}
						} else {
							throw new SFException(
									"the status from the checkVerifyEmail api was not found in the data returned");
						}
					} else {
						failureReason = "verificationFailed";
					}

				}
			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable" + ex.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex);
			}

		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if (!isErrorSpecified) {
				addError(SFAPIServiceResponseError.ERROR_VERIFY_TRUSTED_EMAIL, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		jsonOutObj.put(IS_VALID, isVerifySuccessful);
		return jsonOutObj;
	}

	//This method is to get microservice version
    private JSONObject getMsvcVersion(SFJsonObject payLoad) throws JSONException, SFException {
		final String method = COMPONENT + ".getMsvcVersion - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject data = new JSONObject();
		String failureReason = null;
		JSONObject result = new JSONObject();
		JSONObject jsonOutObj = new JSONObject();
		boolean isErrorSpecified = false;
		
		try{
			
			SFUserBean userBean = userAccountManager.getUserBean();
			if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} 
			
			if(!isErrorSpecified){
				String jwtToken = getMicroserviceJwtToken(payLoad);
			
				SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "getVersion", GET_MSVC_API_CALL,jwtToken);
				SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();
		
				if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
		
					String jsonDataOut = responseObj.getData();
					jsonOutObj = new JSONObject(jsonDataOut);
		
					if (jsonOutObj.has("storefront-version")) {
						String storefrontversion = jsonOutObj.getString("storefront-version");
						String pomversion = jsonOutObj.getString("pom-version");
						result.put("storefrontversion", storefrontversion);
						result.put("pomversion", pomversion);
						SFLogger.printInfo(method + " - storefront-version:"+ storefrontversion + " - pom-version:" +pomversion +" .");
						} 
					
		
				} else {
					failureReason = "getMsvcFailed";
				}
			}
		
		}catch( Exception ex){
			failureReason = "There was an error in retrieving microservice version";
			SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
					+ "Error in reteriving microservice version: " + ex);
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_GET_MSVC_VERSION, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		stat.stop(methStatId, true);
		return result;
	}
	/**
	 * This method is intended to resend sms code to users truster phone number,
	 * 
	 * Expected to call this api only after authenticateUserLogin and user 2FA
	 * should be Enabled.
	 * 
	 * @param payLoad
	 * @return
	 * @throws JSONException
	 */
	private JSONObject processSendSmsCode(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".processSendSmsCode - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject result = new JSONObject();
		JSONObject jsonOutObj = new JSONObject();
		String failureReason = null;
		String trustedPhoneNumber = null;
		String twoFactorAuthStatus = null;
		boolean isSms2FACodeSent = false;
		
		String jwtToken = null;

		SFUser currentUser = null;

		if (userAccountManager.getUserBean() != null) {
			currentUser = userAccountManager.getUserBean().getUser();
		} else {
			SFLogger.printWarning(method + " user bean object is null, cannot determine 2fa info.");
		}

		if (null != currentUser) {
			// checks for 2FA is Enabled
			twoFactorAuthStatus = currentUser.getTwoFactorAuthStatus();
			if (StringUtils.isEmpty(twoFactorAuthStatus)
					|| SFConstant.TWO_FACTOR_AUTH_DISABLED.getDescription().equalsIgnoreCase(twoFactorAuthStatus)) {
				failureReason = "2FA is disabled, unable to to process send sms code !!";
				SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
						COMPONENT + method + "2fa disabled, we can't process sending sms code for userId ("
								+ currentUser.getUserId() + " )");
			}
			// checks for valid trustedPhoneNumber
			trustedPhoneNumber = currentUser.getTrustedPhoneNumber();
			if (!SFStringUtil.isEmpty(trustedPhoneNumber)) {
				int len = trustedPhoneNumber.length();
				String last4 = null;
				if (len > 4) {
					int lenRemain = len - 4;
					last4 = trustedPhoneNumber.substring(lenRemain);
				} else {
					last4 = trustedPhoneNumber;
				}
				trustedPhoneNumber = SFUtils.maskTrustedPhoneNumber(last4);
			} else {
				failureReason = "trustedPhoneNumberIsEmpty";
				SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
						COMPONENT + method
								+ "trustedPhoneNumber is not available, we can't process sending sms code for userId ("
								+ currentUser.getUserId() + " )");
			}
		} else if (null == currentUser) {
			failureReason = "UserAuthenticationIsRequired!!";
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					COMPONENT + method + " sf user object is null, cannot determine 2fa info.");
		}

		// Start that process to resend SMS code when user 2FA is Enabled,has
		// trustedPhoneNumber and user should not be logged in successfully.
		if (StringUtils.isEmpty(failureReason) && !currentUser.isLoggedIn()) {
			SFLogger.printInfo(method + " - userId (" + currentUser.getUserId() + ") has 2fa enabled.");

			// send the sms code to the phone
			JSONObject data = new JSONObject();

			// call the acctmgr microservice
			try {

				String authyId = userAccountManager.getUserBean().getAuthyId();

				if (SFStringUtil.isEmpty(authyId)) {
					throw new SFException(
							"The authy id was empty but is required when 2FA is enabled. Cannot send sms code to trusted phone number");
				} else {

					data.put(AUTHY_ID, authyId);
					data.put("ipAddress", determineIpAddress());
					String sessId = getHashedSessionId();
	    			if(sessId == null){
	    				sessId = determineSessionIdWithoutHashing();
	    			}
					data.put("sessionId", sessId);
					int userId = currentUser.getUserId();
					String sUserId = Integer.toString(userId);
					data.put("originatorId", sUserId);
					data.put("personOrgId", sUserId);
					data.put("phoneNumber", currentUser.getTrustedPhoneNumber());

					SFLogger.printInfo(method + " - attempt to send sms code to trusted phone number.");

					jwtToken = getMicroserviceJwtToken(payLoad);
					
					SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "sendSmsCode", MS_SEND_SMSCODE_API_CALL, jwtToken);
					SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

					if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {

						String jsonDataOut = responseObj.getData();
						jsonOutObj = new JSONObject(jsonDataOut);

						if (jsonOutObj.has(STATUS_ATTR)) {
							String smsStatus = jsonOutObj.getString(STATUS_ATTR);
							if ("success".equalsIgnoreCase(smsStatus)) {
								isSms2FACodeSent = true;
								SFLogger.printInfo(method + " - sms code has been sent to trusted phone number.");
							} else {
								failureReason = "sendSmsCodeFailed";
								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,method + " - send SmsCode to trustedphone is failed !!");

								// log the actual error returned
								List<String> errors = sfMsvcResponseInfo.getErrors();
								StringBuffer errorBuf = new StringBuffer();
								if (errors != null) {
									for (String currentError : errors) {
										errorBuf.append(currentError);
									}
								}

								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
										COMPONENT + method + "Error : " + errorBuf.toString());

							}
						} else {
							failureReason = "There may have been a problem sending the sms code to the trusted phone number.., the status was not returned.";
						}

					} else {
						failureReason = "There was an error when sending the sms code to the trusted phone number...";						
					}
				}
			} catch (Exception ex) {
				failureReason = "There was an error when sending the sms code to the trusted phone number";
				SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
						+ "Error while sending 2fa sms code for userId (" + currentUser.getUserId() + "): " + ex);
			}
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			addError(SFAPIServiceResponseError.ERROR_SENDING_SMS_CODE, failureReason);
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		result.put(TWO_FACTOR_AUTH_STATUS, twoFactorAuthStatus);
		result.put(TRUSTED_PHONE_NUMBER, trustedPhoneNumber);
		result.put(SMS_2FA_CODE_SENT, isSms2FACodeSent);

		stat.stop(methStatId, true);
		return result;
	}

    /**
     * This method is intended to resend sms code to merge users (2nd User) trusted phone number,
     *
     * Expected to call this api only after authenticateUserForMerge and merge user 2FA
     * should be Enabled.
     *
     * @param payLoad
     * @return
     * @throws JSONException
     */
    private JSONObject processSendSmsCodeForMergeUser(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processSendSmsCodeForMergeUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        JSONObject jsonOutObj = new JSONObject();
        String failureReason = null;
        String mergeuserTrustedPhoneNumber = null;
        String mergeuserTwoFactorAuthStatus = null;
        boolean isSms2FACodeSent = false;

        String jwtToken = null;

        SFUser mergeUser = null;
        if (null != mergeUserBeanData && null != mergeUserBeanData.getUser() && -1 != mergeUserBeanData.getUser().getUserId()) {
            mergeUser = mergeUserBeanData.getUser();
        } else {
            SFLogger.printWarning(method + " merge user bean object is null, cannot determine 2fa info.");
        }

        if (null != mergeUser) {
            // checks for merge user 2FA is Enabled
            mergeuserTwoFactorAuthStatus = mergeUser.getTwoFactorAuthStatus();
            if (StringUtils.isEmpty(mergeuserTwoFactorAuthStatus)
                    || SFConstant.TWO_FACTOR_AUTH_DISABLED.getDescription().equalsIgnoreCase(mergeuserTwoFactorAuthStatus)) {
                failureReason = "Merge user 2FA is disabled, unable to to process send sms code !!";
                SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
                        COMPONENT + method + "Merge User 2fa disabled, we can't process sending sms code for userId ("
                                + mergeUser.getUserId() + " )");
            }
            // checks for valid trustedPhoneNumber
            mergeuserTrustedPhoneNumber = mergeUser.getTrustedPhoneNumber();
            if (!SFStringUtil.isEmpty(mergeuserTrustedPhoneNumber)) {
                int len = mergeuserTrustedPhoneNumber.length();
                String last4 = null;
                if (len > 4) {
                    int lenRemain = len - 4;
                    last4 = mergeuserTrustedPhoneNumber.substring(lenRemain);
                } else {
                    last4 = mergeuserTrustedPhoneNumber;
                }
                mergeuserTrustedPhoneNumber = SFUtils.maskTrustedPhoneNumber(last4);
            } else {
                failureReason = "MergeUserTrustedPhoneNumberIsEmpty";
                SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
                        COMPONENT + method
                                + "Merge user trustedPhoneNumber is not available, we can't process sending sms code for userId ("
                                + mergeUser.getUserId() + " )");
            }
        } else if (null == mergeUser) {
            failureReason = "UserAuthenticationIsRequired!!";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                    COMPONENT + method + " sf merge user object is null, cannot determine merge user 2fa info.");
        }

        // Start that process to resend SMS code when merge user 2FA is Enabled,has
        // trustedPhoneNumber.
        if (StringUtils.isEmpty(failureReason) && SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription().equalsIgnoreCase(mergeuserTwoFactorAuthStatus)) {
            SFLogger.printInfo(method + " - Merge User - userId (" + mergeUser.getUserId() + ") has 2fa enabled.");

            // send the sms code to the phone
            JSONObject data = new JSONObject();

            // call the acctmgr microservice
            try {

                String mergeUserAuthyId = mergeUserBeanData.getAuthyId();

                if (SFStringUtil.isEmpty(mergeUserAuthyId)) {
                    throw new SFException(
                            "The authy id was empty but is required when Merge user 2FA is enabled. Cannot send sms code to Merg user trusted phone number");
                } else {

                    data.put(AUTHY_ID, mergeUserAuthyId);
                    data.put("ipAddress", determineIpAddress());
                    String sessId = getHashedSessionId();
                    if(sessId == null){
                        sessId = determineSessionIdWithoutHashing();
                    }
                    data.put("sessionId", sessId);
                    int userId = mergeUser.getUserId();
                    String sUserId = Integer.toString(userId);
                    data.put("originatorId", sUserId);
                    data.put("personOrgId", sUserId);
                    data.put("phoneNumber", mergeUser.getTrustedPhoneNumber());

                    SFLogger.printInfo(method + " - attempt to send sms code to Merge User trusted phone number.");

                    jwtToken = getMicroserviceJwtToken(payLoad);

                    SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "sendSmsCode", MS_SEND_SMSCODE_API_CALL, jwtToken);
                    SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

                    if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {

                        String jsonDataOut = responseObj.getData();
                        jsonOutObj = new JSONObject(jsonDataOut);

                        if (jsonOutObj.has(STATUS_ATTR)) {
                            String smsStatus = jsonOutObj.getString(STATUS_ATTR);
                            if ("success".equalsIgnoreCase(smsStatus)) {
                                isSms2FACodeSent = true;
                                SFLogger.printInfo(method + " - sms code has been sent to Merge User trusted phone number.");
                            } else {
                                failureReason = "MergeUserSendSmsCodeFailed";
                                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,method + " - send SmsCode to Merge User trustedphone is failed !!");

                                // log the actual error returned
                                List<String> errors = sfMsvcResponseInfo.getErrors();
                                StringBuffer errorBuf = new StringBuffer();
                                if (errors != null) {
                                    for (String currentError : errors) {
                                        errorBuf.append(currentError);
                                    }
                                }

                                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                                        COMPONENT + method + "Error : " + errorBuf.toString());

                            }
                        } else {
                            failureReason = "There may have been a problem sending the sms code to Merge user trusted phone number.., the status was not returned for merge user ";
                        }

                    } else {
                        failureReason = "There was an error when sending the sms code to Merge User trusted phone number...";
                    }
                }
            } catch (Exception ex) {
                failureReason = "There was an error when sending the sms code to Merge User trusted phone number";
                SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
                        + "Error while sending 2fa sms code for Merge user userId (" + mergeUser.getUserId() + "): " + ex);
            }
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            result.put(FAILURE_REASON, failureReason);
            addError(SFAPIServiceResponseError.ERROR_SENDING_SMS_CODE, failureReason);
            stat.stop(methStatId, false);
        } else {
            stat.stop(methStatId, true);
        }

        result.put(TWO_FACTOR_AUTH_STATUS, mergeuserTwoFactorAuthStatus);
        result.put(TRUSTED_PHONE_NUMBER, mergeuserTrustedPhoneNumber);
        result.put(SMS_2FA_CODE_SENT, isSms2FACodeSent);

        stat.stop(methStatId, true);
        return result;
    }

	/**
   	 * This api is used fetch email address when username provided
   	 * 
   	 * @param payLoad
   	 * @return
   	 * @throws JSONException ,SFFGException
   	 */
    
    private JSONObject verifyEmailGivenUserLoginName(SFJsonObject payLoad) throws JSONException, SFFGException {
		final String method = COMPONENT + ".verifyEmailGivenUserLoginName - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject data = new JSONObject();
		String failureReason = null;
		String userLoginName = payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH);
		boolean isEmailFound = false;
		SFUser sfUser = null;
		try {
		    if(null == userLoginName || userLoginName.isEmpty()){
                SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "Required UserName:"+userLoginName);
                failureReason = "Invalid Input - userLoginName is null or empty";
                throw new Exception("userLoginName is null or empty");
            }
			sfUser = helperManager.getUserHelper().pullSfUserGivenUserLoginName(userLoginName);
			if (sfUser == null) {
				failureReason = "null user returned from FG";
				SFLogger.printDebug(method + " - null user returned from FG...");
			}
		} catch (SFFGException e) {
			failureReason = e.getMessage();
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					method + " - unknown user type returned from FG given user login name, " + userLoginName, e);
		} catch (Exception ex) {
			failureReason = "General-SystemUnavailable";
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex);
		}
		
		String emailAddr = null;
		if(sfUser!=null){
			emailAddr = sfUser.getEmail();
			if (!SFStringUtil.isEmpty(emailAddr)) {
				emailAddr = SFUtils.maskVerifiedEmail(emailAddr);
			}
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			data.put(FAILURE_REASON, failureReason);
			addError(SFAPIServiceResponseError.ERROR_VERIFYING_EMAIL, failureReason);
			isEmailFound = false;
			stat.stop(methStatId, false);
		} else {
			isEmailFound = true;
			data.put(EMAIL, emailAddr);
			stat.stop(methStatId, true);
		}
		data.put(IS_EMAIL_FOUND, isEmailFound);
		return data;
	}

	private JSONObject resetPassword(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".resetPassword - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		SFUserHelper userHelper = null;
		SFCCHelper ccHelper = null;
		JSONObject jsonResponseObj = new JSONObject();
		String failureReason = null;
		String password = payLoad.getField(PASSWORD_PATH);
		String confirmPassword = payLoad.getField(CONFIRM_PASSWORD_PATH);
		String encodedRequestData = payLoad.getField(ENCODED_REQUEST_DATA_PATH);

		SFUserBean userBean = userAccountManager.getUserBean();
		SFUserBean userBean1 = userAccountManager.getUserBean();
		boolean resetPwd = false;
		boolean releaseLock = false;
		boolean invalidateResetPwdLink = false;
		try {
			
			if (SFStringUtil.isEmpty(password)) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "empty password");
				failureReason = "Invalid Input - password is null or empty";
				throw new Exception("password is null or empty");
			}
			if (SFStringUtil.isEmpty(confirmPassword)) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "empty confirmPassword");
				failureReason = "Invalid Input - confirmPassword is null or empty";
				throw new Exception("confirmPassword is null or empty");
			}
			if (!(password.equalsIgnoreCase(confirmPassword))) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "password and confirmPassword does not match");
				failureReason = "Invalid Input - password and confirmPassword does not match";
				throw new Exception("password and confirmPassword does not match");
			}
			if (SFStringUtil.isEmpty(encodedRequestData)) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "empty encodedRequestData");
				failureReason = "Invalid Input - encodedRequestData is null or empty";
				throw new Exception("encodedRequestData is null or empty");
			}
			
			ccHelper = helperManager.getCCHelper();
			userHelper = helperManager.getUserHelper();
			
			PwResetRequestData data = ccHelper.getPwResetEmailRequest(encodedRequestData);
			if ((data == null) || !data.isRequestValid()) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG, method + ": invalid reset link ");
				failureReason = "InvalidResetLink";
				throw new Exception("InvalidResetLink");
			}

			int userId = (int) data.getUserId();
			userBean.setUserId(userId);
			userBean.getSfCredential().setOriginatorId(userBean.getUserId());
			userBean.setUserLoginName(userHelper.queryLoginName(userId));
			userBean.setPassword(password);
			userBean.setConfirmPassword(confirmPassword);
			boolean flag = userHelper.checkFraudAndSaveLoginAttempt(userBean.getUser(), userBean.getOriginatorId());
			if (flag == false) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG,
						method + ": failed fraud check for reset link (" + encodedRequestData + "), userId="
								+ userBean.getUserId());
				failureReason = "SFPasswordAction-ExpiredLink";
				throw new Exception("InvalidExpiredLink");
			}

			if (data.isRequestExpired()) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG,
						method + ": expired reset link (" + encodedRequestData + "), userId=" + +userBean.getUserId());
				failureReason = "SFPasswordAction-ExpiredLink";
				throw new Exception("InvalidExpiredLink");
			}
			/*
			 * Clear Customer Server data repository, this will create new
			 * Customer Server CORBA objects
			 */
			helperManager.resetDataRepository();

			// Get customer & domain helper from helper manager
			userHelper = helperManager.getUserHelper();

			// get CC Helper
			ccHelper = helperManager.getCCHelper();

			// Track the ip for all user updates TR 24924

			userBean.setFraudProtection(userBean1.getFraudProtection());

			try{
				 userHelper.updateUserPassword(userBean.getUser());
				 resetPwd = true;
			} catch (SFFGException e) {
				resetPwd = false;
				failureReason = e.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG, method + ": " + failureReason);
				if (e.getFgExceptionType() == 33) {
					failureReason = "passwordSameAsOldPassword";
				}
			} 
			
			// encodedRequstData will only be set if the user came here via a
			// password reset link in an email.
			if (encodedRequestData != null) {
				ccHelper.invalidateEmailRequest(encodedRequestData);
				invalidateResetPwdLink = true;
			}

			// Release both login (L) & security challenge question (C) locks
			final String lockTypes[] = new String[2];
			lockTypes[0] = "L";
			lockTypes[1] = "C";
			userHelper.releaseLockForUser(userBean.getUser(), lockTypes);
			releaseLock = true;
			}
			catch (SFCCGException e) { 
				failureReason = e.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG, method + ": " + failureReason);
			}	
		 catch (RuntimeException e) {
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					method + ": Runtime exception : " + e.getMessage());
			failureReason = "General-SystemUnavailable";
		} catch (Exception e) {
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					method + ": exception : " + e.getMessage());
			failureReason =  e.getMessage();
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			jsonResponseObj.put(FAILURE_REASON, failureReason);
			addError(SFAPIServiceResponseError.ERROR_RESET_PASSWORD, failureReason);
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}
		jsonResponseObj.put("resetPwd", resetPwd);
		jsonResponseObj.put("releaseLock",releaseLock);
		jsonResponseObj.put("invalidateResetPwdLink",invalidateResetPwdLink);
		return jsonResponseObj;
	}

    private JSONObject processGetUserRole(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processGetUserRole - ";
        JSONObject jsonResponseObj = new JSONObject();
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        String failureReason = null;
        boolean isErrorSpecified = false;

        try {

            SFUser user = userAccountManager.getUserBean().getUser();
            Integer accountId = payLoad.getNumericField(ACCOUNT_ID_PATH);

            if (user.getUserId() == -1) {
                failureReason = "loginRequired";
                addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
                isErrorSpecified = true;
            } else if (user.getAccounts() != null && isAccountFoundForUser(user, accountId)) {

                RoleEnum role = userAccountManager.getRelation( user.getUserId(), accountId );
                jsonResponseObj.put(ROLE_ATTR, role);

            } else {
                failureReason = "AccountIdDoesNotBelongToUser";
                addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountId);
                isErrorSpecified = true;
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountId+" does not belong to user "+ user.getUserId() );
            }
        }
        catch(Exception ex){
            failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT + method + "Error : ",ex);
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            jsonResponseObj.put(FAILURE_REASON, failureReason);
            if(!isErrorSpecified){
                addError(SFAPIServiceResponseError.ERROR_GET_USER_ROLE, failureReason);
            }
            stat.stop(methStatId, false);
        }
        else {
            stat.stop(methStatId, true);
        }

        return jsonResponseObj;
    }

    private JSONObject processCheckUserActionPermission(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + "processCheckUserActionPermission";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        String failureReason = null;
        boolean isErrorSpecified = false;
        boolean isPermissionAllowed = false;

        JSONObject jsonResponseObj = new JSONObject();

        try {
            SFUserBean userBean = productManager.getUserAccountManager().getUserBean();

            SFUser user = productManager.getUserAccountManager().getUserBean().getUser();
            int accountId = payLoad.getNumericField(ACCOUNT_ID_PATH) != null ? payLoad.getNumericField(ACCOUNT_ID_PATH) : userBean.getCurrentAccount().getAccountId();

            if (user.getUserId() == -1) {
                failureReason = "loginRequired";
                addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
                isErrorSpecified = true;
            } else if (accountId == 0) {
                failureReason = "AccountIdIsEmpty";
                addError(SFAPIServiceResponseError.ERROR_GET_ACCOUNT_DETAILS, failureReason);
                isErrorSpecified = true;
            } else if (user.getAccounts() != null && isAccountFoundForUser(user, accountId)) {

                boolean flag = true;

                SFAccount account = productManager.getUserAccountManager().getAccount(accountId);
                Map constraintData = new Hashtable();
                constraintData.put(SFRetailAccountConstraintChecker.ACCOUNT_TYPE, account.getAccountChannelType());
                constraintData.put(SFSuspendedAccountConstraintChecker.ACCOUNT_STATUS, new Integer(account.getStatus()));
                constraintData.put(SFESEAccountConstraintChecker.ACCOUNT_TYPE, account.getAccountChannelType());
                constraintData.put(SFESEAccountConstraintChecker.IS_CSR, productManager.getUserAccountManager().getUserBean().getUser().isCSRRep());

                TaskEnum taskCode = TaskEnum.valueOf(payLoad.getField(TASK_PATH));
                ResourceEnum resourceCode = ResourceEnum.valueOf(payLoad.getField(RESOURCE_PATH));
                
                RoleEnum roleCode = productManager.getUserAccountManager().getRelation( user.getUserId(), accountId );

                if ( roleCode == null ){
                    SFLogger.getInstance().logError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,  method +
                            "cannot find the role for this user id: " + user.getUserId() +
                            " and account id: " + accountId, null);
                }

                SFPermissionRequest permissionRequest = new SFPermissionRequest(taskCode, roleCode, resourceCode);
                isPermissionAllowed = accessController.checkPermission(permissionRequest, constraintData);

            }
            else {
                failureReason = "AccountIdDoesNotBelongToUser";
                addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountId);
                isErrorSpecified = true;
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountId+" does not belong to user "+ user.getUserId() );
            }
        }
        catch(IllegalArgumentException exception) {
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                    method + ": received exception ",exception);
            failureReason = "Invalid Request Parameters";
            addError(SFAPIServiceResponseError.ERROR_INVALID_CHECK_USER_ACTION_PERMISSION_REQUEST, failureReason);
            isErrorSpecified = true;
        }
        catch(Exception ex){
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                    method + ": received exception ",ex);
            failureReason = "General-SystemUnavailable";
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            jsonResponseObj.put(FAILURE_REASON, failureReason);
            if(!isErrorSpecified){
                addError(SFAPIServiceResponseError.ERROR_CHECK_USER_ACTION_PERMISSION, failureReason);
            }
            stat.stop(methStatId, false);
        } else {
            stat.stop(methStatId, true);
        }

        jsonResponseObj.put(HAS_PERMISSION, isPermissionAllowed);

        return jsonResponseObj;
    }

    public JSONObject processCheckEditBillingInfoPermission(SFJsonObject payLoad) throws JSONException {
		
        final String method = COMPONENT + "processCheckEditBillingInfoPermission";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
        
        JSONObject jsonResponseObj = new JSONObject();
        
        int userId = -1;
        String failureReason = null;
        boolean isErrorSpecified = false;
        boolean isPermissionAllowed = false;
        
        JSONObject result = new JSONObject();

        Integer accountId = payLoad.getNumericField(ACCOUNT_ID_PATH);
        
        try {
        	        	
        	SFUser user = userAccountManager.getUserBean().getUser();
    		if (user.getUserId() == -1) {
    			failureReason = "loginRequired";
    			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
    			isErrorSpecified = true;
    		} else {
        	    userId = user.getUserId();
	        	if (accountId == null) {
					failureReason = "AccountIdIsEmpty";
					result.put(FAILURE_REASON, failureReason);
					addError(SFAPIServiceResponseError.REQUEST_ACCOUNT_ID_NOT_FOUND, failureReason);
					isErrorSpecified = true;
					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId has to be specified on the request.");
				}
	        	else{
					boolean match = false;
					if (user.getAccounts() != null) {
						match = isAccountFoundForUser(user, accountId);
					}else{
						SFLogger.printDebug(method + ": user "+ user.getUserId()+" does not have accounts ");
					}
					if (!match) {
						failureReason = "AccountIdDoesNotBelongToUser";
						result.put(FAILURE_REASON, failureReason);
						addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountId);
						isErrorSpecified = true;
						SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountId+" does not belong to user "+ user.getUserId() );
						
					}
					else{
					   String resultFlag = "false";
					   boolean flag = true;
					   String taskStr = "EDIT_BILLING_INFO";
		        	   String resourceStr = "Account";
		        	   
		               TaskEnum taskCode = TaskEnum.valueOf( taskStr );
		               ResourceEnum resourceCode = ResourceEnum.valueOf( resourceStr );
		               
		               
		               RoleEnum roleCode = userAccountManager.getRelation( userId, accountId );

	                   if ( roleCode == null ){
	                        SFLogger.getInstance().logError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,  method +
	                            "cannot find the role for this user id: " + userId +
	                            " and account id: " + accountId + 
								" .The task code is: " + taskStr + " and the resource code is: " + resourceStr, null);
	                        resultFlag = "error";
	                   }
		               

		               if ( taskCode != null  && resourceCode != null ){
		                    SFPermissionRequest permissionRequest =  new SFPermissionRequest( taskCode, roleCode, resourceCode );

		                    flag = accessController.checkPermission( permissionRequest );
		                   
		                    if ( !resultFlag.equals("error") )
		                    {
		                        if (flag){
		                        	resultFlag = "true";
		                        	isPermissionAllowed = true;
		                        }
		                        else{
		                        	resultFlag = "false";
		                        }
		                    }
		                }
					}
	        	}
    		}
        }
        catch(Exception ex){
           SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
              method + ": received exception ",ex);
           failureReason = "General-SystemUnavailable";
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
			jsonResponseObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_CHECK_EDIT_BILLING_INFO_PERMISSION, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}
        
        jsonResponseObj.put(HAS_PERMISSION, isPermissionAllowed);
		
		return jsonResponseObj;
	}

    private JSONObject processCheckUserHasRequestAuthCodePermission(SFJsonObject payLoad) throws JSONException {

        final String method = COMPONENT + "processCheckUserHasRequestAuthCodePermission";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject jsonResponseObj = new JSONObject();

        int userId = -1;
        String failureReason = null;
        boolean isErrorSpecified = false;
        boolean canRequestAuthCode = false;

        JSONObject result = new JSONObject();

        try {
            SFUserBean userBean = userAccountManager.getUserBean();
            userId = userBean.getUser().getUserId();

            if (userId == -1) {

                failureReason = "loginRequired";
                addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
                isErrorSpecified = true;
            } else {
                SFCredential sfCredential = userBean.getSfCredential();
                User user = helperManager.getUserHelper().pullUser(userId, sfCredential,
                        null);

                if (null != user) {
                    if (user.getUserProfiles() != null || user.getUserProfiles().length > 0) {
                        for (UserProfile userProfile : user.getUserProfiles()) {
                            if (userProfile.getProfileName().equalsIgnoreCase(DOM_REQUEST_AUTH_CODE)
                                    && userProfile.getProfileValue().equalsIgnoreCase(DOM_REQUEST_AUTH_CODE_TRUE_VALUE)) {
                                canRequestAuthCode = true;
                            }
                        }
                    }
                } else {
                    failureReason = "user not found";
                    SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
                            COMPONENT + method + " - error: unable to fetch User");
                }
            }
        }
        catch (SFFGException e) {
            failureReason = "unable to fetch data from FG";
            addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_FETCH_DATA_FROM_FG, "UserID= " + userId, e.getMessage());
            isErrorSpecified = true;
        } catch (Exception ex) {
            failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ", ex);
        }


        if (StringUtils.isNotEmpty(failureReason)) {
            jsonResponseObj.put(FAILURE_REASON, failureReason);
            if(!isErrorSpecified){
                addError(SFAPIServiceResponseError.ERROR_GENERIC, failureReason);
            }
            stat.stop(methStatId, false);
        } else {
            jsonResponseObj.put(USER_ID_ATTR, userId);
            jsonResponseObj.put(CAN_REQUEST_AUTHCODE, canRequestAuthCode);
            stat.stop(methStatId, true);
        }

        return jsonResponseObj;
    }

	 private JSONObject processCheckEditCreditCardPermission(SFJsonObject payLoad) throws JSONException {
			
	        final String method = COMPONENT + "processCheckEditCreditCardPermission";
	        SFMethodStatistics stat = SFMethodStatistics.getInstance();
			long methStatId = stat.start(method);
	        
	        JSONObject jsonResponseObj = new JSONObject();
	        
	        int userId = -1;
	        String failureReason = null;
	        boolean isErrorSpecified = false;
	        boolean isPermissionAllowed = false;
	        
	        JSONObject result = new JSONObject();

	        Integer accountId = payLoad.getNumericField(ACCOUNT_ID_PATH);
	        
	        try {
	        	        	
	        	SFUser user = userAccountManager.getUserBean().getUser();
	    		if (user.getUserId() == -1) {
	    			failureReason = "loginRequired";
	    			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
	    			isErrorSpecified = true;
	    		} else {
	    			userId = user.getUserId();
		        	if (accountId == null) {
						failureReason = "AccountIdIsEmpty";
						result.put(FAILURE_REASON, failureReason);
						addError(SFAPIServiceResponseError.REQUEST_ACCOUNT_ID_NOT_FOUND, failureReason);
						isErrorSpecified = true;
						SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId has to be specified on the request.");
					}
		        	else{
						boolean match = false;
						if (user.getAccounts() != null) {
							match = isAccountFoundForUser(user, accountId);
						}else{
							SFLogger.printDebug(method + ": user "+ user.getUserId()+" does not have accounts ");
						}
						if (!match) {
							failureReason = "AccountIdDoesNotBelongToUser";
							result.put(FAILURE_REASON, failureReason);
							addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountId);
							isErrorSpecified = true;
							SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountId+" does not belong to user "+ user.getUserId() );
							
						}
						else{
						   String resultFlag = "false";
						   boolean flag = true;
						   String taskStr = "EDIT_CREDIT_CARD";
			        	   String resourceStr = "Account";
			        	   
			               TaskEnum taskCode = TaskEnum.valueOf( taskStr );
			               ResourceEnum resourceCode = ResourceEnum.valueOf( resourceStr );
			               
			               
			               RoleEnum roleCode = userAccountManager.getRelation( userId, accountId );

		                   if ( roleCode == null ){
		                        SFLogger.getInstance().logError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,  method +
		                            "cannot find the role for this user id: " + userId +
		                            " and account id: " + accountId + 
									" .The task code is: " + taskStr + " and the resource code is: " + resourceStr, null);
		                        resultFlag = "error";
		                   }
			               

			               if ( taskCode != null  && resourceCode != null ){
			                    SFPermissionRequest permissionRequest =  new SFPermissionRequest( taskCode, roleCode, resourceCode );

			                    flag = accessController.checkPermission( permissionRequest );
			                   
			                    if ( !resultFlag.equals("error") )
			                    {
			                        if (flag){
			                        	resultFlag = "true";
			                        	isPermissionAllowed = true;
			                        }
			                        else{
			                        	resultFlag = "false";
			                        }
			                    }
			                }
						}
		        	}
	    		}
	        }
	        catch(Exception ex){
	           SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
	              method + ": received exception ",ex);
	           failureReason = "General-SystemUnavailable";
	        }
	        
	        if (StringUtils.isNotEmpty(failureReason)) {
				jsonResponseObj.put(FAILURE_REASON, failureReason);
				if(!isErrorSpecified){
				   addError(SFAPIServiceResponseError.ERROR_CHECK_EDIT_CREDIT_CARD_PERMISSION, failureReason);
				}
				stat.stop(methStatId, false);
			} else {
				stat.stop(methStatId, true);
			}
	        
	        jsonResponseObj.put(HAS_PERMISSION, isPermissionAllowed);
			
			return jsonResponseObj;
		}

		
	/**
	 * 
	 * For new AM, user should land on 
	 * https://www.register.com/my-account/account-center/login/reset-password?request=5945eca0f418044a5c2879dfa3e7b705, 
	 * from this page, we need SFCore API call that has the same logic in SFForgetPasswordWebTask.
	 * 
	 * @param payLoad
	 * @return
	 * @throws JSONException
	 */
	private JSONObject processGetDataForForgotPassword(SFJsonObject payLoad) throws JSONException {
		
        final String meth = COMPONENT + "processGetDataForForgotPassword";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long statId = stat.start(meth);
        
        JSONObject jsonResponseObj = new JSONObject();
        
        String userLoginName = null;
        int userId = -1;
        String failureReason = null;
        boolean isErrorSpecified = false;
        boolean isResetPasswordAllowed = false;

        String resetData = null;
        
        try {
        
	        resetData = payLoad.getField(RESET_DATA_PATH);
	
	        if ( resetData == null) {
	           failureReason = "resetData is null";
	           addError(SFAPIServiceResponseError.ERROR_RESET_DATA_NOT_SPECIFIED, failureReason);
	           isErrorSpecified = true;
	        }
	        else{
	           resetData = resetData.trim(); //CCG is sending leading and trailing spaces for resetData
	        
	           SFCCHelper ccHelper = helperManager.getCCHelper();
	           SFUserHelper userHelper = helperManager.getUserHelper();
	           
	           SFUserBean userBean = userAccountManager.getUserBean();
	
	           PwResetRequestData data = ccHelper.getPwResetEmailRequest(resetData);
	           if ( (data == null) || !data.isRequestValid() ) {
	              SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG, meth + ": invalid reset link ("+resetData+") , userId=" + userBeanPassword.getUserId());
	              SFErrorHolder err = new SFErrorHolder();
	              err.addFormError("SFPasswordAction-InvalidResetLink");
	              String errorMsg = err.getErrorMessages().replace("|", "");
	              
	              addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
	              failureReason = "InvalidResetLink";
	              isErrorSpecified = true;
	           }
	           else{
	              userId = (int) data.getUserId();
	              userBeanPassword.setUserId(userId);
	              userBeanPassword.getSfCredential().setOriginatorId( userBeanPassword.getUserId() );
	              userBeanPassword.setUserLoginName(userHelper.queryLoginName(userId));
	              SFUser currentUser = userBeanPassword.getUser();
	              if(currentUser != null){
	            	 SFFraudProtection fraudProtection = userBean.getFraudProtection();
	                 currentUser.setFraudProtection(fraudProtection);
	              }
	
	              boolean flag = userHelper.checkFraudAndSaveLoginAttempt( userBeanPassword.getUser(), 
	            		  userBeanPassword.getOriginatorId() );
	              
	              if (flag == false){
	                 SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG,
	                    meth + ": failed fraud check for reset link ("+resetData+"), userId=" +
			           userBeanPassword.getUserId());
	                 	                 
	                 SFErrorHolder err = new SFErrorHolder();
		             err.addFormError("SFPasswordAction-ExpiredLink");
		             String errorMsg = err.getErrorMessages().replace("|", "");
		              
		             addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
		             failureReason = "ExpiredLink";
		             isErrorSpecified = true;
	                 
	              }
	              else{
	                 if (data.isRequestExpired()){
	                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_CCG,
	                          meth + ": expired reset link ("+resetData+"), userId=" +
	                          +userBeanPassword.getUserId());
	                    
	                    SFErrorHolder err = new SFErrorHolder();
			            err.addFormError("SFPasswordAction-ExpiredLink");
			            String errorMsg = err.getErrorMessages().replace("|", "");
			              
			            addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
			            failureReason = "ExpiredLink";
			            isErrorSpecified = true;
	                    
	                 }
	                 else{
	                	 //success
	                	 userLoginName = userBeanPassword.getUserLoginName();
	                	 isResetPasswordAllowed = true;
	                 }
	              }
	           }
	       }
        }
        catch(SFCCGException ccge) {
           SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
              meth + ": received ccg exception for reset link ("+resetData+") ",ccge);
           
           SFErrorHolder err = new SFErrorHolder();
           err.addFormError(ccge.getErrorMsgKey());
           String errorMsg = err.getErrorMessages().replace("|", "");
           
           addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
           failureReason = "ccg error occurred";
           isErrorSpecified = true;
           
        }
        catch(SFFGException fge){
           SFErrorHolder err = new SFErrorHolder();
           if ( fge.getFgExceptionType() == SFFGException.VALIDATION ){
              SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                 meth + ": invalid reset link ("+resetData+") ");
                             
              err.addFormError("SFPasswordAction-InvalidResetLink");
              String errorMsg = err.getErrorMessages().replace("|", "");
	          addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
              failureReason = "InvalidResetLink";
              
           }
           else{
              SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                 meth + ": received fg exception for reset link ("+resetData+") ",fge);
           

	           err.addFormError(fge.getErrorMsgKey());
	           
	           String errorMsg = err.getErrorMessages().replace("|", "");
	           addError(SFAPIServiceResponseError.ERROR_GENERIC,errorMsg);
	           failureReason = "fg error occurred";
           }
           
           isErrorSpecified = true;
        } 
        catch(Exception ex){
           SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
              meth + ": received exception for reset link ("+resetData+") ",ex);
           failureReason = "General-SystemUnavailable";
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
			jsonResponseObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_GET_USER_NAME_FOR_FORGOT_PASSWORD, failureReason);
			}
			stat.stop(statId, false);
		} else {
			stat.stop(statId, true);
			jsonResponseObj.put(USER_LOGIN_NAME, userLoginName);
			jsonResponseObj.put(USER_ID_ATTR, userId);
		}
        
        jsonResponseObj.put(IS_RESET_PASSWORD_ALLOWED, isResetPasswordAllowed);
		
		return jsonResponseObj;
	}

	/***
     * This method is used to trigger a reset password email to the user based on a valid login name.
     *
     * @param payLoad
     * @return JSONObject - {"response":{"data":{"resetLinkSent":true},"responseInfo":{"errors":null,"status":"Success"}}}
     * @throws JSONException
     */
    private JSONObject processForgotPassword(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processForgotPassword - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject jsonOutObj = new JSONObject();
        String failureReason = null;
        String userName = payLoad.getField(REQUEST_ATTRIBUTE_USERNAME_PATH);

        boolean resetLinkSent = false;

        try{
            if ( (userName == null) || userName.equals("") )
            {
                SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER,
                        method + "empty user login name");
                failureReason = "Invalid Input - username is null or empty";
                throw new Exception("username is null or empty");
            }
            
			// Check whether given username is belongs to same channel, calling
			// authenticateLogin with dummy password
			SFUser tempUser = new SFPerson();
			tempUser.setUserLoginName(userName);
			tempUser.setPassword("xxxxxxxxxx");
			tempUser.setConfirmPassword("xxxxxxxxxx");
			tempUser.setFraudProtection(userAccountManager.getUserBean().getFraudProtection());
			SFAuthenticationResult authenticationResult = helperManager.getUserHelper().authenticateLogin(tempUser,
					SFLoginTypeConstant.MANUAL, -ONE, (long) SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue());

			if (authenticationResult == null || (authenticationResult.getErrorMessage() != null
					&& authenticationResult.getErrorMessage().startsWith("No data found in edb for loginName:"))) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER,
						method + "no user data found for username " + userName);
				failureReason = "No Data Found - no user found for username - " + userName;
			}
            
			if (null == failureReason) {
            helperManager.resetDataRepository();
            SFUserHelper userHelper = helperManager.getUserHelper();


            SFLogger.printDebug(method + "Checking user wholesale status...");
            SFUser sfWholesaler = userHelper.isUserOrDomainAssociatedWithWholesaleOnly(userName, "-1");

            // If the user is associated with wholesale accounts only, return error message.
            if ( sfWholesaler != null )
            {
                SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR,
                        method + "wholesale user");
                failureReason = "Invalid User - user is a wholesaler";
                throw new Exception("user is a wholesaler");
            }

            SFUser user = userHelper.pullSfUserGivenUserLoginName(userName);

            // if the user is null, then there is no record found in the edb.
            if(user == null || user.getUserId() == -1){
                SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER,
                        method + "no user data found for username " + userName);
                failureReason = "No Data Found - no user found for username - " + userName;
                throw new Exception("no user found for username " + userName);
            }else{
                userBeanPassword.clear();
                userBeanPassword.setUser(user);
                userBeanPassword.setUserLoginName(userName);
                int userId = user.getUserId();

                //NSI and RCOM password plus flow
                if ((SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue() == SFMTChannelConstants.DEFAULT_NSI_PARENT_CHANNEL_ID ||
                        SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue() == SFMTChannelConstants.DEFAULT_REGISTER_PARENT_CHANNEL_ID)
                        && userBeanPassword.isPasswordPlusEnabled() && !userBeanPassword.getPasswordPlusInfo().getIsAuthenticated()) {
                    failureReason = "Password Plus enabled user.";
                    addWarning(SFAPIServiceResponseError.WARNING_PASSWORD_PLUS_ENABLED_USER, failureReason);
                    stat.stop(methStatId, true);
                    jsonOutObj.put("resetLinkSent", resetLinkSent);
                    return jsonOutObj;
                }else{
                    String clientIp = userAccountManager.getUserBean().getUser().getFraudProtection().getIpAddress();
                    SFCCHelper ccHelper = helperManager.getCCHelper();
                    if (!ccHelper.userHasActivePasswordResetRequest(userId)) {
                        ccHelper.sendPasswordResetLinkEmail(userId, clientIp);
                        resetLinkSent = true;
                        String email = user.getEmail();
						if (!SFStringUtil.isEmpty(email)) {
							email = SFUtils.maskVerifiedEmail(email);
							jsonOutObj.put("userEmail", email);
						}
                        SFLogger.printInfo(method + "reset email sent to user " + userName);
                    } else {
                        SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR,
                                method + "password reset link already passed to user - " + userName);
                        failureReason = "Reset Link Already Sent";
                        throw new Exception("reset password link already sent to user");
                    }
                }
            }
		  }
        } catch (SFFGException e ) {
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                    method + e.getMessage(), e);
            failureReason = "General - FG System Unavailable";
        } catch (SFCCGException e) {
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                    method + e.getMessage(), e);
            failureReason = "General - CCG System Unavailable";
        } catch (Exception e) {
        	failureReason = "General - exception occurred: " + e.getMessage();
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM_FG,
                    method + e.getMessage(), e);
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            jsonOutObj.put(FAILURE_REASON, failureReason);
            addError(SFAPIServiceResponseError.ERROR_FORGOT_PASSWORD, failureReason);
            stat.stop(methStatId, false);
        } else {
            stat.stop(methStatId, true);
        }

        jsonOutObj.put("resetLinkSent", resetLinkSent);
        return jsonOutObj;
    }

    /**
	 * This api is used serve to send an email with forgot username details on given email or domain.
	 * 
	 * @param payLoad
	 * @return
	 * @throws JSONException
	 */
	private JSONObject forgotUsername(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".forgotUsername - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject jsonOutObj = new JSONObject();
		String failureReason = null;
		String email = payLoad.getField(REQUEST_EMAIL_PATH);
		String domainName = payLoad.getField(REQUEST_DOMAIN_PATH);

		if (email != null && domainName != null && email.trim().length() > 0 && domainName.trim().length() > 0) {
			failureReason = "BothDomainAndEmailEntered";
		} else if (email != null && email.trim().length() > 0) {
			jsonOutObj = sfForgotUserNameUtil.findUsernameByEmail(email);
			failureReason = jsonOutObj.has(FAILURE_REASON) ? jsonOutObj.getString(FAILURE_REASON): null;
		} else if (domainName != null && domainName.trim().length() > 0) {
			jsonOutObj = sfForgotUserNameUtil.findUsernameByDomain(domainName);
			failureReason = jsonOutObj.has(FAILURE_REASON) ? jsonOutObj.getString(FAILURE_REASON): null;
		} else {
			failureReason = "EmptyEmailOrDomainEntered";
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			addError(SFAPIServiceResponseError.ERROR_FORGOT_USERNAME, failureReason);
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		return jsonOutObj;
	}

	/***
     * This method is used to update user Pin.
     * @param payLoad - payload holds the 6-digit user Pin.
     * @return
     * @throws SFException
     */
	private JSONObject updatePin(SFJsonObject payLoad) throws SFException, JSONException {
		final String method = COMPONENT + ".updatePin - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
		JSONObject jsonOutObj = new JSONObject();
		
		String failureReason = null;
		boolean isUpdateSuccessful = false;
		boolean isErrorSpecified = false;
		
		String jwtToken = null;
				
		String pin = payLoad.getField(REQ_FIELD_PIN);

		// Validation User Pin Business Validations
		if (!SFUtils.validatePin(pin)) {
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					COMPONENT + method + "User Pin Validation Error" + pin);
			jsonOutObj.put(FAILURE_REASON, "PINValidationFailed");
			jsonOutObj.put(IS_UPDATE_SUCCESSFUL, false);
			addError(SFAPIServiceResponseError.REQUEST_INVALID_USER_PIN, failureReason);
			stat.stop(methStatId, false);
			return jsonOutObj;
		}
		

        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
        }
		else{
				// call the acctmgr microservice
				try {
                    SFUser currentUser = userAccountManager.getUserBean().getUser();
				// if new PIN is same as current PIN, then we won't call 2FA to update user PIN
					if(currentUser.getPin() != null && currentUser.getPin().equalsIgnoreCase(pin)){
						//failureReason = "pinEnteredUnchanged";
						isUpdateSuccessful = true;
						SFLogger.printDebug(method + ": User Pin Entered Unchanged !!");
			        }
					else{
						
						jwtToken = getMicroserviceJwtToken(payLoad);
						
						JSONObject data = new JSONObject();
						data.put(PIN, pin);
		
		                SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "resetCredential from updatePin", MS_RESET_CREDENTIAL_API_CALL, jwtToken);
		        		SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();
						jsonOutObj = new JSONObject(sfMsvcResponseInfo);
						
			            	
			            	//expected return data
				            /*
				            { 
				            	   "response":{
				            	     "responseInfo":{
				            	       "status":"Success",
				            	       "errors":""
				            	     }
				            	   }
				            	}
				            */
			            	
			               if("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())){
			            	   //update succeeded so update the value in session 
			            	   currentUser.setPin(pin);
			            	   isUpdateSuccessful = true;
			            	   String jsonDataOut = responseObj.getData();
					           JSONObject jsonOutDataObj = new JSONObject(jsonDataOut);
					           if (jsonOutDataObj.has(hasEmailSentStatus)) {
					            	boolean emailStatusReturned = jsonOutDataObj.getBoolean(hasEmailSentStatus);
					            	if (!emailStatusReturned) {
					            		addWarning(SFAPIServiceResponseError.WARNING_FAILED_TO_SEND_EMAIL, failureReason);
					            	}
					            }
								// clear Missing Pin alert from account manager
								alertManager.removeAlert(SFAlert.NAME_MISSING_PIN);
			               }
			               else{
			            	   failureReason = "pinUpdateFailed";
			               }
					}
		            
	
				} catch (Exception ex) {
					failureReason = "General-SystemUnavailable";
	
		            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
		                    method + "Error : " + ex);
				}
			
		}
		
		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_UPDATE_PIN, failureReason);
			}
            isUpdateSuccessful = false;
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
		
		jsonOutObj.put(IS_UPDATE_SUCCESSFUL, isUpdateSuccessful);
		
		stat.stop(methStatId, true);

		return jsonOutObj;
	}

	/***
     * This method is used to update user credential info.
     * @param payLoad - payload holds the userLoginName, and password
     * @return
     * @throws SFException
     */
	private JSONObject performUpdateUserCredentialInfo(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".performUpdateUserCredentialInfo - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
        boolean isUpdateSuccessful = false;
        
        boolean isUpdatingChallengeQuestions = false;
        boolean isUserNameChanged = false;
        boolean isUpdatingPassword = false;
        SFAPIServiceResponseError errorFound = null;
        
		JSONObject result = new JSONObject();
		
		String jwtToken = null;
		
		
			try {
				SFUserBean currentUser = userAccountManager.getUserBean();
				if (currentUser == null || !currentUser.getIsLoggedIn() || currentUser.getUser().getUserId() == -1) {
					failureReason = "loginRequired";
					errorFound = SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN;
				} 
				else{
				
					JSONObject data = new JSONObject();
					
					String userLoginName = payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH);
					
					if(SFStringUtil.isEmpty(userLoginName)){
						failureReason = "userLoginNameRequired";
					}
					else{
					
					
						String password = payLoad.getField(PASSWORD_PATH);
						String confirmPassword = payLoad.getField(CONFIRM_PASSWORD_PATH);

						Vector<String> errVector = new Vector<String>();
						SFBeanValidator beanValidator = SFBeanValidator.getInstance();
						
						
						
						SFUser currentSfUser = userAccountManager.getUserBean().getUser();
		
						
						if (!userLoginName.equalsIgnoreCase(currentSfUser.getUserLoginName())) {
							beanValidator.validateUserName(userLoginName, errVector);
							isUserNameChanged = true;
						}
						
						
						if(isUserNameChanged){
							SFLogger.printInfo(method + " username change detected, existing username ("+currentSfUser.getUserLoginName()+") and new username ("+userLoginName+") ");
							boolean isErrorFound = false;
							int size = errVector.size();
							StringBuffer errorMessageBuf = new StringBuffer();
							for (int i = 0; i < size; i++) {
								errorMessageBuf.append(errVector.elementAt(i).toString());
								isErrorFound = true;
							}
	
							if (isErrorFound) {						
								String errors = errorMessageBuf.toString();
								if(errors != null && errors.indexOf("InvalidFormat") != -1){
									failureReason = "The username has an invalid format";
									errorFound = SFAPIServiceResponseError.ERROR_UPDATE_USERNAME_INVALID_CHARACTER;
								}
								else if(errors != null && (errors.indexOf("RequireMinLength") != -1 ||
										errors.indexOf("ExceedMinLength") != -1)){
									failureReason = "The username should have a minimum length of 8 characters";
									errorFound = SFAPIServiceResponseError.ERROR_UPDATE_USERNAME_MIN_LENGTH;
								}
								else if(errors != null && errors.indexOf("ExceedMaxLength") != -1){
									failureReason = "The username should have a maximum length of 100 characters";
									errorFound = SFAPIServiceResponseError.ERROR_UPDATE_USERNAME_MAX_LENGTH;
								}
								else{
									failureReason = "The username is invalid";
								}
								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
					                    method + "Error : " + errorMessageBuf.toString());
							}
							else{
								//check if the username exists. If it aleady exists dont allow the user to choose it
								if(!isUserIdAvailable(userLoginName)){
									failureReason = "The username is already existing";
									SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
						                    method + "Error : username is already existing");
									errorFound = SFAPIServiceResponseError.ERROR_UPDATE_USERNAME_NOT_AVAILABLE;
									
								}
							}
						}
						
						if(SFStringUtil.isEmpty(failureReason)){


							SFUserBean userBean = new SFUserBean();
							
							SFPerson tempUser = new SFPerson();
							tempUser.setUserLoginName(userLoginName);

							if(!SFStringUtil.isEmpty(password) && !SFStringUtil.isEmpty(confirmPassword)){
								tempUser.setPassword(password);
								tempUser.setConfirmPassword(confirmPassword);
								isUpdatingPassword = true;
							}
                            if(!isUserNameChanged && isUpdatingPassword){
                                JSONObject verifyResult = verifyPermission(payLoad);
                                if(verifyResult.has(FAILURE_REASON)){
                                    return verifyResult;
                                }
                            }
							
							userBean.setUser(tempUser);
							
							boolean isPasswordChanged = false;
							String oldPasswd = null;
							
							if(isUpdatingPassword){
							   oldPasswd = currentSfUser.getPassword();
							   String newPasswd = tempUser.getPassword();
			
							   if (newPasswd != null &&
									!newPasswd.equalsIgnoreCase(MASKED_PASSWORD) &&
									!newPasswd.equalsIgnoreCase(oldPasswd)) {
								   isPasswordChanged = true;
							   }
							}
							
							//jayw, TR 34243, only check if password valid if not a masked password.
							if (isPasswordChanged) {
								beanValidator.isValidPasswords(
										userBean.getPassword(),
										userBean.getConfirmPassword(),
										errVector);			
							}
							
							int size = errVector.size();
							StringBuffer errorMessageBuf = new StringBuffer();
							boolean isErrorFound = false;
							for (int i = 0; i < size; i++) {
								errorMessageBuf.append(errVector.elementAt(i).toString());
								isErrorFound = true;
							}
		
							if (isErrorFound) {						
								String errors = errorMessageBuf.toString();
								if(errors != null && errors.indexOf("SFBeanValidator-NotSameError") != -1){
									failureReason = "PasswordNotMatchingConfirmPassword";
									errorFound = SFAPIServiceResponseError.ERROR_PASSWORD_NOT_MATCHING_CONFIRM_PASSWORD;
								}
								else if(errors != null && errors.indexOf("RequireMinLength") != -1){
									failureReason = "The password should have a minimum length of 8 characters";
									errorFound = SFAPIServiceResponseError.ERROR_PASSWORD_MIN_LENGTH;
								}
								else if(errors != null && errors.indexOf("ExceedMaxLength") != -1){
									failureReason = "The password should have a maximum length of 16 characters";
									errorFound = SFAPIServiceResponseError.ERROR_PASSWORD_MAX_LENGTH;
								}
								else if(errors != null && errors.indexOf("NoSpaceAllowed") != -1){
									failureReason = "The password should not contain spaces";
									errorFound = SFAPIServiceResponseError.ERROR_PASSWORD_CONTAINS_SPACES;
								}
								else{
									failureReason = "PasswordInvalid";
								}
		
					            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
					                    method + "Error : " + errorMessageBuf.toString());
							}
							else{
								
								if(!isUserNameChanged && !isPasswordChanged){
									failureReason = "UsernameAndPasswordUnchanged";
		
						            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
						                    method + "Error : Username and password entered were unchanged from existing values");
								}
								else{
					                try {
					        			// since the user is trying to set password, the password becomes un-encrypted
					        			tempUser.getSfCredential().setIsPasswordEncrypted(false);
					        			
					        			//TODO: we will revisit this when we 
					        			//      decide which apis will use the microservice
					        			boolean isUseAcctmgrMsvc = true;
					        			
					        			SFMsvcResponseInfo respInfo = null;
					        			
					        			boolean isUpdateSucceeding = false;
					        					
					        			if (isUseAcctmgrMsvc) {
					        				
					        				jwtToken = getMicroserviceJwtToken(payLoad);
					        				
					        				// call the acctmgr microservice
					        			
						        			if(isPasswordChanged){
												data.put("password", password);
												data.put("passwordChanged", (isPasswordChanged ? "true" : "false"));
											}
												
										    data.put("userLoginName", userLoginName);
								
								            SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "resetCredential from UpdateUserCredentialInfo", "/sf/api/user/resetCredential", jwtToken);
								            					            
								            respInfo = responseObj.getResponseInfo();
								            
								            if(respInfo == null){
								                throw new Exception("The SFMsvcResponseInfo was not returned as expected, cannot determine result");    			
								            }
								            else {
								            	
								            	//expected return data
									            /*
									            { 
									            	   "response":{
									            	     "responseInfo":{
									            	       "status":"Success",
									            	       "errors":""
									            	     }
									            	   }
									            	}
									            */
								            	
								               if("Success".equalsIgnoreCase(respInfo.getStatus())){
								            	   isUpdateSucceeding = true;
								            	   String jsonDataOut = responseObj.getData();
										           JSONObject jsonOutDataObj = new JSONObject(jsonDataOut);
										           if (jsonOutDataObj.has(hasEmailSentStatus)) {
										            	boolean emailStatusReturned = jsonOutDataObj.getBoolean(hasEmailSentStatus);
										            	if (!emailStatusReturned) {
										            		addWarning(SFAPIServiceResponseError.WARNING_FAILED_TO_SEND_EMAIL, failureReason);
										            	}
										            }
								               }
								               else{
								            	   SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
								    	                    method + "Error : the microservice returned status ("+respInfo.getStatus()+") ");
								            	   failureReason = "General-SystemUnavailable";
								               }
								            }
					        			}
					        			else{
					        				// not using the acctmgr microservice, do it the old way
					        				try {
					        					// since the user is trying to set password, the password becomes un-encrypted
					        					tempUser.getSfCredential().setIsPasswordEncrypted(false);
					        					helperManager.getUserHelper().updateUserCredentialInfo(
					        					tempUser, isPasswordChanged, false);
					        					isUpdateSucceeding = true;
					        					
					        				} catch (SFFGException e) {
					        					throw e;
					        				}
					        			}
								            
					        			if(isUpdateSucceeding){
							            
							               sfUpdateUserInfoValidationUtil.notifyCredentialUpdateToAlertMgr(tempUser, currentUser);
							               
							               // TR 93614 --> copy only modifiable values, without calling SFUser.copyUser()
							        	   updateUserCredentialInfo(tempUser, currentUser.getUser(), isUpdatingChallengeQuestions, isUserNameChanged, isPasswordChanged);
							        	   // End of work for TR 93614
							        	   
							        	   // TR 36658 MBrown Apr 4 2006
							               // If the form's password was the masked password and it's been copied
							               // into the session user, make sure to set the user's password to the 
							               // 'old' value.
							               // I'm doing this here instead of in the form itself because I don't
							               // want the real unmasked password in the form (in case of back button, error, etc.)
							               if (currentUser.getUser().getPassword() != null && 
							                        currentUser.getUser().getPassword().equals(MASKED_PASSWORD)) {
							                    currentUser.getUser().setPassword(oldPasswd);
							               }
							               // End TR 36658
							                
							               
							        	    updateStoredCredential(currentUser, helperManager);
							        	    
							        	    isUpdateSuccessful = true;
							               
							            }
					        		} catch (Exception e) {
					        			failureReason = "General-SystemUnavailable";
			
					    	            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
					    	                    method + "Error : ",e);
					        		}
								}
							}
						}
					}
				}

			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";

	            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
	                    method + "Error : " + ex);
			}
			
			if (StringUtils.isNotEmpty(failureReason)) {
	            result.put(FAILURE_REASON, failureReason);
	            if(errorFound != null){
	            	addError(errorFound, failureReason);
	            }
	            else{
	            	clearErrors();
	               addError(SFAPIServiceResponseError.ERROR_UPDATE_USER_INFORMATION, failureReason);
	            }
	            isUpdateSuccessful = false;
	            stat.stop(methStatId, false);
	        }
	        else{
	        	stat.stop(methStatId,true);
	        }
			
			result.put(IS_UPDATE_SUCCESSFUL, isUpdateSuccessful);
		

		return result;
	}
	
	
	/***
	* This method is used to update the number of skip pin attempts
	* @param payLoad -
	* @return
	* @throws SFException
	*/
	private JSONObject performUpdateSkipPinSetupCount(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".performUpdateSkipPinSetupCount - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		
		String failureReason = null;
		boolean isUpdateSuccessful = false;
		boolean isErrorSpecified = false;
		
		JSONObject result = new JSONObject();
		
		try {
			
			SFUser currentUser = userAccountManager.getUserBean().getUser();
			
			if (currentUser.getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} 
			else{
				int iSkipCount = currentUser.getSkipPinSetupCount();
				iSkipCount++;
				helperManager.getUserHelper().setSkipPinSetupCountProfileOption(currentUser, iSkipCount);
				currentUser.setSkipPinSetupCount(iSkipCount);
				isUpdateSuccessful = true;
			}
		
		} catch (Exception ex) {
			failureReason = "General-SystemUnavailable";
			
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
			    method + "Error : " + ex);
		}
		
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_UPDATE_SKIP_PIN_SETUP_COUNT, failureReason);
			}
			isUpdateSuccessful = false;
			stat.stop(methStatId, false);
		}
		else{
			stat.stop(methStatId,true);
		}
		
		result.put(IS_UPDATE_SUCCESSFUL, isUpdateSuccessful);
		
		return result;
	}

    /***
     * This method is check the country is eligible for tax or not
     * @param payLoad -
     * @return
     * @throws SFException
     */
    private JSONObject processCheckForVatApplicableCountry(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processCheckForVatApplicableCountry - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        boolean isVatSupportedCountry=false;
        try {
            String countryCode = payLoad.getField(USER_DATA_COUNTRY_CODE);
            if(CountryHelper.isVatSupported(countryCode)) {
                isVatSupportedCountry = true;
            }
        } catch (Exception ex) {
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                    method + "Error : " + ex);
        }
        result.put(IS_VAT_APPLICABLE_COUNTRY,isVatSupportedCountry);
        stat.stop(methStatId,true);

        return result;
    }

	/**
	 * Update the user's credential in the data repository.  It must be updated
	 * whenever the user changes their password.
	 *
	 * @param userBean The updated SFUserBean (containing the new credential info)
	 * @param helperManager The helper manager
	 */
	private void updateStoredCredential(SFUserBean userBean,
										SFHelperManager helperManager)
	{
		final String methodName = COMPONENT + ".updateStoredCredential";
		
		SFHelperDataRepository rep = null;

		try {
			rep = helperManager.getDataRepository();
			rep.setCredential(userBean.getSfCredential(), userBean.getUser());
		} catch (SFFGException e) {
			SFLogger.getInstance().logError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM,
				methodName +
				"Unable to acquire the data repository.  Failed to update user credential for user: "
				+ userBean.getUserLoginName(), e);
		}
	}

	private void updateUserCredentialInfo(SFPerson fromUser, SFUser toUser, boolean isUpdatingChallengeQuestions, boolean isUpdatingUserName, boolean isUpdatingPassword){
		
		if(isUpdatingPassword){
		   toUser.setConfirmPassword(fromUser.getConfirmPassword());
		   
		}
        /*toUser.setChallengeQuestion(fromUser.getChallengeQuestion());
        toUser.setCustomChallengeQuestion(fromUser.getCustomChallengeQuestion());
		toUser.setChallengeAnswer(fromUser.getChallengeAnswer());*/
		
		if(isUpdatingChallengeQuestions){
		   toUser.setChallengeQuestions(fromUser.getChallengeQuestions());
		   toUser.setChallengeAnswers(fromUser.getChallengeAnswers());
		   toUser.setCustomQuestions(fromUser.getCustomQuestions());
		}
		
		SFCredential fromCredential = fromUser.getSfCredential();
		SFCredential toCredential = toUser.getSfCredential();
		
		if(isUpdatingUserName){
           toCredential.setUserLoginName(fromCredential.getUserLoginName());
		}
		
		if(isUpdatingPassword){
           toCredential.setPassword(fromCredential.getPassword());
           toCredential.setIsPasswordEncrypted(fromCredential.getIsPasswordEncrypted());
		}
	}

	/***
	 * This method is used to update TwoFactor Authentication status
	 * @param payLoad - payload holds the status "enabled" or "disabled".
	 * @return
	 * @throws SFException
	 * @throws JSONException 
	 */
	private JSONObject updateTwoFactorAuthenticationStatus(SFJsonObject payLoad) throws SFException, JSONException {
		final String method = COMPONENT + ".updateTwoFactorAuthentication - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject jsonOutObj = new JSONObject();
		SFMsvcResponse responseObj = null;

		String failureReason = null;
		String recoveryKey = null;
		
		String jwtToken = null;
		
		boolean isErrorSpecified = false;
		boolean isRemoveTrustedPhoneNumberRequired = false;

        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
			
		} else {
			
				// call the acctmgr microservice
				try {
                    SFUser currentUser = userBean.getUser();
					JSONObject data = new JSONObject();
					String status = payLoad.getField(REQ_FIELD_STATUS);
					String resetTrustedPhoneNumber = payLoad.getField(REQ_RESET_TRUSTED_PHONE_NUMBER);
					
					if (ENABLED.equalsIgnoreCase(status)) {
						
						if(SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription().equalsIgnoreCase(currentUser.getTwoFactorAuthStatus())){
							failureReason = "2faIsAlreadyEnabled";
						}
						else{
							//we should try to create the recovery key first. if that succeeds,
							//then move forward with enable
							try{
							   JSONObject recoveryKeyJsonObj = createRecoveryKey(payLoad);
							   if(recoveryKeyJsonObj != null && recoveryKeyJsonObj.has(RECOVERY_KEY)){
								   recoveryKey = recoveryKeyJsonObj.getString(RECOVERY_KEY);
							   }
							   
							   if(SFStringUtil.isEmpty(recoveryKey)){
								   failureReason = "recoveryKeyWasNotReturned";
							   }
							}
							catch(Exception ex1){
								failureReason = "recoveryKeyCreationFailed";
							}
							
							if(SFStringUtil.isEmpty(failureReason)){
							    JSONObject userInfo = new JSONObject();
							    userInfo.put("username", currentUser.getUserLoginName());
	                            userInfo.put("email", currentUser.getEmail());

	                            //Throw an error to the user if the trusted phone number is null or empty.
	                            if(SFStringUtil.isEmpty(currentUser.getTrustedPhoneNumber())) {
                                    failureReason = "The trustedPhoneNumber was not found for the current user";
                                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM, "The trustedPhoneNumber was not found for the current user");
                                }else{
                                	jwtToken = getMicroserviceJwtToken(payLoad);
                                    userInfo.put("phoneNumber", currentUser.getTrustedPhoneNumber());
                                    data.put("userInfo", userInfo);
                                    responseObj = getAcctmgrMsvcResponse(data, MS_ENABLE_2FA_METHOD, MS_ENABLE_2FA_API_CALL, jwtToken);
                                }
							}
						}
					} else if (DISABLED.equalsIgnoreCase(status)) {
						if(SFStringUtil.isEmpty(currentUser.getTwoFactorAuthStatus()) || SFConstant.TWO_FACTOR_AUTH_DISABLED.getDescription().equalsIgnoreCase(currentUser.getTwoFactorAuthStatus())){
							failureReason = "2faIsAlreadyDisabled";
						}
						else{
							if("true".equalsIgnoreCase(resetTrustedPhoneNumber)){
								//the user wants to remove trusted phone number
								isRemoveTrustedPhoneNumberRequired = true;
							}
							
							if(SFStringUtil.isEmpty(userAccountManager.getUserBean().getAuthyId())){
								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " the authyId was not found in session as expected");
							}
							jwtToken = getMicroserviceJwtToken(payLoad);
                            data.put(AUTHY_ID, userAccountManager.getUserBean().getAuthyId());
						    responseObj = getAcctmgrMsvcResponse(data, MS_DISABLE_2FA_METHOD, MS_DISABLE_2FA_API_CALL, jwtToken);
						}
					}

					if (SFStringUtil.isEmpty(failureReason)){
						SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();
						jsonOutObj = new JSONObject(sfMsvcResponseInfo);
	
						if (!"Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
							failureReason = "2faStatusUpdateFailed";
						}
						else{
							if (ENABLED.equalsIgnoreCase(status)) {
								String jsonDataOut = responseObj.getData();
				                jsonOutObj = new JSONObject(jsonDataOut);
				                
				                //get the new authyId which is generated by twilio an put into the session
				                if(jsonOutObj.has(AUTHY_ID)){
				                   String authyIdReturned = jsonOutObj.getString(AUTHY_ID); 
				                   
				                   if(!SFStringUtil.isEmpty(authyIdReturned)){
				                	   SFLogger.printInfo(method + " setting authyId (" +authyIdReturned+ ") ");
				                	   userAccountManager.getUserBean().setAuthyId(authyIdReturned);
				                	   currentUser.setTwoFactorAuthStatus(SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription());
				                	   
				                	   // clear 2-Step Verification alert from account manager
							           alertManager.removeAlert(SFAlert.ELIGIBLE_FOR_2FA);
				                   }
				                   else{
				                	   failureReason = "2faStatusUpdateFailed - authyId value returned was empty";
				                	   SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : authyId value returned was empty");
				                   }
								   
				                }
				                else{
				                	failureReason = "2faStatusUpdateFailed - authyId value was not returned";
				                	SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : authyId was not returned as expected");
				                }
							}
							else {
								//the authyId becomes null once the 2fa is disabled
								SFLogger.printInfo(method + " setting authyId to null ");
								userAccountManager.getUserBean().setAuthyId(null);
								
								currentUser.setTwoFactorAuthStatus(SFConstant.TWO_FACTOR_AUTH_DISABLED.getDescription());
								
								
								//now lets remove the trusted phone number if that was requested
								if(isRemoveTrustedPhoneNumberRequired){
									if(jwtToken == null){
									   jwtToken = getMicroserviceJwtToken(payLoad);
									}
									
									JSONObject dataReset = new JSONObject();
									dataReset.put(TRUSTED_PHONE_NUMBER, ClientInitializer.stringNull);
					
					                SFMsvcResponse responseObjResetTPN = getAcctmgrMsvcResponse(dataReset, "resetCredential from updateTwoFactorAuth - resetting trusted phone number", MS_RESET_CREDENTIAL_API_CALL, jwtToken);
					        		SFMsvcResponseInfo sfMsvcResponseInfoResetTPN = responseObjResetTPN.getResponseInfo();
									//jsonOutObj = new JSONObject(sfMsvcResponseInfoResetTPN);
									
						            	
					            	//expected return data
						            /*
						            { 
						            	   "response":{
						            	     "responseInfo":{
						            	       "status":"Success",
						            	       "errors":""
						            	     }
						            	   }
						            	}
						            */
					            	
					               if("Success".equalsIgnoreCase(sfMsvcResponseInfoResetTPN.getStatus())){
					            	   //update succeeded so update the value in session 
					            	   currentUser.setTrustedPhoneNumber(null);
					            	   //isUpdateSuccessful = true;
					            	   
					            	   //check if update email failed to send, if so then add warning
					            	   String jsonDataOutTPN = responseObjResetTPN.getData();
							           JSONObject jsonOutDataObjTPN = new JSONObject(jsonDataOutTPN);
							           if (jsonOutDataObjTPN.has(hasEmailSentStatus)) {
							            	boolean emailStatusReturned = jsonOutDataObjTPN.getBoolean(hasEmailSentStatus);
							            	if (!emailStatusReturned) {
							            		addWarning(SFAPIServiceResponseError.WARNING_FAILED_TO_SEND_EMAIL, failureReason);
							            	}
							            }
					               }
					               else{
					            	   failureReason = "resetOfTrustedPhoneNumberFailed";
					               }
								}
								
							}
						}
					}
				} catch (Exception ex) {
					failureReason = "General-SystemUnavailable";
					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex);
				}
		}
		
		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_2FA_STATUS, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	if(!SFStringUtil.isEmpty(recoveryKey)){
        	   //only expected when enabling 2fa
        	   jsonOutObj.put(RECOVERY_KEY, recoveryKey);
        	}
        	stat.stop(methStatId,true);
        }
		
		stat.stop(methStatId, true);

		return jsonOutObj;
	}
	
	/**
	 * This method will return the users ip address
	 * @return
	 */
	private String determineIpAddress(){
		final String method = COMPONENT + ".determineIpAddress - ";
		String ipAddress = null;
		if(userAccountManager.getUserBean().getUser() != null){
		   SFFraudProtection fraudProtection = userAccountManager.getUserBean().getUser().getFraudProtection();
		   if(null != fraudProtection) {
		      ipAddress = fraudProtection.getIpAddress();
		   }
		}
		if(ipAddress == null){
		   SFLogger.printInfo(method + "ipaddress was not located in fraud protection, using sessionOriginInfo");
		   ipAddress = helperManager.getSessionOriginInfo().getIpAddress();
		}
		
		return ipAddress;
	}
	
	/**
	 * This method will return the users session id.
	 * Will use this when we want to avoid having a null 
	 * sessionId.
	 * 
	 * @return
	 */
	private String determineSessionIdWithoutHashing(){
		final String method = COMPONENT + ".determineSessionIdWithoutHashing - ";
		String sessionId = null;
		try{
			if(userAccountManager.getUserBean().getUser() != null){
			   SFFraudProtection fraudProtection = userAccountManager.getUserBean().getUser().getFraudProtection();
			   if(null != fraudProtection) {
				   sessionId = fraudProtection.getSessionId();
			   }
			}
			if(sessionId == null){
			   SFLogger.printInfo(method + "sessionId was not located in fraud protection, using sessionOriginInfo");
			   sessionId = helperManager.getSessionOriginInfo().getOriginalSessionId();
			}
			
			
		}
		catch(Exception ex){
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
					method + " - problem occurred determining sessionId. This is not fatal. error was: " + ex.getMessage());
		}
		
		if(sessionId == null){
			SFLogger.printInfo(method + "sessionId was not located in fraud protection or origin info, returning empty");
			sessionId = "null";
		}
		
		return sessionId;
	}

	  /***
     * This method is used to verify Sms Code.
     * @param payLoad - payload holds the 6-digit sms code and AuthyId.
     * @return
     * @throws SFException
	 * @throws JSONException 
     */
	private JSONObject verifySmsCode(SFJsonObject payLoad) throws SFException, JSONException {
		final String method = COMPONENT + ".verifySmsCode - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
		JSONObject jsonOutObj = new JSONObject();
		JSONObject data = new JSONObject();
		
		String failureReason = null;
		
		String jwtToken = null;
		
		boolean isVerifySuccessful = false;
		
		SFUser currentUser = userAccountManager.getUserBean().getUser();
		if(currentUser.getUserId() == -1){
			//avoid setting as an error, as agreed with angular team,
			//they will interpret response
			failureReason = "loginRequired";
			
        }
		else {
			try {
				String authyId = userAccountManager.getUserBean().getAuthyId();

				if (SFStringUtil.isEmpty(authyId)) {
					failureReason = "The authentication may not have been successful, since the authyId was not found for the user.";
				} else {

					data.put(AUTHY_ID, authyId);
					data.put(CODE, payLoad.getField(SMS_VERIFICATION_CODE));

					data.put("ipAddress", determineIpAddress());
					String sessId = getHashedSessionId();
	    			if(sessId == null){
	    				sessId = determineSessionIdWithoutHashing();
	    			}
					data.put("sessionId", sessId);
					int userId = currentUser.getUserId();
					String sUserId = Integer.toString(userId);
					data.put("originatorId", sUserId);
					data.put("personOrgId", sUserId);

					jwtToken = getMicroserviceJwtToken(payLoad);
					
					// call the acctmgr microservice
					SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, MS_VERIFY_SMSCODE_METHOD,
							MS_VERIFY_SMSCODE_API_CALL, jwtToken);
					SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

					if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {

						String jsonDataOut = responseObj.getData();
						jsonOutObj = new JSONObject(jsonDataOut);

						if (jsonOutObj.has(STATUS_ATTR)) {
							String smsStatus = jsonOutObj.getString(STATUS_ATTR);
							if ("success".equalsIgnoreCase(smsStatus)) {
								isVerifySuccessful = true;
								SFLogger.printInfo(
										method + " - verification successful, marking user as being logged in.");
							} else {
								failureReason = "verificationFailed";
								SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
										method + " - verification failed, marking user as not being logged in.");
								
								String failedCodeCount = jsonOutObj.getString("failedVerificationCodeCount");

								jsonOutObj.put(FALSE_VERIFICATION_CODE_COUNT, failedCodeCount);
								userAccountManager.getUserBean().setFalseVerificationCodeCount(Integer.parseInt(failedCodeCount));
							}
						} else {
							throw new SFException(
									"the status from the verify sms api was not found in the data returned");
						}
					} else {
						failureReason = "verificationFailed";
						// not going to increment the error count since it
						// seemed to fail due to a bug
						int count = userAccountManager.getUserBean().getFalseVerificationCodeCount();
						jsonOutObj.put(FALSE_VERIFICATION_CODE_COUNT, count);
					}
				}

			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex);
			}

		}
		
		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			//avoid setting as an error, as agreed with angular team,
			//they will interpret response
            //addError(SFAPIServiceResponseError.ERROR_VERIFY_SMS_CODE, failureReason);
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
		
		//mark the user as being logged in based on results, since 2FA is completed
		userAccountManager.getUserBean().setIsLoggedIn(isVerifySuccessful);
		//status.setIsAuthenticated(isVerifySuccessful);
		jsonOutObj.put(IS_VALID, isVerifySuccessful);
		return jsonOutObj;
	}

    private JSONObject processGetUserInfo() throws JSONException {
        JSONObject result = new JSONObject();
        SFUserBean userBean = userAccountManager.getUserBean();
        result.put(FIRST_NAME_ATTR, userBean.getFirstName());
        result.put(LAST_NAME_ATTR, userBean.getLastName());
        result.put(EMAIL_ATTR, userBean.getEmail());
        result.put(PHONE, userBean.getUser().getPhoneNum());
        Map<String, Object> address = new LinkedHashMap<>();
        address.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, userBean.getUser().getStreetAddress());
        address.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, userBean.getUser().getStreetAddress2());
        address.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, userBean.getUser().getCity());
        address.put(STATE_PROV, userBean.getUser().getState());
        address.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, userBean.getUser().getCountry());
        address.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, userBean.getUser().getZip());
        result.put(ADDRESS_ATTR, address);
        return result;
    }

    private JSONObject processGetUserInfoFullDetails() throws JSONException, SFException {
    	
    	final String method = COMPONENT + ".processGetUserInfoFullDetails - ";
        JSONObject result = new JSONObject();
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
        boolean isErrorSpecified = false;
        
        try{
	        SFUserBean userBean = userAccountManager.getUserBean();
	        
	        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} 
	        else {
		        result.put(FIRST_NAME_ATTR, userBean.getFirstName());
		        result.put(LAST_NAME_ATTR, userBean.getLastName());
		        result.put(EMAIL_ATTR, userBean.getEmail());
		        result.put(PHONE, userBean.getUser().getPhoneNum());
		        result.put(FAX, userBean.getUser().getFaxNum());
		        result.put(VIP_CUSTOMER, userBean.getUser().getVIPCustomer());

		        if(userBean.getUser().getVIPCustomer() == true){
		            result.put(VIP_TYPE, userBean.getUser().getSegmentInfo().getSegmentName());
                }

		        result.put(COMPANY_NAME, userBean.getUser().getCompanyName());
		        result.put(NIC_HANDLE_ID, userBean.getUser().getNicHandleId());
		        result.put(VAT_ID, userBean.getUser().getVatId() != null ? userBean.getUser().getVatId() : "");
		        result.put(BULK_WHOIS_OPTOUT, userBean.getUser().getBulkWhoIsOptout());
		        result.put(EMAIL_NOTIFY, userBean.getUser().getEmailNotify());
		        result.put(PARTNER_PROMOTIONS_NOTIFY, userBean.getUser().getPartnerPromotionsNotify());
		        result.put(OX_UPSELL_OPTOUT, userBean.getUser().isOxUpsellOptout());
                if(userBean.getUser().getPhoneOptIn()!=null && userBean.getUser().getPhoneOptIn().equalsIgnoreCase("granted"))
                    result.put("communicationOptOut", false);
                else
                    result.put("communicationOptOut", true);

                /*boolean isSalesTaxApplicable =  shoppingCart.isSalesTaxApplicable(userBean);
                result.put(IS_SALES_TAX_APPLICABLE, isSalesTaxApplicable);*/
                boolean isVatApplicableCountry = CountryHelper.isVatSupported(userBean.getUser().getContactInfo().getCountry());
                result.put(IS_VAT_APPLICABLE_COUNTRY, isVatApplicableCountry);

		        //being replaced by pin
		        //result.put(CHALLENGE_QUESTIONS, userBean.getUser().getChallengeQuestions());
		        
		        result.put(USER_LOGIN_NAME, userBean.getUser().getUserLoginName());
		        
		        //angular should stop referencing this isAccountHolder. They should use isRegistrant instead
		        result.put(IS_ACCOUNT_HOLDER, isAccountHolder(userBean.getUserId()));
		        result.put(IS_REGISTRANT, isRegistrant(userBean.getUser()));
		        result.put(IS_FIRST_LOGIN, userBean.getIsFirstLogin());
		        
		        java.util.Date lastSuccessfulLogin = userBean.getLastSuccessfulLogin();
		        if(lastSuccessfulLogin != null){
		        	SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm aa");
		        	String lastSuccessfulLoginStr = formatter.format(lastSuccessfulLogin);
		        	result.put(LAST_LOGIN_DATE_TIME, lastSuccessfulLoginStr);
		        }
		
		       
		        //2FA related fields
		        result.put(PIN, userBean.getUser().getPin()); // this is a string value , which is 6 digits
		        result.put(TRUSTED_PHONE_NUMBER, userBean.getUser().getTrustedPhoneNumber()); // this is is a string value
		        
		        if(userBean.getUser().getTwoFactorAuthStatus() != null &&
		        		userBean.getUser().getTwoFactorAuthStatus().equalsIgnoreCase(SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription())){
		        	 result.put(TWOFACTOR_AUTH_STATUS, "enabled");
		        }
		        else{
		        	result.put(TWOFACTOR_AUTH_STATUS, "disabled");
		        }
		         
		        result.put(RECOVERY_KEY, userBean.getUser().getRecoveryKey()); //this will be a string value
		        
		        result.put(FALSE_RECOVERY_KEY_COUNT, userBean.getFalseRecoveryKeyCount());
		        result.put(FALSE_VERIFICATION_CODE_COUNT, userBean.getFalseVerificationCodeCount());
		        
		        result.put(SKIP_PIN_SETUP_COUNT, userBean.getUser().getSkipPinSetupCount());
		        result.put(RENEWAL_CENTER_REMINDER_NOTIFICATION_DISMISS, userBean.getUser().getLastRenewalCenterNotificationDismissal());
                result.put(RENEWAL_CENTER_REMINDER_POPUP_DISMISS, userBean.getUser().getLastRenewalCenterPopUpDismissal());
                result.put(CORP_TARGET_FLAG, userBean.getUser().getCorpTargetFlag());
		        
		        SFLogger.printDebug(method + " authyId = " + userBean.getAuthyId());
		
		
		        result.put(ACCOUNT_THRESHOLD_EXCEEDED, userBean.getUser().isAccountThresholdExceeded());
		        result.put(PRODUCTS_THRESHOLD_EXCEEDED, userBean.getUser().isProductsThresholdExceeded());
		
		        Map<String, Object> address = new LinkedHashMap<>();
		        address.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, userBean.getUser().getStreetAddress());
		        if(userBean.getUser().getStreetAddress2() != null){
		           address.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, userBean.getUser().getStreetAddress2());
		        }
		        address.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, userBean.getUser().getCity());
		        address.put(STATE_PROV, userBean.getUser().getState());
		        address.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, userBean.getUser().getCountry());
		        address.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, userBean.getUser().getZip());
		        address.put(ADDRESS_QUALITY_CODE, userBean.getUser().getAddress().getAddressQualityCode());
		        result.put(ADDRESS_ATTR, address);
		        
		        List accountList =  userBean.getUser().getAccounts();
			       boolean isSingleAccount= false;
			       boolean isWhoisEditable =false;
				if (accountList != null) {
					if (1 == accountList.size()) {
						isSingleAccount = true;
						SFAccount singleAccount = (SFAccount) accountList.get(0);
						int accountId = singleAccount.getAccountId();
						result.put(ACCOUNT_ID_ATTR, accountId);
						isWhoisEditable = accountApiService.checkHasWhois(singleAccount);
					} else if (accountList.size() > 1) {
						Iterator accountItr = accountList.iterator();
						while (accountItr.hasNext()) {
							SFAccount sfAccount = (SFAccount) accountItr.next();
							isWhoisEditable = accountApiService.checkHasWhois(sfAccount);
							if (isWhoisEditable) {
								break;
							}
						}
					}
					
					result.put(SHOW_USERS_AND_ROLES, false);
					if (accountList.isEmpty()) {
						userBean.setShowUserAndRoles(true);
						result.put(SHOW_USERS_AND_ROLES, true);
					}
					if (accountList != null && !accountList.isEmpty()) {
						for (int i = 0; i < accountList.size(); i++) {
							SFAccount account = (SFAccount) accountList.get(i);
							if (account.getAccountType().equals(SFAccountTypeConstant.INDIVIDUAL) && accountList.size() == 1) {
								userBean.setShowUserAndRoles(true);
								result.put(SHOW_USERS_AND_ROLES, true);
								break;
							}
						}
					}
				}
					result.put(hasWHOIS, isWhoisEditable);
					result.put(IS_SINGLE_ACCT, isSingleAccount);
		        
	
				SFContactInfo contactInfo = userBean.getUser().getContactInfo();
		        
		        if(contactInfo != null){
		        	Map<String, Object> contactInfoMap = new LinkedHashMap<>();
			        contactInfoMap.put("email", contactInfo.getEmail());
			        contactInfoMap.put("secondaryEmail", contactInfo.getSecondaryEmail());
			        contactInfoMap.put("phone", contactInfo.getPhoneNum());
			        contactInfoMap.put("fax", contactInfo.getFaxNum());
			        contactInfoMap.put("companyName", contactInfo.getCompanyName());
			        contactInfoMap.put("companyType", contactInfo.getCompanyType());
			        contactInfoMap.put("contactType", contactInfo.getContactType());
			        contactInfoMap.put("contactId", contactInfo.getContactId());
			        contactInfoMap.put("isNsiContact", contactInfo.getIsNSIContact());
			        if(contactInfo.getVat() != null){
			           contactInfoMap.put("vat", contactInfo.getVat());
			        }
			        
			        SFAddress contactAddress = contactInfo.getAddress();
			        if(contactAddress != null){
				        Map<String, Object> contactAddressMap = new LinkedHashMap<>();
				        contactAddressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, contactAddress.getStreetAddress());
				        if(contactAddress.getStreetAddress2() != null){
				           contactAddressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, contactAddress.getStreetAddress2());
				        }
				        contactAddressMap.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, contactAddress.getCity());
				        contactAddressMap.put(STATE_PROV, contactAddress.getState());
				        contactAddressMap.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, contactAddress.getCountry());
				        contactAddressMap.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, contactAddress.getZip());
				        contactAddressMap.put(ADDRESS_QUALITY_CODE, contactAddress.getAddressQualityCode());
				        contactInfoMap.put(ADDRESS_ATTR, contactAddressMap);
			        }
			        result.put("contactInfo", contactInfoMap);
		        }
	
	            /***
	             * populate nexus based fields in user info and response result
	             */
	            result.put(DOT_US_USAGE_INTENT, SFStringUtil.EMPTY);
	            result.put(DOT_US_USER_CATEGORY, SFStringUtil.EMPTY);
	
		        User user = helperManager.getUserHelper().pullUser(userBean.getUserId(), userBean.getSfCredential(), null);
	
		        /* SOFT-89580 - Domain Special Rules Requirements Modal - .US */
		        if(userBean.getUser().getDotUSDomainCount() > 0 || userBean.getUser().getDotCADomainCount() > 0 || 
		           userBean.getUser().getDotEUDomainCount() > 0 || userBean.getUser().getDotNYCDomainCount() > 0) {
	            /**
	             * pick profiles related to only Nexus category and intent i.e NEXUS_APP_PURPOSE and NEXUS_CATEGORY.
	             */
	            if (user.getUserProfiles() != null || user.getUserProfiles().length > 0){
	                for(UserProfile userProfile : user.getUserProfiles()){
	                    if (registrar.fulfill.gateway.corbagen.usermgmtintf.User.NEXUS_APP_PURPOSE.equals(userProfile.getProfileName())) {
	                        userBean.getUser().setDotusUsageIntent(userProfile.getProfileValue());
	                        result.put(DOT_US_USAGE_INTENT, userProfile.getProfileValue());
	                    } else if (registrar.fulfill.gateway.corbagen.usermgmtintf.User.NEXUS_CATEGORY.equals(userProfile.getProfileName())) {
	                        userBean.getUser().setDotusUserCategory(userProfile.getProfileValue());
	                        result.put(DOT_US_USER_CATEGORY, userProfile.getProfileValue());
	                    }
	                }
	             }
		        }

				/**
				 * SOFT-91456 : return the user profile setting for
				 * isDismissAllNotifications, So if sfUser.isShowDashboardAlert
				 * is true, then output "isDismissAllNotifications":false
				 */
				if (!userBean.isShowDashboardAlert()) {
					result.put(IS_DISMISS_ALL_NOTIFICATIONS, true);
				} else {
					result.put(IS_DISMISS_ALL_NOTIFICATIONS, false);
				}
				
				/**
				 * SOFT-94145 : return the user profile setting for
				 * isDismissAllItemsInProgress, So if sfUser.isDismissAllItemsInProgress
				 * is true, then output "isDismissAllItemsInProgress":false
				 */
				if (!userBean.isShowItemsInProgressAlert()) {
					result.put(IS_DISMISS_ALL_ITEMS_INPROGRESS, true);
				} else {
					result.put(IS_DISMISS_ALL_ITEMS_INPROGRESS, false);
				}
				//SOFT-94032- adding new flag for byPassCor functinality
				List<SFAccount> sfAccountList = userAccountManager.getAccounts(userBean.getUserId());
				boolean allowOptOutCor = false;
				boolean isPrimaryUser = false;
				if (sfAccountList != null) {
					for (SFAccount currentAcct : sfAccountList) {
						RoleEnum role = userAccountManager.getRelation(userBean.getUserId(),
								currentAcct.getAccountId());
						if (role.equals(RoleEnum.PRIMARYUSER)) {
							isPrimaryUser = true;
							break;
						}
					}
				}

				if (isPrimaryUser) {
					if (user.getUserProfiles() != null || user.getUserProfiles().length > 0) {
						for (UserProfile userProfile : user.getUserProfiles()) {
							if (userProfile.getProfileName().equalsIgnoreCase("ALLOW_OPT_OUT_COR")
									&& userProfile.getProfileValue().equalsIgnoreCase("Y")) {
								allowOptOutCor = true;
								break;
							}
						}
					}
				}
				result.put("allowOptOutCor", allowOptOutCor);
                //SOFT-103522
                Predicate<RoleEnum> isNotATechUserRole = role -> (!RoleEnum.TECHCONTACT.equals(role));
                boolean isNotATechUser = sfAccountList.stream().anyMatch(a ->
                        isNotATechUserRole.test(userAccountManager.getRelation(userBean.getUserId(),
                            a.getAccountId())));
                result.put("isTechContactUser", !isNotATechUser);
			}
        }
        catch(Exception ex){
        	failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ",ex);
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
        	result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_GET_USER_INFO_FULL_DETAILS, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
        
        return result;
        
    }
    

    private JSONObject processGetQuickLinksInfo(SFJsonObject payLoad) throws JSONException, SFException {
    	
    	final String method = COMPONENT + ".processGetQuickLinksInfo - ";
        JSONObject result = new JSONObject();
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
        boolean isErrorSpecified = false;
        
        int numberOfAccounts = 0;
        boolean isSingleAccount = true;
        
        try{
	        SFUserBean userBean = userAccountManager.getUserBean();
	        
	        Collection<JSONObject> quickLinksList = new ArrayList<>();
            int size = 0;
	        
	        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} 
	        else {
	        	
	        	if(userBean != null){
	        		SFUser user = userBean.getUser();
	        		if(user != null){
	        			List userAccounts = user.getAccounts();
	        			if(userAccounts != null){
	        				numberOfAccounts = userAccounts.size();
	        				if(numberOfAccounts > 1){
	        					isSingleAccount = false;
	        				}
	        			}
	        	    }
	        	}
	        	
	        	if(quickLinksHolder != null){
	        		List<SFQuickLinksInfo> currentQuickLinksList = quickLinksHolder.getQuickLinksList(userBean.getUser().getUserLoginName());
	        		if(currentQuickLinksList != null){
	        			size = currentQuickLinksList.size();
	        			
	        			for(int i=0;i<size;i++){
	        				SFQuickLinksInfo info = currentQuickLinksList.get(i);
	        				JSONObject obj = SFQuickLinksUtils.convertQuickLinkInfoToJson(info);
	        				if(info.getPageCode() != null){
	        					SFQuickLinkCodeConstant pageName = SFQuickLinkCodeConstant.getByValue(info.getPageCode());
	        			   		if(pageName != null){
	        			   			obj.put(SFQuickLinksConstants.PAGE_NAME, pageName);
	        			   		}
	        				}
	        				quickLinksList.add(obj);
	        			}
	        		}
	        	}
	        }
	        
	        result.put("quickLinks", quickLinksList);
	        result.put("totalQuickLinks", size);
	        
	        JSONObject summaryData = new JSONObject();
	        summaryData.put("isSingleAccount", isSingleAccount);
	        summaryData.put("numberOfAccounts", numberOfAccounts);
	        
	        result.put("summaryData", summaryData);

		       
        }
        catch(Exception ex){
        	failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ",ex);
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
        	result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_GET_QUICK_LINKS_INFO, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
        
        return result;
        
    }
    
    private String determineDetailsUrlForProduct(SFAccount account, SFProduct product){
	   	   
 	   String url = SFUtils.constructManageLink(account,product);
 	   
 	   return url;
 	}
    

    private JSONObject processSaveQuickLinksEvent(SFJsonObject payLoad) throws JSONException, SFException {
    	
    	final String method = COMPONENT + ".processSaveQuickLinksEvent - ";
        JSONObject result = new JSONObject();
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
        boolean isErrorSpecified = false;
        
        
        boolean isUpdateSuccessful = false;
        
        try{
	        SFUserBean userBean = userAccountManager.getUserBean();
	        SFUser user = userAccountManager.getUserBean().getUser();
	        
	        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} 
	        else {
	        	
	        	if(user.isCSRRep()){
	        		failureReason = "logged in user is CSR rep, save will not be performed";
	        	}
	        	else{
		        	String pageCode = payLoad.getField(PAGE_CODE_PATH);
		        	String qlAccountId = payLoad.getField(ACCOUNT_ID_PATH);
		        	String qlProductInstanceId = payLoad.getField(PRODUCT_INSTANCE_ID_PATH);
		        	String qlDomain = payLoad.getField(DOMAIN_NAME_PATH);
		        	
		        	//will be determined based on accountId passed in
		        	String qlAccountName = null;
		        	String qlProductName = null;
		        	String qlProductCategoryName = null;
		        	
		        	SFAccount qlAccount = null;
		        	SFProduct qlProduct = null;
		        	
		        	boolean isOkToContinue = true;
		        	
		        	if(quickLinksHolder != null){
		        		
		        		SFQuickLinkCodeConstant currentConstant = SFQuickLinkCodeConstant.getByValue(pageCode);
		        		
		        		if(currentConstant != null){
		        		
			        		//update the holder with the new quicklink
			        		
			        		//certain pages will require more inputs than others
			        		//so will map out some logic here, will improve later
			        		
			        		/*
			        		 * 1. domainName
		
		                      2. name of product
		
		                      3. accountName
		
		                      4. product category name
		
		                      5. assigned domain name
		
		                      6. pageCode <REFERENCE THE PAGE TABLE FOR THIS VALUE, SEE UC2 - Step 1>
		
		                      7. ctaRedirectUrl - dynamic url
			        		 */
			        		
			        		SFQuickLinksInfo qlInfo = new SFQuickLinksInfo();
			        		qlInfo.setPageCode(pageCode);
			        		
			        		if(!SFStringUtil.isEmpty(qlAccountId)){
			        			
			        			int accountIdVal = 0;
			        			try{
			        				accountIdVal = Integer.parseInt(qlAccountId);
			        			}
			        			catch(NumberFormatException ex){
			        				failureReason = "accountId provided in input was not numeric";
			        				isOkToContinue = false;
			        			}
			        			
			        			if(isOkToContinue){
				        			//validate that the account belongs to the user,
				        			//then lookup if there is an account name
				        			
				        			if (accountIdVal == 0) {
				                        failureReason = "AccountIdIsEmpty";
				                        isOkToContinue = false;
				                        
				                    } else if (user.getAccounts() != null && isAccountFoundForUser(user, accountIdVal)) {
		
				                    	qlAccount = userAccountManager.getAccount(accountIdVal);
				                        qlAccountName = qlAccount.getAccountName();
				                        qlInfo.setAccountName(qlAccountName);	
				                    }
				                    else {
				                        failureReason = "AccountIdDoesNotBelongToUser";
				                        addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountIdVal);
				                        isErrorSpecified = true;
				                        isOkToContinue = false;
				                        SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountIdVal+" does not belong to user "+ user.getUserId() );
				                    }
			        			}
			        			
			        			if(!SFStringUtil.isEmpty(qlProductInstanceId)){
			        				if(productManager != null){
			        				   try{
			        				      qlProduct = productManager.getProductGivenProductInstanceId(qlProductInstanceId,user);
			        				      qlProductCategoryName = qlProduct.getProductTypeDisplayName();
			        				      qlProductName = qlProduct.getProductDisplayName();
			        				      String productName=null,productCategoryName = null;
			        				      try {
			        				    	  productName = URLEncoder.encode(qlProductName, UTF8);
			        				    	  productCategoryName = URLEncoder.encode(qlProductCategoryName, UTF8);
			        				    	  qlInfo.setProductCategoryName(productCategoryName);
				        				      qlInfo.setNameOfProduct(productName);
			        	                    } catch (UnsupportedEncodingException e) {
			        	                    	failureReason = "error while encoding";
					        				    isOkToContinue = false;
					        				    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " a problem occurred when encoding productName ("+qlProductName+") ");
						        				
			        	                    }
			        				      
			        				      
			        				      //determine product display name
			      						  SFViewItem viewItem = SFViewItemFactory.createDashboardViewItem(qlProduct,userBean);
			      						
			      						  String URL = (String)viewItem.getAttribute(IViewItemAttributeConstant.DASHBOARD_PRODUCT_URL);
			        				      
			      						   qlInfo.setAssignedDomainName(URL);
			        				   }
			        				   catch(Exception ex){
			        					   failureReason = "product lookup failed";
			        					   isOkToContinue = false;
			        					   SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " a problem occurred when trying to locate product ("+qlProductInstanceId+") ");
			        				   }
			        				}
			        				
			        			}
			        			
			        			if(!SFStringUtil.isEmpty(qlDomain)){
			        				MultiLevelDomainNameChecker domainChecker = MultiLevelDomainNameChecker.getTheInstance();
	
			        			    //Checking formattings
			        			    String [] errors = domainChecker.getErrors(qlDomain);
			        			    if ( null != errors )
			        			    {
			        			    	failureReason = "domain name validation failed";
			        					isOkToContinue = false;
			        					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " the domain name input was not valid ("+qlDomain+") ");
			        			    }
			        			    else{
			        			    	qlInfo.setDomainName(qlDomain);
			        			    }
			        			}
			        		}
			        		
			        		//determine the type of ctaRedirectUrl required
			        		String ctaRedirectUrl = null;
			        		if(isOkToContinue && SFQuickLinksUtils.isQuickLinkForProductDetailPage(pageCode)){
			        			if(qlAccount != null && qlProduct != null ){
			        				
			        			   ctaRedirectUrl = determineDetailsUrlForProduct(qlAccount, qlProduct);
			        			   if (SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue() == SFMTChannelConstants.DEFAULT_WEB_PARENT_CHANNEL_ID){
			        				   if (pageCode.equals(SFQuickLinkCodeConstant.EMAIL_DETAILS_PAGE.getValue())){
			        					   if (!(qlProduct instanceof SFDotComEmail)){
			        						   ctaRedirectUrl = ctaRedirectUrl.replace("hosting-detail", "emailbox-details");
			        					   }
			        				   }
			        				  
			        			   }
			        			}
			        			else{
			        				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " The accountId, and productInstanceId are not valid for the quicklink being saved. pageCode("+pageCode+"), accountId("+qlAccountId+"), productInstanceId("+qlProductInstanceId+") ");
			        				failureReason = "The accountId, and productInstanceId are not valid for the quicklink being saved";
			        				isOkToContinue = false;
			        			}
			        		}
			        		else{
			        			ctaRedirectUrl = SFQuickLinksUtils.determineCtaRedirectUrlForPageCode(pageCode, qlAccountId, qlProductInstanceId, qlDomain);
			        		}
			        		qlInfo.setCtaRedirectUrl(ctaRedirectUrl);
			        		
							if (isOkToContinue && !SFStringUtil.isEmpty(qlInfo.getPageCode())
									&& !SFStringUtil.isEmpty(qlInfo.getCtaRedirectUrl())) {
								String loggedUser = user.getUserLoginName();
			        			quickLinksHolder.addQuickLink(qlInfo, loggedUser);
			        			
				        	    //save the quicklinks to the cookie
				        		SFCoreRequestInfo reqInfo = this.getRequestInfo();
				        		if(reqInfo != null){
				        			HttpServletResponse resp = reqInfo.getHttpResponse();
				        			if(resp != null){
			        					isUpdateSuccessful = SFQuickLinksUtils.saveQuickLinksTrackingCodeCookie(quickLinksHolder, resp, getRequestInfo().getHttpRequest(), loggedUser);
			        					if(!isUpdateSuccessful){
			        						failureReason = "the quicklinks data was not acceptable"; 
			        					}
				        			}
				        			else{
				        				failureReason = "response obj not found as expected";
				        			}
				        		}
				        		else{
				        			failureReason = "request info not found as expected";
				        		}
			        		}
		        		}
		        		else{
		        			failureReason = "the pageCode provided is not a valid quicklink pageCode";
		        		}
		        	}
		        	else{
		        		failureReason = "quicklinks holder not found as expected"; 		
		        	}
	        	}
	        }
        }
        catch(Exception ex){
        	failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ",ex);
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
        	result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_SAVE_QUICK_LINKS_EVENT, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
        
        result.put(IS_UPDATE_SUCCESSFUL, isUpdateSuccessful);
        
        return result;
        
    }




    /* this method checks if the user has a weblocked domain */
	private JSONObject processCheckUserHasDomainWithWeblock() throws JSONException, SFException {
		final String method = COMPONENT + ".processCheckUserHasDomainWithWeblock - ";
		JSONObject result = new JSONObject();
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
        boolean isErrorSpecified = false;
        
        try{
        	
        	SFUser currentUser = userAccountManager.getUserBean().getUser();
    		if (currentUser.getUserId() == -1) {
    			failureReason = "loginRequired";
    			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
    			isErrorSpecified = true;
    		} else {

				boolean isDomainWeblockFound = false;
				 SFUserBean userBean = userAccountManager.getUserBean();
				if (null != userBean && null != userBean.getUser() && !isLargeUser(userBean.getUser())) {
					List<SFAccount> sfAccountList = userAccountManager.getAccounts(userBean.getUserId());
					if (sfAccountList != null) {
						for (SFAccount currentAcct : sfAccountList) {
							List<SFDomain> domainList = currentAcct.getProductCollection().getProductByType(SFClassNameConstant.DOMAIN_TYPE.getDescription());
		
							if (domainList != null) {
								for (int i = 0; i < domainList.size(); i++) {
									SFDomain domainProduct = domainList.get(i);
									if (domainProduct != null) {
										String weblockInd = ((SFDomain) domainProduct).getWebLockIndicator();
										if (SFProductUtils.isWebLockActive(weblockInd)) {
											isDomainWeblockFound = true;
											break;
										}
									}
								}
							}
						}
					}
				}
				result.put(USER_DOMAINWITH_WEBLOCK, isDomainWeblockFound);
    		}
        }
        catch(Exception ex){
			failureReason = "General-SystemUnavailable";
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : ",ex);
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
        	result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_CHECK_USER_HAS_DOMAIN_WITH_WEBLOCK, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
        
		return result;
	}

    /***
     * This method is used to update user info of any existing user. For this method to process ahead, the user needs to be logged in.
     * @param payLoad - holds the required user fields to be updated.
     * @return
     * @throws JSONException 
     */
	private JSONObject updateUserProfileInfo(SFJsonObject payLoad) throws JSONException {
		JSONObject jsonOutObj = new JSONObject();
		final String method = COMPONENT + ".updateUserProfileInfo - ";

		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		String failureReason = null;
		boolean isRequestHasEditedConatctDetails = false;
		boolean isUpdateUserProfileCallFailed = false;
		boolean isErrorSpecified = false;

		String jwtToken = null;

        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
		} else {
			try {
                SFUser currentUser = userAccountManager.getUserBean().getUser();
				// check if this is nexus request only
				boolean isNexusReq = IsNexusRequest(payLoad);
				SFPerson tempUser = (SFPerson) getTempUser(payLoad);



          if (isNexusReq) {
					String validationError = sfUpdateUserInfoValidationUtil.validationNexusInfo(tempUser, currentUser);
					if (validationError != null && !validationError.isEmpty()) {
						jsonOutObj.put(FAILURE_REASON, "ValidateNexusCheckFailed");
						addError(validationError);
						stat.stop(methStatId, false);
						return jsonOutObj;
					}
					boolean isUseAcctmgrMsvc = true;

					if (isUseAcctmgrMsvc) {
						jwtToken = getMicroserviceJwtToken(payLoad);

						try {
							JSONObject data = new JSONObject();
							addNexusJsonData(payLoad, data);

							SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "updateUserProfileInfo",
									"/sf/api/user/updateUserProfileInfo", jwtToken);

							if (responseObj.getResponseInfo().getStatus().equalsIgnoreCase("success")) {

								// TR 93614 --> copy only modifiable values,
								// without calling SFUser.copyUser()
								//updateUserInfo(tempUser, currentUser);
								// End of work for TR 93614

								String jsonDataOut = responseObj.getData();
								jsonOutObj = new JSONObject(jsonDataOut);
							} else {
								failureReason = "Error occurred during nexus update";
								isUpdateUserProfileCallFailed = true;
							}
						} catch (Exception ex) {
							failureReason = "General-SystemUnavailable";
							isUpdateUserProfileCallFailed = true;
							SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, ex.getMessage());
						}
						if (StringUtils.isNotEmpty(failureReason)) {
							jsonOutObj.put(FAILURE_REASON, failureReason);
							addError(SFAPIServiceResponseError.ERROR_UPDATE_USER_PROFILE, failureReason);
							stat.stop(methStatId, false);
						} else {
							stat.stop(methStatId, true);
						}

						return jsonOutObj;
					}
				}


          accountApiService.updateCommunicationOptInfo(tempUser,Boolean.valueOf(payLoad.getField(USER_DATA_COMMUNICATION_OPTOUT)),method);

          JSONObject validatedAddressJSON = new JSONObject();
				int addressConfidence = -1;

				// validate the address when the skipAddressValidation flag is
				// false.
				if (payLoad.getField(USER_DATA_SKIP_VALIDATE_ADDRESS) != null
						&& payLoad.getField(USER_DATA_SKIP_VALIDATE_ADDRESS).equalsIgnoreCase("false")) {
					try {
						SFAddress validatedAddress = sfUpdateUserInfoValidationUtil.validateAddressforSalesTax(tempUser);
						
						if(validatedAddress != null){
							
							addressConfidence = validatedAddress.getAddressQualityCode();
							
							if(addressConfidence > 99){
							   // Update the user with the results of the address validation without asking the user.
																
							   validatedAddressJSON.put("address1", validatedAddress.getStreetAddress());
							   validatedAddressJSON.put("address2", validatedAddress.getStreetAddress2());
							   validatedAddressJSON.put("city", validatedAddress.getCity());
							   validatedAddressJSON.put("stateProv", validatedAddress.getState());
							   validatedAddressJSON.put("country", validatedAddress.getCountry());
							   validatedAddressJSON.put("postalCode", validatedAddress.getZip());
						       validatedAddressJSON.put("addressConfidence", addressConfidence);
						       
						    // Copy the address validation results to the form user
						       helperManager.getUserHelper().updateUserAddressForAddressValidation(tempUser, validatedAddress);
	                                            
							}
							else if (addressConfidence >= 30) {
		                        
	                            if (addressConfidence > 30 || (addressConfidence == 30 && validatedAddress.hasPlusFourZip())) {
	                            
	                            	// Show the user the suggested address, allow them to confirm or cancel.	
									jsonOutObj.put("address1", validatedAddress.getStreetAddress());
									jsonOutObj.put("address2", validatedAddress.getStreetAddress2());
									jsonOutObj.put("city", validatedAddress.getCity());
									jsonOutObj.put("stateProv", validatedAddress.getState());
									jsonOutObj.put("country", validatedAddress.getCountry());
									jsonOutObj.put("postalCode", validatedAddress.getZip());
									
									addWarning(SFAPIServiceResponseError.WARNING_VALIDATED_ADDRESS_FOUND, failureReason);
									return jsonOutObj;
									
	                            } else {
	                            	throw new SFException("cannot validate address due to invalid zip");
	                            }
							}
						}
							
					} catch (Exception e) {
						failureReason = "Address could not be verified";
						jsonOutObj.put(FAILURE_REASON, failureReason);
						addError(SFAPIServiceResponseError.ERROR_INVALID_ADDRESS, failureReason);
						stat.stop(methStatId, false);
						return jsonOutObj;
					}
				} else {
					// Address validation not needed.
					// Set the address quality value to not validated
					sfUpdateUserInfoValidationUtil.setAddressQualityCodeForNonUSCountry(tempUser);
				}

				// set current userId to temp user
				tempUser.setUserId(currentUser.getUserId());

				// Validate check for Contact details
				SFErrorHolder contactErrorHolder = sfUpdateUserInfoValidationUtil.validateContact(tempUser);
				if (contactErrorHolder.getFormError()) {
					jsonOutObj.put(FAILURE_REASON, "ValidateUserContactDetailsFailed!!");
					addErrorDetails(contactErrorHolder);
					stat.stop(methStatId, false);
					return jsonOutObj;
				}

				// Validate Check for EUOwnerShip
				SFErrorHolder euOwnerShipErrorHolder = sfUpdateUserInfoValidationUtil.checkEUOwnerShip(tempUser);
				if (euOwnerShipErrorHolder.getFormError()) {
					jsonOutObj.put(FAILURE_REASON, "ValidateCheckEUOwnerShipFailed!!");
					addErrorDetails(euOwnerShipErrorHolder);
					stat.stop(methStatId, false);
					return jsonOutObj;
				}

				// Validate check for VatId
				SFErrorHolder vatIdErrorHolder = sfUpdateUserInfoValidationUtil.validateCheckVatId(tempUser);
				if (vatIdErrorHolder.getFormError()) {
					jsonOutObj.put(FAILURE_REASON, "ValidateCheckVatIdFailed!!");
					addErrorDetails(vatIdErrorHolder);
					stat.stop(methStatId, false);
					return jsonOutObj;
				}

				/***
				 * Carry the prerequisite validations before updation of user
				 * data.
				 */
				// sfUpdateUserInfoValidationUtil.validateCorOccuring(tempUser,
				// currentUser);
				SFCorEventType isCoROccurring = SFBeanValidator.getInstance().isCoROccurring(currentUser, tempUser);

				// Check if request payload has edited current profile data
				isRequestHasEditedConatctDetails = sfUpdateUserInfoValidationUtil
						.isUserProfileContactDetailsEdited(tempUser, currentUser);

				if (isRequestHasEditedConatctDetails) {

					// TODO: will revisit this when we decide
					// which apis will use the microservice
					boolean isUseAcctmgrMsvc = true;

					if (isUseAcctmgrMsvc) {
						jwtToken = getMicroserviceJwtToken(payLoad);

						try {
							JSONObject data = new JSONObject();
							JSONObject address = new JSONObject();

							/*
							 * set user profile data except firtName and
							 * lastName.
							 * 
							 * firtName and lastName will be updated by calling
							 * updateAccountHolderName() method on lncPermission
							 * check after user profile data updated
							 * successfully.
							 */
							addJsonData(payLoad, data);

							addAddressJson(payLoad, validatedAddressJSON, address);
							
							// Check and set the address quality code to confidence value
							helperManager.getUserHelper().setAddressQualityCode(tempUser.getAddress(),
										                                        currentUser,
                                                                                addressConfidence);
							address.put("addressUpdateVehicle", tempUser.getAddress().getAddressUpdateVehicle());

							data.put("address", address);
							data.put("isCoROccurring", isCoROccurring.name());
							SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "updateUserProfileInfo",
									"/sf/api/user/updateUserProfileInfo", jwtToken);

							if (responseObj.getResponseInfo().getStatus().equalsIgnoreCase("success")) {

							    boolean byPassCor = "true".equalsIgnoreCase(payLoad.getField(BY_PASS_COR))? true: false;
								// TR 93614 --> copy only modifiable values,
								// without calling SFUser.copyUser()
								updateUserInfo(tempUser, currentUser, byPassCor);
								// End of work for TR 93614

								sfUpdateUserInfoValidationUtil.notifyAddressUpdateToAlertMgr(userBean);
								String jsonDataOut = responseObj.getData();
								jsonOutObj = new JSONObject(jsonDataOut);
							} else {
								failureReason = "Error occurred during update";
								isUpdateUserProfileCallFailed = true;
							}

						} catch (JSONException je) {
							failureReason = "General-SystemUnavailable";
							isUpdateUserProfileCallFailed = true;
							SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, je.getMessage());
						} catch (Exception ex) {
							failureReason = "General-SystemUnavailable";
							isUpdateUserProfileCallFailed = true;
							SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, ex.getMessage());
						}
					} else {
						// TODO: add alternative to calling microservice here
					}
				}

				//SOFT-96535 According to this ticket we dont need to submit LNC order
                //we can use FG api updateUserData(firstname/lastName/email all are updated thru this API only)
				//COMMENTED below code
				
				/*
				 * If micro service call is failed, an error response should be
				 * sent to caller and below condition will not execute.
				 * 
				 * This piece of code executes when user profile data updated
				 * successfully or request payload profile contact data not
				 * edited.
				 */
				/*if (!isUpdateUserProfileCallFailed) {
					String newFirstName = payLoad.getField(USER_DATA_FIRST_NAME_PATH);
					String newLastName = payLoad.getField(USER_DATA_LAST_NAME_PATH);

					
					 * Call updateAccountHolderName method to update User
					 * FullName.
					 * 
					 * To update FullName, check if user has a LNC permission
					 * access and firtName or lastName are not same as current
					 * user.
					 
					if (currentUser.isLncPermissionCheck()
							&& (!currentUser.getFirstName().equalsIgnoreCase(newFirstName)
									|| !currentUser.getLastName().equalsIgnoreCase(newLastName))) {
						SFLogger.printInfo(
								method + " - making a call to update Name (Accont Holder Name) change information  !!");

						
						 * set invalid AccountId, updated firstName, lastName
						 * and current company Name.
						 
						boolean isAccountHolderNameUpdated = updateAccountHolderInfoUtil.updateAccountHolderName(ZERO,
								currentUser.getCompanyName(), newFirstName, newLastName);

						if (!isAccountHolderNameUpdated) {
							
							 * if request is to update only company name and not
							 * edited profile data then send an error message as
							 * company name update is failed
							 
							if (!isRequestHasEditedConatctDetails) {
								SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
										+ " - error: unable to update user profile Account holder Name (First Name & Last Name ). returns the error details!! ");
								failureReason = "updateAccountHolderName-SystemUnavailable";
								jsonOutObj.put(FAILURE_REASON, failureReason);
								jsonOutObj.put(IS_UPDATE_SUCCESSFUL, false);
								addError(SFAPIServiceResponseError.ERROR_UPDATE_ACCOUNTHOLDER_NAME, failureReason);
								stat.stop(methStatId, false);
								return jsonOutObj;
							} else {
								SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
										+ " - error: unable to update user profile Account holder Name (First Name & Last Name ) but not returning error details to caller as already user profile information is updated successfully !!");
							}

						} else {
							// set updated firstName, lastName to
							// current user session.
							currentUser.setFirstName(newFirstName);
							currentUser.setLastName(newLastName);
							SFLogger.printInfo(method
									+ " - User profile Accont Holder Name (firstName & lastName) has been updated successfully!! ");
						}

					}
				}*/

			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";
				SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, ex.getMessage());
				stat.stop(methStatId, false);
			}
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_UPDATE_USER_PROFILE, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		return jsonOutObj;
	}

    private boolean IsNexusRequest(SFJsonObject payLoad) {
    	boolean isNexusReq = false;
    	
    	if(payLoad.getField(USER_DATA_BULK_WHO_IS_OPT_OUT_PATH) != null ||
    			payLoad.getField(USER_DATA_EMAIL_NOTIFY_PATH) != null ||
    			payLoad.getField(USER_DATA_PARTNER_PROMOTIONS_NOTIFY_PATH) != null ||
    			payLoad.getField(USER_DATA_OX_UPSELL_OPT_OUT_PATH) != null ||
    			payLoad.getField(USER_DATA_FIRST_NAME_PATH) != null ||
    			payLoad.getField(USER_DATA_LAST_NAME_PATH) != null ||
    			payLoad.getField(USER_DATA_EMAIL_PATH) != null ||
    			payLoad.getField(USER_DATA_PHONE_PATH) != null ||
    			payLoad.getField(USER_DATA_FAX_PATH) != null || 
    			payLoad.getField(USER_DATA_COMPANY_NAME_PATH) != null ||
    			payLoad.getField(USER_DATA_ADDRESS1_PATH) != null ||
    			payLoad.getField(USER_DATA_ADDRESS2_PATH) != null ||
    			payLoad.getField(USER_DATA_CITY_PATH) != null ||
    			payLoad.getField(USER_DATA_STATE_PROVINCE_PATH) != null ||
    			payLoad.getField(USER_DATA_COUNTRY_PATH) != null ||
    			payLoad.getField(USER_DATA_POSTAL_CODE_PATH) != null
    			) {
    		isNexusReq = false;
        }

    	else if(payLoad.getField(USER_DATA_US_USAGE_INTENT_PATH) != null ||  payLoad.getField(USER_DATA_US_USER_CATEGORY_PATH) != null){
        	isNexusReq = true;
        }	
        return isNexusReq;
	}

	private void updateUserInfo(SFPerson fromUser, SFUser toUser, boolean byPassCor) {
		SFContactInfo fromContact = fromUser.getContactInfo();
		SFContactInfo toContact = toUser.getContactInfo();
		
		toContact.setCompanyName((fromContact.getCompanyName() == null)? "" : fromContact.getCompanyName());
        if (fromContact.getAddress() != null && toContact.getAddress() != null) {
        	SFAddress.copyAddress(fromContact.getAddress(), toContact.getAddress());
        }
        toContact.setFirstName((fromContact.getFirstName() == null)? "" :fromContact.getFirstName());
        toContact.setLastName((fromContact.getLastName() == null)? "" :fromContact.getLastName());
        if (byPassCor) { // SOFT-111747 : update session with new email only when COR is true.
            toContact.setEmail((fromContact.getEmail() == null)? ""  : fromContact.getEmail()) ;
        }
        toContact.setSecondaryEmail((fromContact.getSecondaryEmail() == null)? ""  : fromContact.getSecondaryEmail()) ;
        toContact.setFaxNum((StringUtils.isBlank(fromContact.getFaxNum()))? "" : SFAccountUtils.stripOutCharacters(fromContact.getFaxNum(), countryList.getTelephoneCountryCode(fromContact.getCountry()))) ;
        toContact.setPhoneNum((fromContact.getPhoneNum() == null)? "" : SFAccountUtils.stripOutCharacters(fromContact.getPhoneNum(), countryList.getTelephoneCountryCode(fromContact.getCountry())) );
        toUser.setBulkWhoIsOptout(fromUser.getBulkWhoIsOptout());
        toUser.setEmailNotify(fromUser.getEmailNotify());
        toUser.setPartnerPromotionsNotify(fromUser.isPartnerPromotionsNotify());
        toUser.setOxUpsellOptout(fromUser.isOxUpsellOptout());
		// toUser.setPassword(fromUser.getPassword());
		// toUser.setConfirmPassword(fromUser.getConfirmPassword());       
        toUser.setVatId(fromUser.getVatId());
        
        String country = fromContact.getCountry();
		//SFHelperManager helperManager = sessionFacade.getHelperManager();
		SFAffiliate affiliate = helperManager == null? null : helperManager.getSfAffiliate();
		
		if (country != null && affiliate != null) {
			affiliate.setSelectedCurrency(SFCurrencyUtils.getCurrencyCodeFromCountryCode(country));
			affiliate.setGeoCountryCode(country);			
		}
		
		if(fromUser.getDotusUsageIntent() !=null){
			toUser.setDotusUsageIntent(fromUser.getDotusUsageIntent());
		}
		
		if(fromUser.getDotusUserCategory() !=null){
			toUser.setDotusUserCategory(fromUser.getDotusUserCategory());
		}
        
	}

	private void addAddressJson(SFJsonObject payLoad, JSONObject validatedAddressJSON, JSONObject address)
			throws JSONException {
		if (!validatedAddressJSON.has("address1")
				|| validatedAddressJSON.getString("address1") == null) {
			address.put("address1", payLoad.getField(USER_DATA_ADDRESS1_PATH));
		} else {
			address.put("address1", validatedAddressJSON.getString("address1"));
		}

		if (!validatedAddressJSON.has("address2")
				|| validatedAddressJSON.getString("address2") == null) {
			address.put("address2", payLoad.getField(USER_DATA_ADDRESS2_PATH));
		} else {
			address.put("address2", validatedAddressJSON.getString("address2"));
		}

		if (!validatedAddressJSON.has("city") || validatedAddressJSON.getString("city") == null) {
			address.put("city", payLoad.getField(USER_DATA_CITY_PATH));
		} else {
			address.put("city", validatedAddressJSON.getString("city"));
		}

		if (!validatedAddressJSON.has("stateProv")
				|| validatedAddressJSON.getString("stateProv") == null) {
			address.put("stateProv", payLoad.getField(USER_DATA_STATE_PROVINCE_PATH));
		} else {
			address.put("stateProv", validatedAddressJSON.getString("stateProv"));
		}

		if (!validatedAddressJSON.has("country") || validatedAddressJSON.getString("country") == null) {
			address.put("country", payLoad.getField(USER_DATA_COUNTRY_PATH));
		} else {
			address.put("country", validatedAddressJSON.getString("country"));
		}

		if (!validatedAddressJSON.has("postalCode")
				|| validatedAddressJSON.getString("postalCode") == null) {
			address.put("postalCode", payLoad.getField(USER_DATA_POSTAL_CODE_PATH));
		} else {
			address.put("postalCode", validatedAddressJSON.getString("postalCode"));
		}

		if (validatedAddressJSON.has("addressConfidence")
				&& validatedAddressJSON.getString("addressConfidence") != null) {
			address.put("addressConfidence", validatedAddressJSON.getString("addressConfidence"));
		}
	}

	private void addNexusJsonData(SFJsonObject payLoad, JSONObject data) throws JSONException {
		data.put("dotusUsageIntent", payLoad.getField(USER_DATA_US_USAGE_INTENT_PATH));
		data.put("dotusUserCategory", payLoad.getField(USER_DATA_US_USER_CATEGORY_PATH));
	}
	
	private void addJsonData(SFJsonObject payLoad, JSONObject data) throws JSONException {
		data.put("firstName", payLoad.getField(USER_DATA_FIRST_NAME_PATH));
		data.put("lastName", payLoad.getField(USER_DATA_LAST_NAME_PATH));
		data.put("email", payLoad.getField(USER_DATA_EMAIL_PATH));
		data.put("phone", payLoad.getField(USER_DATA_PHONE_PATH));
		data.put("fax", payLoad.getField(USER_DATA_FAX_PATH));
		data.put("companyName", payLoad.getField(USER_DATA_COMPANY_NAME_PATH));
		data.put("bulkWhoIsOptout", payLoad.getField(USER_DATA_BULK_WHO_IS_OPT_OUT_PATH));
		data.put("emailNotify", payLoad.getField(USER_DATA_EMAIL_NOTIFY_PATH));
		data.put("partnerPromotionsNotify", payLoad.getField(USER_DATA_PARTNER_PROMOTIONS_NOTIFY_PATH));
		data.put("oxUpsellOptout", payLoad.getField(USER_DATA_OX_UPSELL_OPT_OUT_PATH));
		data.put("byPassCor", payLoad.getField(BY_PASS_COR));
        data.put("vatId",payLoad.getField(VAT_ID_PATH));
	}
	
	private void addErrorDetails(SFErrorHolder errorHolder) throws JSONException {
		List<String> errorMsg = sfUpdateUserInfoValidationUtil.getAllErrorMessages(errorHolder);
		errorMsg.forEach(error -> addError(error));
	}

    private SFUser getTempUser(SFJsonObject payLoad) {

	    SFUser tempUser = new SFPerson();
	    if(payLoad.getField(USER_DATA_BULK_WHO_IS_OPT_OUT_PATH) != null) {
            tempUser.setBulkWhoIsOptout(Boolean.valueOf(payLoad.getField(USER_DATA_BULK_WHO_IS_OPT_OUT_PATH)));
        }

        if(payLoad.getField(USER_DATA_EMAIL_NOTIFY_PATH) != null) {
            tempUser.setEmailNotify(Boolean.valueOf(payLoad.getField(USER_DATA_EMAIL_NOTIFY_PATH)));
        }

        if(payLoad.getField(USER_DATA_PARTNER_PROMOTIONS_NOTIFY_PATH) != null) {
            tempUser.setPartnerPromotionsNotify(Boolean.valueOf(payLoad.getField(USER_DATA_PARTNER_PROMOTIONS_NOTIFY_PATH)));
        }

        if(payLoad.getField(USER_DATA_OX_UPSELL_OPT_OUT_PATH) != null) {
            tempUser.setOxUpsellOptout(Boolean.valueOf(payLoad.getField(USER_DATA_OX_UPSELL_OPT_OUT_PATH)));
        }

        if(payLoad.getField(USER_DATA_FIRST_NAME_PATH) != null) {
            tempUser.setFirstName(payLoad.getField(USER_DATA_FIRST_NAME_PATH));
        }

        if(payLoad.getField(USER_DATA_LAST_NAME_PATH) != null) {
            tempUser.setLastName(payLoad.getField(USER_DATA_LAST_NAME_PATH));
        }

        if(payLoad.getField(USER_DATA_EMAIL_PATH) != null) {
            tempUser.setEmail(payLoad.getField(USER_DATA_EMAIL_PATH));
        }

        if(payLoad.getField(USER_DATA_PHONE_PATH) != null) {
            tempUser.setPhoneNum(payLoad.getField(USER_DATA_PHONE_PATH));
        }

        if(payLoad.getField(VAT_ID_PATH) != null) {
            tempUser.setVatId(payLoad.getField(VAT_ID_PATH));
        }

		tempUser.setFaxNum(null == payLoad.getField(USER_DATA_FAX_PATH) ? "" : payLoad.getField(USER_DATA_FAX_PATH));

		tempUser.setCompanyName(null == payLoad.getField(USER_DATA_COMPANY_NAME_PATH) ? ""
				: payLoad.getField(USER_DATA_COMPANY_NAME_PATH));

        SFAddress tempAddress = new SFAddress();

        if(payLoad.getField(USER_DATA_ADDRESS1_PATH) != null) {
            tempAddress.setStreetAddress(payLoad.getField(USER_DATA_ADDRESS1_PATH));
        }

		tempAddress.setStreetAddress2(
				null == payLoad.getField(USER_DATA_ADDRESS2_PATH) ? "" : payLoad.getField(USER_DATA_ADDRESS2_PATH));
        
        if(payLoad.getField(USER_DATA_CITY_PATH) != null) {
            tempAddress.setCity(payLoad.getField(USER_DATA_CITY_PATH));
        }
        if(payLoad.getField(USER_DATA_STATE_PROVINCE_PATH) != null) {
            tempAddress.setState(payLoad.getField(USER_DATA_STATE_PROVINCE_PATH));
        }
        if(payLoad.getField(USER_DATA_COUNTRY_PATH) != null) {
            tempAddress.setCountry(payLoad.getField(USER_DATA_COUNTRY_PATH));
        }
        if(payLoad.getField(USER_DATA_POSTAL_CODE_PATH) != null) {
            tempAddress.setZip(payLoad.getField(USER_DATA_POSTAL_CODE_PATH));
        }

        tempUser.setAddress(tempAddress);

        if(payLoad.getField(USER_DATA_US_USAGE_INTENT_PATH) != null) {
            tempUser.setDotusUsageIntent(payLoad.getField(USER_DATA_US_USAGE_INTENT_PATH));
        }
        if(payLoad.getField(USER_DATA_US_USER_CATEGORY_PATH) != null) {
            tempUser.setDotusUserCategory(payLoad.getField(USER_DATA_US_USER_CATEGORY_PATH));
        }

        return tempUser;
    }

    /***
     * This method is used to save a trusted phone number.
     * @param payLoad - payload holds the trusted phone number and the 6-digit verification code.
     * @return
     * @throws SFException
     */
    private JSONObject saveTrustedPhoneNumber(SFJsonObject payLoad) throws SFException, JSONException {

    	final String method = COMPONENT + ".saveTrustedPhoneNumber - ";
        JSONObject jsonOutObj = new JSONObject();
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        String failureReason = null;
		boolean isUpdateSuccessful = false;
		boolean isErrorSpecified = false;
        
		SFUserBean userBean = userAccountManager.getUserBean();
        SFUser currentUser = userBean.getUser();
        
        String jwtToken = null;
		
		if(currentUser.getUserId() == -1){
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
        }
		else{
	            try{
	            	
	            	String trustedPhoneNumber = payLoad.getField(USER_DATA_TRUSTED_PHONE_NUMBER_PATH);

                    /***
                     * Validate if the trustedphone number is in the correct format and then go ahead.
                     */
	            	if(SFUtils.validateTrustedPhoneNumber(trustedPhoneNumber)) {
	            		
	            		//check if it is a MOCK request, if so return success
	                	if(isMockTrustedPhoneNumber(trustedPhoneNumber)){
	                		SFLogger.printInfo(method + " - mock phone number detected, return success for save trusted phone number.");
	                		isUpdateSuccessful = true;
	                	}
	            		else{
	            		

	                        String verificationCode = payLoad.getField(USER_DATA_VERIFICATION_CODE_PATH);
	
	                        JSONObject dataForVerify = new JSONObject();
	                        dataForVerify.put("code", verificationCode);
	                        dataForVerify.put("phoneNumber", trustedPhoneNumber);
	
	                        dataForVerify.put("ipAddress", determineIpAddress());
	                        dataForVerify.put("sessionId", getHashedSessionId());
	                        int userId = currentUser.getUserId();
	                        String sUserId = Integer.toString(userId);
	                        dataForVerify.put("originatorId", sUserId);
	                        dataForVerify.put("personOrgId", sUserId);
	                        
	                        jwtToken = getMicroserviceJwtToken(payLoad);
	
	                        //first test the verification code
	                        SFMsvcResponse responseObjForVerify = getAcctmgrMsvcResponse(dataForVerify, "checkVerifyTrustedPhoneNumber from saveTrustedPhoneNumber", "/sf/api/security/checkVerifyPhoneNumber", jwtToken);
	                        SFMsvcResponseInfo sfMsvcResponseInfoForVerify = responseObjForVerify.getResponseInfo();
	
	                        if ("Success".equalsIgnoreCase(sfMsvcResponseInfoForVerify.getStatus())
	                                && (sfMsvcResponseInfoForVerify.getErrors() == null || sfMsvcResponseInfoForVerify.getErrors().isEmpty())) {
	
	
	                            //check if user has 2FA enabled, if so then
	                            //disableTwoFactorAuthentication
	
	                            String authyId = userBean.getAuthyId();
	                            String twoFactorAuthStatus = currentUser.getTwoFactorAuthStatus();
	
	                            boolean isDisable2FARequired = false;
	                            boolean isDisable2FASuccessful = false;
	
	                            if (SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription().equalsIgnoreCase(twoFactorAuthStatus)) {
	                                isDisable2FARequired = true;
	                                JSONObject dataForDisable = new JSONObject();
	                                if (SFStringUtil.isEmpty(authyId)) {
	                                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " the authyId was not found in session as expected...");
	                                }
	                                dataForDisable.put(AUTHY_ID, authyId);
	                                SFMsvcResponse responseObjForDisable = getAcctmgrMsvcResponse(dataForDisable, "disableTwoFactorAuth from saveTrustedPhoneNumber", "/sf/api/security/disableTwoFactorAuthentication", jwtToken);
	                                SFMsvcResponseInfo sfMsvcResponseInfoForDisable = responseObjForDisable.getResponseInfo();
	
	                                if ("Success".equalsIgnoreCase(sfMsvcResponseInfoForDisable.getStatus())) {
	                                    isDisable2FASuccessful = true;
	                                    //the authyId becomes null once the 2fa is disabled
	                                    SFLogger.printInfo(method + " setting authyId to null ");
	                                    userBean.setAuthyId(null);
	
	                                    //update succeeded so update the value in session
	                                    currentUser.setTwoFactorAuthStatus(SFConstant.TWO_FACTOR_AUTH_DISABLED.getDescription());
	                                }
	                            }
	
	                            if (isDisable2FARequired && !isDisable2FASuccessful) {
	                                failureReason = "disable2FAFailed";
	                            } else {
	
	                                JSONObject data = new JSONObject();
	                                data.put("trustedPhoneNumber", trustedPhoneNumber);
	
	                                //verification code was good, attempt the update
	                                SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "resetCredential from saveTrustedPhoneNumber", "/sf/api/user/resetCredential", jwtToken);
	                                SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();
	                                jsonOutObj = new JSONObject(responseObj);
	
	                                //expected return data
	                        /*
	                        {
	                               "response":{
	                                 "responseInfo":{
	                                   "status":"Success",
	                                   "errors":""
	                                 }
	                               }
	                            }
	                        */
	
	                                if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
	                                    //update succeeded so update the value in session
	                                    currentUser.setTrustedPhoneNumber(trustedPhoneNumber);
	                                    isUpdateSuccessful = true;
	                                    String jsonDataOut = responseObj.getData();
	     					            JSONObject jsonOutDataObj = new JSONObject(jsonDataOut);
	     					            if (jsonOutDataObj.has(hasEmailSentStatus)) {
	     					            	boolean emailStatusReturned = jsonOutDataObj.getBoolean(hasEmailSentStatus);
	     					            	if (!emailStatusReturned) {
	     					            		addWarning(SFAPIServiceResponseError.WARNING_FAILED_TO_SEND_EMAIL, failureReason);
	     					            	}
	     					            }
	     					            
	     		                        // clear Missing TPN alert from account manager
	     								alertManager.removeAlert(SFAlert.NAME_MISSING_TPN);
	                                } else {
	                                    failureReason = "updateFailed";
	                                }
	                            }
	                        } else if(!sfMsvcResponseInfoForVerify.getErrors().isEmpty()) {
	                            failureReason = SFStringUtil.EMPTY;
	                            for(String error : sfMsvcResponseInfoForVerify.getErrors()){
	                                failureReason = failureReason + error;
	                            }
	
	                            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
	                                    "Error occured in verification of code with reason : " + failureReason);
	                        }else {
	                            failureReason = "verificationFailed";
	                        }
	            		}
                    }else{
                        failureReason = "Invalid format for trusted phone number";
                        SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                                "Invalid format for trusted phone number: ("+trustedPhoneNumber+")");
                    }
	
	            }
	            catch(Exception ex){
	            	failureReason = "General-SystemUnavailable";
	            	
		            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
		                    method + "Error : ",ex);
	            }
		}
		
		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_SAVE_TRUSTED_PHONE_NUMBER, failureReason);
			}
            isUpdateSuccessful = false;
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
		
		jsonOutObj.put(IS_UPDATE_SUCCESSFUL, isUpdateSuccessful);
		
        stat.stop(methStatId, true);
        return jsonOutObj;
    }

	/***
     * This method is used to verify the trustedPhoneNumber based on the payload input.
     * @param payLoad - payload holds the trusted phone number.
     * @return
     * @throws SFException
     */
     private JSONObject verifyTrustedPhoneNumber(SFJsonObject payLoad) throws SFException, JSONException {
    	
    	final String method = COMPONENT + ".verifyTrustedPhoneNumber - ";
    	SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        JSONObject jsonOutObj = new JSONObject();
        
        String jwtToken = null;
        
        String failureReason = null;
        boolean isErrorSpecified = false;
        
        try{
        	
        	SFUser currentUser = userAccountManager.getUserBean().getUser();
    		
    		if(currentUser.getUserId() == -1){
    			failureReason = "loginRequired";
    			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
    			isErrorSpecified = true;
            }
    		else{

    		    String trustedPhoneNumber = payLoad.getField(USER_DATA_TRUSTED_PHONE_NUMBER_PATH);
                /***
                 * Validate if the trustedphone number is in the correct format and then go ahead.
                 */
                if(SFUtils.validateTrustedPhoneNumber(trustedPhoneNumber) && getPhoneNumberForComparison(trustedPhoneNumber) != null
                        && !getPhoneNumberForComparison(trustedPhoneNumber).equals(getPhoneNumberForComparison(currentUser.getTrustedPhoneNumber()))) {

                	//check if it is a MOCK request, if so return success
                	if(isMockTrustedPhoneNumber(trustedPhoneNumber)){
                		SFLogger.printInfo(method + " - mock phone number detected, return success for verify trusted phone number.");
                		String jsonDataOut = "{\"status\":\"success\"}";
                        jsonOutObj = new JSONObject(jsonDataOut);
                	}
                	else{
	                    JSONObject data = new JSONObject();
	                    data.put("phoneNumber", trustedPhoneNumber);
	                    data.put("channel", "sms");
		    			data.put("ipAddress", determineIpAddress());
		    			String sessionId = getHashedSessionId();
		    			if(sessionId == null){
		    				sessionId = determineSessionIdWithoutHashing();
		    			}
						data.put("sessionId", sessionId);
						int userId = currentUser.getUserId();
						String sUserId = Integer.toString(userId);
						data.put("originatorId", sUserId);
						data.put("personOrgId", sUserId);
	
						SFLogger.printInfo(method + " - attempt to verify trusted phone number.");
	                    
	                    jwtToken = getMicroserviceJwtToken(payLoad);
	
	                    SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "verifyTrustedPhoneNumber", "/sf/api/security/startVerifyPhoneNumber", jwtToken);
	                    
	                    SFMsvcResponseInfo sfMsvcResponseInfoForVerify = responseObj.getResponseInfo();
	
	                    if ("Success".equalsIgnoreCase(sfMsvcResponseInfoForVerify.getStatus())) {
	                    	String jsonDataOut = responseObj.getData();
	                        jsonOutObj = new JSONObject(jsonDataOut);
	                    } else {
	                        failureReason = "verification failed due to error";
	                    }
                	}

                }else if(!SFStringUtil.isEmpty(trustedPhoneNumber)
                        && getPhoneNumberForComparison(trustedPhoneNumber).equals(getPhoneNumberForComparison(currentUser.getTrustedPhoneNumber()))) {
                    failureReason = "This phone number is already in use, please enter a new number";
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                            "This phone number is already in use ("+trustedPhoneNumber+")");
                }else{
                    failureReason = "Invalid format for trusted phone number";
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                            "Invalid format for trusted phone number ("+trustedPhoneNumber+")");
                }
    		}

        }
        catch(Exception ex){
        	failureReason = "General-SystemUnavailable";
        	SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, 
                    method + "Error : ",ex);
        }
        
        if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_VERIFY_TRUSTED_PHONE_NUMBER, failureReason);
			}
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }
        
        stat.stop(methStatId, true);
        return jsonOutObj;
    }
     
    /**
     * test whether the trustedPhoneNumber being entered should
     * be treated as a mock phone number used by automation team.
     * We do not allow this in production env.
     * 
     * @param trustedPhoneNumber
     * @return boolean - isMocked
     */
    public boolean isMockTrustedPhoneNumber(String trustedPhoneNumber){
    	boolean isMocked = false;
    	String environment = SFConfig.getInstance().getEnvironment();
    	
    	if(!ENVIRONMENT_PROD.equalsIgnoreCase(environment)) {
    		if(MOCK_TRUSTED_PHONE_NUMBER.equals(trustedPhoneNumber)){
    			isMocked = true;
    		}
    	}
    	
    	return isMocked;
    }


	/***
	 * This method is used to verify the trusted phone number with the 6 digit code sent as a part of the payload.
	 *
	 * @param payLoad -
	 *    {
	 *    "request":{
	 *       "requestInfo":{
	 *          "service":"UserAPI",
	 *          "method":"checkVerifyTrustedPhoneNumber",
	 *          "clientId":"SOME_CLIENT",
	 *          "apiAccessKey":"xxxxxxxxxxxxxxxxx"
	 *       },
	 *       "trustedPhoneNumber":"7037758889",
	 *		 "verificationCode":"123456"
	 * }
	 *
	 * @return -
	 *   {
	 *    "response":{
	 *       "responseInfo":{
	 *         "errors":null,
	 *         "status":"Success"
	 *       }
	 *     }
	 * 	 }
	 */
	public JSONObject checkVerifyTrustedPhoneNumber(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".checkVerifyTrustedPhoneNumber - ";
		JSONObject jsonOutObj = new JSONObject();
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		String failureReason = null;
		boolean isErrorSpecified = false;
		
		String jwtToken = null;

		SFUser currentUser = userAccountManager.getUserBean().getUser();

		if(currentUser.getUserId() == -1){
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
		}
		else {
			
			try {

				String trustedPhoneNumber = payLoad.getField(USER_DATA_TRUSTED_PHONE_NUMBER_PATH);

                /***
                 * Validate if the trustedphone number is in the correct format and then go ahead.
                 */
                if(SFUtils.validateTrustedPhoneNumber(trustedPhoneNumber)) {

                    String verificationCode = payLoad.getField(USER_DATA_VERIFICATION_CODE_PATH);

                    JSONObject dataForVerify = new JSONObject();
                    dataForVerify.put("code", verificationCode);
                    dataForVerify.put("phoneNumber", trustedPhoneNumber);

                    dataForVerify.put("ipAddress", determineIpAddress());
                    String sessionId = getHashedSessionId();
	    			if(sessionId == null){
	    				sessionId = determineSessionIdWithoutHashing();
	    			}
                    dataForVerify.put("sessionId", sessionId);
                    int userId = currentUser.getUserId();
                    String sUserId = Integer.toString(userId);
                    dataForVerify.put("originatorId", sUserId);
                    dataForVerify.put("personOrgId", sUserId);
                    
                    jwtToken = getMicroserviceJwtToken(payLoad);

                    SFMsvcResponse responseObjForVerify = getAcctmgrMsvcResponse(dataForVerify, "checkVerifyTrustedPhoneNumber", "/sf/api/security/checkVerifyPhoneNumber", jwtToken);
                    SFMsvcResponseInfo sfMsvcResponseInfoForVerify = responseObjForVerify.getResponseInfo();

                    if ("Success".equalsIgnoreCase(sfMsvcResponseInfoForVerify.getStatus()) && (sfMsvcResponseInfoForVerify.getErrors() == null || sfMsvcResponseInfoForVerify.getErrors().isEmpty())) {
                        jsonOutObj = new JSONObject(responseObjForVerify);
                    } else if(!sfMsvcResponseInfoForVerify.getErrors().isEmpty()) {
                        failureReason = SFStringUtil.EMPTY;
                        for(String error : sfMsvcResponseInfoForVerify.getErrors()){
                            failureReason = failureReason + error;
                        }

                        SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                                "Error occured in verification of code with reason : " + failureReason);
                    }

                        else
                    {
                        failureReason = "verificationFailed";

                        /*
                        List<String> errors = sfMsvcResponseInfoForVerify.getErrors();
                        StringBuffer errorBuf = new StringBuffer();
                        if (errors != null) {
                            for (String currentError : errors) {
                                errorBuf.append(currentError);
                            }
                        }

                        SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT
                                + method + "Error : " + errorBuf.toString());
                                */
                    }
                }else{
                    failureReason = "Invalid format for trusted phone number";
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                            "Invalid format for trusted phone number ("+trustedPhoneNumber+")");
                }

			} catch (Exception e) {
				failureReason = "General-SystemUnavailable";

				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
						method + "Error : " + e);
			}
		}
		

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_VERIFY_TRUSTED_PHONE_NUMBER, failureReason);
			}
			stat.stop(methStatId, false);
		}
		else{
			stat.stop(methStatId,true);
		}

		return jsonOutObj;
	}

	private String getPhoneNumberForComparison(String phoneNumber){
	    
	    String phoneNumberForCompare = null;
	    
	    if(phoneNumber == null){
	    	phoneNumberForCompare = SFStringUtil.EMPTY;
	    }
	    else{
	    	phoneNumberForCompare = phoneNumber;
	    }
	    
	    return phoneNumberForCompare;
    }

    /***
     * This method is used to create the recovery key for a user.
     * @return
     * @throws SFException
     */
    private JSONObject createRecoveryKey(SFJsonObject payLoad) throws SFException, JSONException {
		JSONObject jsonOutObj = new JSONObject();
		final String method = COMPONENT + ".createRecoveryKey - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		String failureReason = null;
		boolean isErrorSpecified = false;
		
		String jwtToken = null;

		SFUser currentUser = userAccountManager.getUserBean().getUser();

		if (currentUser.getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
			
		} else{

			try {

				JSONObject data = new JSONObject();
				JSONObject recoveryKeyInfo = new JSONObject();
				recoveryKeyInfo.put("userId", String.valueOf(currentUser.getUserId()));
				recoveryKeyInfo.put("clientIp", determineIpAddress());
				String sessionId = getHashedSessionId();
    			if(sessionId == null){
    				sessionId = determineSessionIdWithoutHashing();
    			}
				recoveryKeyInfo.put("sessionId", sessionId);
				recoveryKeyInfo.put("timeStamp", String.valueOf(System.currentTimeMillis()));
				data.put("recoveryKeyInfo", recoveryKeyInfo);
				
				jwtToken = getMicroserviceJwtToken(payLoad);

				SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "createRecoveryKey", "/sf/api/security/createRecoveryKey", jwtToken);
				
				SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

				if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
				   String jsonDataOut = responseObj.getData();
				   jsonOutObj = new JSONObject(jsonDataOut);
				}
				else{
					failureReason = "error occurred when creating recovery key";
				}

			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";

				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
						method + "Error : " + ex);
			}
			
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_CREATE_RECOVERY_KEY, failureReason);
			}
			stat.stop(methStatId, false);
		}
		else{
			stat.stop(methStatId,true);
		}

        return jsonOutObj;
    }

    /***
     * This method is used to verify the recovery key which is entered as a part of the request
     * @param payLoad - holds the recovery key for verification.
     * @return
     * @throws SFException
     */
    private JSONObject verifyRecoveryKey(SFJsonObject payLoad) throws SFException, JSONException {
        JSONObject jsonOutObj = new JSONObject();
        final String method = COMPONENT + ".verifyRecoveryKey - ";
    	SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
		String failureReason = null;
		
		boolean isVerifySuccessful = false;
		
		String jwtToken = null;

		String recoveryKey = payLoad.getField(USER_DATA_RECOVERY_KEY_PATH);
		
		SFUserBean userBean = userAccountManager.getUserBean();
		SFUser currentUser = userBean.getUser();

		if (currentUser.getUserId() == -1) {
			//avoid setting as an error, as agreed with angular team,
			//they will interpret response
			failureReason = "loginRequired";
		} 
		else if(SFStringUtil.isEmpty(recoveryKey)){
			//avoid setting as an error, as agreed with angular team,
			//they will interpret response
        	failureReason = "The recovery key is a required field";
        }		
		else{
			
			try {

				JSONObject data = new JSONObject();
				JSONObject userData = new JSONObject();
				JSONObject sessionData = new JSONObject();

				userData.put("userLogInName",currentUser.getUserLoginName());
				userData.put("recoveryKey", recoveryKey);
				userData.put("originatorId", currentUser.getOriginatorId());

				SFFraudProtection fraud = currentUser.getFraudProtection();
				if (null != fraud.getBrowserId()) {
					sessionData.put("browserId", fraud.getBrowserId());
				}else{
					sessionData.put("browserId", SFStringUtil.EMPTY);
				}

				if (null != fraud.getCookieId()) {
					sessionData.put("cookie", fraud.getCookieId());
				}else{
					sessionData.put("cookie", SFStringUtil.EMPTY);
				}

				if (null != fraud.getHostName()) {
					sessionData.put("hostName", fraud.getHostName());
				}else{
					sessionData.put("hostName", SFStringUtil.EMPTY);
				}

				if (null != fraud.getIpAddress()) {
					sessionData.put("ipAddress", fraud.getIpAddress());
				}else{
					sessionData.put("ipAddress", SFStringUtil.EMPTY);
				}

				if (null != fraud.getSessionId()) {
					sessionData.put("sessionId", fraud.getSessionId());
				}else{
					sessionData.put("sessionId", SFStringUtil.EMPTY);
				}

				data.put("userData", userData);
				data.put("sessionData", sessionData);
				
				jwtToken = getMicroserviceJwtToken(payLoad);
				
				SFMsvcResponse responseObj   = getAcctmgrMsvcResponse(data, "verifyRecoveryKey", "/sf/api/security/authenticateSFLoginWithRecoveryKey", jwtToken);
				SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

				if ("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())) {
					String jsonDataOut = responseObj.getData();
					jsonOutObj = new JSONObject(jsonDataOut);

					if (jsonOutObj.has(IS_VALID)) {
						boolean isValid = jsonOutObj.getBoolean(IS_VALID);
						if (isValid) {
							isVerifySuccessful = true;
							SFLogger.printInfo(
									method + " - verification successful, marking user as being logged in.");
						} else {
							failureReason = "verificationFailed";
							SFLogger.printInfo(
									method + " - verification failed, marking user as not being logged in.");
							int count = userBean.getFalseRecoveryKeyCount();
							count++;
							userBean.setFalseRecoveryKeyCount(count);
							jsonOutObj.put(FALSE_RECOVERY_KEY_COUNT, count);
						}
					} else {
						throw new SFException(
								"the isValid param from the verify recovery key api was not found in the data returned");
					}
				} else {
					failureReason = "verificationFailed";
					// not going to increment the error count since it
					// seemed to fail due to a bug
					int count = userBean.getFalseRecoveryKeyCount();
					jsonOutObj.put(FALSE_RECOVERY_KEY_COUNT, count);
				}

			} catch (Exception ex) {
				failureReason = "General-SystemUnavailable";

				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
						method + "Error : " + ex);
			}
			
		}

		if (StringUtils.isNotEmpty(failureReason)) {
			jsonOutObj.put(FAILURE_REASON, failureReason);
			//avoid setting as an error, as agreed with angular team,
			//they will interpret response
			//addError(SFAPIServiceResponseError.ERROR_VERIFY_RECOVERY_KEY, failureReason);
			stat.stop(methStatId, false);
		}
		else{
			stat.stop(methStatId,true);
		}
		
		//mark the user as being logged in based on results, since 2FA is completed
		userAccountManager.getUserBean().setIsLoggedIn(isVerifySuccessful);
		//status.setIsAuthenticated(isVerifySuccessful);
		
        return jsonOutObj;
    }

    /***
     * This is a wrapper method which can be called before making the microservice call to AcctmgrMsvc.
     * This will build the requestInfo and the additionalInfo which are required for USER APIs.
     *
     * @param data - JSONObject which is based on the respective method to be performed.
     * @param methodName - method to be carried.
     * @param apiURL - Rest URL of the microservice API.
     * @return
     * @throws SFException
     */
    public SFMsvcResponse getAcctmgrMsvcResponse(JSONObject data, String methodName, String apiURL, String jwtToken) throws SFException {
    	final String method = COMPONENT + ".getAcctmgrMsvcResponse - ";
    	SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        SFMsvcResponse responseObj = null;
        
        try {
            JSONObject requestWrapper = new JSONObject();
            JSONObject requestObj = new JSONObject();
            JSONObject requestInfo = new JSONObject();

            SFUserBean userBean = userAccountManager.getUserBean();
            if (userBean.getUser().getUserId() == -1) {
                throw new SFException("user info cannot be updated without a valid user login");
            }
            requestInfo.put("userId", userBean.getUserId());
            requestInfo.put("userPassword", getUserPassword(userBean.getUserId()));
            requestInfo.put(CLIENT_IPADDRESS, determineIpAddress());

            requestObj.put("requestInfo", requestInfo);
            requestObj.put("data", data);

            requestWrapper.put("request", requestObj);
            
            int personOrgId = userBean.getUserId();
            
            String sessId = determineSessionIdWithoutHashing();
					
			StringBuilder methodNameSb = new StringBuilder();
            methodNameSb.append(methodName);
            methodNameSb.append(" - personOrgId("+personOrgId+")");
            methodNameSb.append(" - " + sessId);

            String formattedMethodName =  methodNameSb.toString();

            responseObj = SFMicroServiceUtil.getAcctmgrMsvcResponse(requestWrapper, formattedMethodName, "user", apiURL, jwtToken);

            // check if  response from microservice is not success and add error in such case.
            if(!"success".equalsIgnoreCase(responseObj.getResponseInfo().getStatus())){
                List<String> msvcErrors = responseObj.getResponseInfo().getErrors();
                if(msvcErrors != null || !msvcErrors.isEmpty()) {
                    StringBuffer errorBuf = new StringBuffer();
                    for (String error : msvcErrors) {
                        addError(error);
                        errorBuf.append(error);
                    }

                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM, COMPONENT
                            + method + "Microservice Error : " + errorBuf.toString());
                }

            }

        } catch(Exception e) {
        	stat.stop(methStatId, false);
            SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, e.getMessage());
            throw new SFException("error occured in processing data to AcctmgrMsvc", e);
        }
        
        stat.stop(methStatId, true);
        
        return responseObj;
    }

    private String getUserPassword(int userId) throws SFFGException {
        return helperManager.getUserHelper().getEncryptedPasswordFromUserId(userId);
    }

	private boolean isLargeUser(SFUser sfUser) {
		return (sfUser.isAccountThresholdExceeded() || sfUser.isProductsThresholdExceeded());
	}

	private JSONObject processAuthenticatePasswordPlus(SFJsonObject payLoad) throws JSONException {
		boolean result = authenticatePasswordPlus(payLoad.getField(PP_SECURITY_CODE_PATH));
        return new JSONObject().put(AUTHENTICATED, result);
	}

    private JSONObject processIsUserLoggedIn() throws JSONException {
        JSONObject result = new JSONObject();
        result.put(IS_LOGGED_IN, userAccountManager.getUserBean().getIsLoggedIn());
        result.put(LOGIN_METHOD, getLogInMethod());
        result.put(HASHED_SESSION_ID, getHashedSessionId());
        result.put(HASHED_USER_ID, SFUtils.generateHashedValue(String.valueOf(userAccountManager.getUserBean().getUserId())));
        result.put(FIRST_NAME_ATTR, userAccountManager.getUserBean().getFirstName());
        result.put(LAST_NAME_ATTR, userAccountManager.getUserBean().getLastName());
        
        boolean isAuthenticated = false;
        if(status != null && status.getIsAuthenticated()){
        	isAuthenticated = true;
        }
        result.put(IS_USER_AUTHENTICATED, isAuthenticated);
        return result;
    }
    
    

    /**
     * This method is intended to handle the login process.
     * 
     * It is similar to authenticateUser, though authenticateUser
     * is used after login.  This method will control the flags related
     * to the isLoggedIn and isAuthenticated properly for login
     * 
     * @param payLoad
     * @return
     * @throws JSONException
     */
    private JSONObject processAuthenticateUserLogin(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processAuthenticateUserLogin - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        boolean isAuthenticated = false;
        String failureReason = null;
        
        String userLoginName = payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH);
        String password = payLoad.getField(PASSWORD_PATH);
        Integer failedLoginAttemptCountInt = null;
        boolean isUsernamePasswordInvalid = false;
        boolean isSms2FACodeSent = false;
        Boolean isNewUserCouponRemoved = null;
        boolean isPasswordPlusLoginRequired = false;
        boolean isResetPasswordRequired = false;
        String authFailureReason = null;
        String twoFactorAuthStatus = null;
        String trustedPhoneNumber = null;
        String userIdAttr = null;
        boolean isLocked = false;
        boolean isFirstLogin = false;
        boolean isPINSet = false;
        
        String jwtToken = null;
        SFUser currentUser = null;
		String amLoginHomePage = null;
		String sessionId = SFStringUtil.EMPTY;
        boolean hasHostingProduct = false;
        
        try{
        	
	        if(SFStringUtil.isEmpty(userLoginName)){
	        	failureReason = "User Login Name is a required field";
	        	status.setIsAuthenticated(isAuthenticated);
	        	userAccountManager.getUserBean().setIsLoggedIn(isAuthenticated);
	        }
	        else if(SFStringUtil.isEmpty(password)){
	        	failureReason = "Password is a required field";
	        	status.setIsAuthenticated(isAuthenticated);
	        	userAccountManager.getUserBean().setIsLoggedIn(isAuthenticated);
	        }
	        else{
	        
	        	//reset user bean data
		        userAccountManager.getUserBean().setIsNewCustomer(false);
		        userAccountManager.getUserBean().setIsLoggedIn(false);
		        userAccountManager.getUserBean().setAuthyId(null);
		        userAccountManager.getUserBean().setFalseRecoveryKeyCount(0);
	        	userAccountManager.getUserBean().setFalseVerificationCodeCount(0);
	        	userAccountManager.getUserBean().setFailedLoginAttemptCount(0);
	        	
	        	sessionId = helperManager.getSessionOriginInfo().getOriginalSessionId();
	        			        
		        JSONObject authResultJson = new JSONObject();
		        SFAuthenticationResult authenticationResult = authenticateUser(payLoad, authResultJson, false);
		        
		        boolean isPassedAuthenticationProcess = false;
		        
		        if (validateLogInResult(authenticationResult, authResultJson, false)) {
		            userAccountManager.getUserBean().setIsFirstLogin(authenticationResult.getIsFirstLogin());
		            isPassedAuthenticationProcess = logInAndPopulateUserInfo(authResultJson, false, false);
		            
		            //NOTE: user is not logged in yet, will control loggedin and authenticated flags below
		            //and also in the verifySmsCode method as used with 2FA when 2FA is 'enabled'.
		             
		        }
		        		        		        
		        if (!isPassedAuthenticationProcess && authResultJson != null && authResultJson.has(FAILURE_REASON)) {
		        	authFailureReason = authResultJson.getString(FAILURE_REASON);
		        	failureReason = authFailureReason;
		        	
		        	try{
		        		//check if user has been locked out
		        		//User WB11428233CHG cannot login because maximum attempts have been exceeded from ip 127.0.0.1
		        		if(failureReason != null && failureReason.indexOf("maximum attempts have been exceeded") != -1){
		        		   isLocked = true;
		        		}
		        		else if(failureReason != null && failureReason.indexOf("password does not match with") != -1){
						   isUsernamePasswordInvalid = true;
						   isLocked = true;
		        		}
		        		else if(failureReason != null && failureReason.indexOf("No data found in edb for loginName") != -1){
						   isUsernamePasswordInvalid = true;
						   isLocked = true;
			        	}
		        		
		        	}
		        	catch(Exception ex1){
		        		SFLogger.printWarning(method + " unable to check failure reason to see if user was locked or password invalid", ex1);
		        	}
		        }
		        
		        //add the additional attributes which were appended during authentication
		        if (authResultJson != null){
                    if(authResultJson.has(NEW_USER_COUPON_REMOVED)) {
		        	   isNewUserCouponRemoved = authResultJson.getBoolean(NEW_USER_COUPON_REMOVED);
                    }
		            if(authResultJson.has(PASSWORD_PLUS_LOGIN_REQUIRED)) {
		        	   isPasswordPlusLoginRequired = authResultJson.getBoolean(PASSWORD_PLUS_LOGIN_REQUIRED);
		            }
			        if (authResultJson.has(USER_ID_ATTR)) {
			        	userIdAttr = authResultJson.getString(USER_ID_ATTR);
			        }
			        if (authResultJson.has(RESET_PASSWORD_ATTR)) {
			        	isResetPasswordRequired = authResultJson.getBoolean(RESET_PASSWORD_ATTR);
			        }
	            }
		        
		        
		        if(userAccountManager.getUserBean() != null){
		        	currentUser = userAccountManager.getUserBean().getUser();
		        }
		        else{
		        	SFLogger.printWarning(method + " user bean object is null, cannot determine 2fa info.");
		        }
		        if(currentUser != null){
		        	//reset the flag, will be set further along in the process
		        	currentUser.setLoggedIn(false);
		        	//some 2fa data is returned only during authentication, populate it now
		        	SFUtils.populateTwoFactorAuthFieldsFromAuthentication(authenticationResult, userAccountManager.getUserBean());
		        	
		        	//userAccountManager.getUserBean().setAuthyId(authenticationResult.getAuthyId());
		        	//SFLogger.printInfo(method + " setting authyId ("+authenticationResult.getAuthyId()+") ");
		        	//userAccountManager.getUserBean().setFalseRecoveryKeyCount(authenticationResult.getFalseRecoveryKeyCount());
		        	//userAccountManager.getUserBean().setFalseVerificationCodeCount(authenticationResult.getFalseVerificationCodeCount());
		        	
		        	//lookup last successful login date, for display on AM home
					long lastLoginDate = authenticationResult.getLastSuccessfulLogin();
					if (0 != lastLoginDate) {
						userAccountManager.getUserBean().setLastSuccessfulLogin(new Date(lastLoginDate));
					}

		        	isFirstLogin = authenticationResult.getIsFirstLogin();
		        	userAccountManager.getUserBean().setIsFirstLogin(isFirstLogin);
		        	
			        twoFactorAuthStatus = currentUser.getTwoFactorAuthStatus();
			        
			        failedLoginAttemptCountInt = authenticationResult.getFailedLoginAttemptCount();
			        //userAccountManager.getUserBean().setFailedLoginAttemptCount(failedLoginAttemptCountInt);
			        
			        trustedPhoneNumber = currentUser.getTrustedPhoneNumber();
			        if(!SFStringUtil.isEmpty(trustedPhoneNumber)){
			        	int len = trustedPhoneNumber.length();
			        	String last4 = null;
			        	if(len > 4){
			        		int lenRemain = len - 4; 
			        		last4 = trustedPhoneNumber.substring(lenRemain);
			        	}
			        	else{
			        		last4 = trustedPhoneNumber;
			        	}
			        	trustedPhoneNumber = SFUtils.maskTrustedPhoneNumber(last4);
			        }
			        
			        if(SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription().equalsIgnoreCase(twoFactorAuthStatus)){
			        	//2fa replaces password plus, so if it is enabled, we should ignore password plus.
			        	isPasswordPlusLoginRequired = false;
						userAccountManager.getUserBean().getPasswordPlusInfo().setStatus(null);
			        }
			        
					if (!SFStringUtil.isEmpty(currentUser.getPin())) {
						isPINSet = true;
					}
					
					//i got the sessionid earlier but this seems the more trusted way
					SFFraudProtection fraud = currentUser.getFraudProtection();
					if (fraud != null && null != fraud.getSessionId()) {
						sessionId = fraud.getSessionId();
					}
					
					
		        }
		        else if(currentUser == null){
		        	SFLogger.printInfo(method + " sf user object is null, cannot determine 2fa info.");
		        }
		       
		        if(isPassedAuthenticationProcess && isResetPasswordRequired){
		        	//set isAuthenticated flag to true, so angular team will know that password was ok.
		        	isAuthenticated = true;
		        	SFLogger.printInfo(method + " - reset password is required for user.");
		        	userAccountManager.getUserBean().setIsLoggedIn(false);
		        	status.setIsAuthenticated(false); 
	        	}
	        	else if(isPassedAuthenticationProcess && isPasswordPlusLoginRequired){
	        		//set isAuthenticated flag to true, so angular team will know that password was ok.
	        		isAuthenticated = true;
		        	SFLogger.printInfo(method + " - reset password is required for user.");
		        	userAccountManager.getUserBean().setIsLoggedIn(false);
		        	status.setIsAuthenticated(false); 
	        	}
	        	else{
		        	//check if 2FA is enabled, if so then start that process
			        if(isPassedAuthenticationProcess && currentUser != null && SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription().equalsIgnoreCase(twoFactorAuthStatus)){
			        	SFLogger.printInfo(method + " - userId ("+currentUser.getUserId()+") has 2fa enabled.");
			        	
			        	//since user passed authentication, I am setting this 
			        	//flag to true, so angular team will know that password was ok.
			        	//But we should not set the userbean.isLoggedIn 
			        	//until sms is verified for 2fa.
			        	isAuthenticated = true;
			        	status.setIsAuthenticated(isAuthenticated); 
			        	
			        	
			        	//send the sms code to the phone
			        	JSONObject jsonOutObj = new JSONObject();
			    		JSONObject requestInfo = new JSONObject();
			    		
			    		// call the acctmgr microservice
			    		try {
			    			
			    			JSONObject data = new JSONObject();
			    			
			    			String authyId = userAccountManager.getUserBean().getAuthyId();
			    			
			    			if(SFStringUtil.isEmpty(authyId)){
			    				throw new SFException("The authy id was empty but is required when 2FA is enabled. Cannot send sms code to trusted phone number");
			    			}
			    			else{
			
				    			data.put(AUTHY_ID, authyId);
				    			data.put("ipAddress", determineIpAddress());
				    			String sessId = getHashedSessionId();
				    			if(sessId == null){
				    				sessId = determineSessionIdWithoutHashing();
				    			}
								data.put("sessionId", sessId);
								int userId = currentUser.getUserId();
								String sUserId = Integer.toString(userId);
								data.put("originatorId", sUserId);
								data.put("personOrgId", sUserId);
								data.put("phoneNumber", currentUser.getTrustedPhoneNumber());
				    			    							
				    			SFLogger.printInfo(method + " - attempt to send sms code to trusted phone number.");
				    			
				    			jwtToken = getMicroserviceJwtToken(payLoad);
				    			
				    			SFMsvcResponse responseObj = getAcctmgrMsvcResponse(data, "sendSmsCode from authenticateUserLogin", MS_SEND_SMSCODE_API_CALL, jwtToken);
				    			SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();
				                jsonOutObj = new JSONObject(sfMsvcResponseInfo);
				                
				                if("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())){
				                	
				                	String jsonDataOut = responseObj.getData();
									jsonOutObj = new JSONObject(jsonDataOut);

									if (jsonOutObj.has(STATUS_ATTR)) {
										String smsStatus = jsonOutObj.getString(STATUS_ATTR);
										if ("success".equalsIgnoreCase(smsStatus)) {	
											isSms2FACodeSent = true;
											SFLogger.printInfo(method + " - sms code has been sent to trusted phone number.");
										} else {
											failureReason = "sendSmsCodeFailed";
											SFLogger.printInfo(method + " - send SmsCode to trustedphone is failed !!");
											
											// log the actual error returned
											List<String> errors = sfMsvcResponseInfo.getErrors();
											StringBuffer errorBuf = new StringBuffer();
											if (errors != null) {
												for (String currentError : errors) {
													errorBuf.append(currentError);
												}
											}

											SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
													COMPONENT + method + "Error : " + errorBuf.toString());
										}
									} 
									else {
										failureReason = "There may have been a problem sending the sms code to the trusted phone number.., the status was not returned.";
									}
				                	
					            }
				                else{
				                	failureReason = "There was an error when sending the sms code to the trusted phone number...";
				                }
				                
			    			}
			
			    		} catch (Exception ex) {
			    			failureReason = "There was an error when sending the sms code to the trusted phone number";
			    			SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT
			                        + method + "Error while sending 2fa sms code for userId ("+currentUser.getUserId()+"): " + ex);
			    		}
			        }
			        else if(isPassedAuthenticationProcess){
			        	//since 2FA is disabled, and user passed authentication		        	
			        	isAuthenticated = true;
			        	SFLogger.printInfo(method + " - user does not have 2fa enabled, but has authenticated, marking as logged in.");
			        	userAccountManager.getUserBean().setIsLoggedIn(isAuthenticated);
			        	status.setIsAuthenticated(isAuthenticated); 
			        }
			        else {
			        	//since 2FA is disabled, but user failed authentication, they are not logged in
			        	isAuthenticated = false;
			        	isLocked = true;
			        	SFLogger.printInfo(method + " - user authentication did not succeed.");
			        	userAccountManager.getUserBean().setIsLoggedIn(isAuthenticated);
			        	status.setIsAuthenticated(isAuthenticated); 
			        }
	        	}
	        }
        }
        catch(Exception ex2){
        	isAuthenticated = false;
        	isLocked = true;
        	if(failureReason == null){
        	   failureReason = "General-SystemUnavailable";
        	}
        	//not setting error code here as agreed with angular team
        	//they will interpret the results
        	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, method + " - unknown exception occurred.", ex2);
        }
        
        
        //the UI needs this api to always have a success path
        //they will parse the results.
        
    	
        result.put(AUTHENTICATED, isAuthenticated);
        result.put(RESET_PASSWORD_ATTR,isResetPasswordRequired);
        result.put(IS_LOCKED, isLocked);
        if(failedLoginAttemptCountInt != null){
	           result.put(FAILED_LOGIN_ATTEMPT_COUNT,failedLoginAttemptCountInt.intValue());
	    }

		//SOFT-97749
		if (isAuthenticated) {
	        SFAffiliate affiliate = helperManager == null? null : helperManager.getSfAffiliate();
			result.put(HASHED_LOGIN_ID,SFUtils.generateHashedValue(String.valueOf(currentUser.getUserId())));
			result.put(LOGIN_METHOD,getLogInMethod());
			if(userAccountManager.getUserBean().getIsLoggedIn()){
				result.put(LOGIN_STATUS,LOGGED_IN);
			}
			else{
				result.put(LOGIN_STATUS,NOT_LOGGED_IN);
			}
			if(null != getRequestInfo()) {
                result.put(COOKIE_VRSNSF, SFUtils.generateHashedValue(getRequestInfo().getSessionCookie(COOKIE_VRSNSF)));
            }
			if(affiliate!=null){
				result.put(SITE_ID,affiliate.getSiteId());
			}
			
			if(!SFStringUtil.isEmpty(trustedPhoneNumber)){
		           result.put(TRUSTED_PHONE_NUMBER,trustedPhoneNumber);
		        }
		        
		        result.put(SMS_2FA_CODE_SENT, isSms2FACodeSent);
		        
		        if(isNewUserCouponRemoved != null){
		           result.put(NEW_USER_COUPON_REMOVED, isNewUserCouponRemoved.booleanValue());
		        }
		                
		        result.put(PASSWORD_PLUS_LOGIN_REQUIRED,isPasswordPlusLoginRequired);
		        
		        if(!SFStringUtil.isEmpty(userIdAttr)){
		            result.put(USER_ID_ATTR, userIdAttr);
		        }
		                
		        result.put(IS_FIRST_LOGIN, isFirstLogin);
		        if(isFirstLogin && SFChannelUtil.isBlueHostChannel()){
                    SFProductHolder sfProductHolder = ((SFAccount) currentUser.getAccounts().get(0)).getProductCollection();
                    hasHostingProduct = sfProductHolder.getHasHostingProduct();
                    if(hasHostingProduct) {
                        List<SFHosting> hostingProducts = sfProductHolder.getAllProductByType("com.nsi.storefront.business.SFHosting");
                        SFHosting sfHosting = hostingProducts.get(0);
                        result.put(PRODUCT_CODE, sfHosting.getProductCode());
                        result.put(INSTANCE_ID, sfHosting.getFGImplementationDetailInfo().getFGProductInstanceId());
                        result.put(ACCOUNT_ID_ATTR, sfProductHolder.getAccount().getAccountId());
                    }
                }
		        result.put(IS_PIN_SET,isPINSet);
		        
		        //we want to avoid returning null to avoid test-eng complaint
				if(sessionId == null){
					sessionId = SFStringUtil.EMPTY;
				}
				result.put(SESSION_ID,sessionId);
				
				// SOFT-96161 - added hashedSessionId
				result.put(HASHED_SESSION_ID, getHashedSessionId());
				if (StringUtils.isNotEmpty(failureReason)) {
		            result.put(FAILURE_REASON, failureReason);
		        }
		        	
		    	if(!SFStringUtil.isEmpty(twoFactorAuthStatus)){
		            result.put(TWO_FACTOR_AUTH_STATUS,twoFactorAuthStatus);
		        }
		}
                
		// set am login landing home page
		if (isAuthenticated) {			
			amLoginHomePage = amLoginHomePageUtil.getAMMLoginLandingHomePage();
		}
		result.put(AM_LOGIN_HOMEPAGE, amLoginHomePage);
		
		if(isUsernamePasswordInvalid){
			SFCoreRequestInfo origRequestInfo = super.getRequestInfo();
			if(origRequestInfo != null){
				HttpServletResponse httpResponse = origRequestInfo.getHttpResponse();
				if(httpResponse != null){
				   httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
				   SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " - since username or password were invalid, setting status 401.");
				}
			}
		}

		stat.stop(methStatId, true);
        return result;
    }


    private JSONObject processAuthenticateUser(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processAuthenticateUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        boolean isAuthenticated = false;
        userAccountManager.getUserBean().setIsNewCustomer(false);
        status.setIsAuthenticated(true);
        userAccountManager.getUserBean().setIsLoggedIn(true);

        SFAuthenticationResult authenticationResult = authenticateUser(payLoad, result);
        if (validateLogInResult(authenticationResult, result)) {
            userAccountManager.getUserBean().setIsFirstLogin(authenticationResult.getIsFirstLogin());
            isAuthenticated = logInAndPopulateUserInfo(result);
        }
        
        result.put(AUTHENTICATED, isAuthenticated);
        stat.stop(methStatId, true);
        return result;
    }
    
    /** default method **/
    private SFAuthenticationResult authenticateUser(SFJsonObject payLoad, JSONObject result) throws JSONException
    {
    	return authenticateUser(payLoad, result, true);
    	
    }

    /** 
     * 
     * @param payLoad
     * @param result
     * @param isAddErrorRequired - allow calling method to handle error
     * @return
     * @throws JSONException
     */
    private SFAuthenticationResult authenticateUser(SFJsonObject payLoad, JSONObject result, boolean isAddErrorRequired) throws JSONException
    {
        final String method = COMPONENT + ".authenticateUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        SFAuthenticationResult authenticationResult = null;
        try {
            status.setIsFromRenewalPromotion(false);
            status.setIsFromExpressRenewal(false);
            status.setIsAuthenticated(false);
            if(userAccountManager.getUserBean().getUser().getIsPasswordEncrypted()){
                userAccountManager.getUserBean().clear();
                userAccountManager.getUserBean().getUser().setIsPasswordEncrypted(true);
            }else{
                userAccountManager.getUserBean().clear();
            }
            userAccountManager.getUserBean().getAlertManager().clear();
            userAccountManager.getUserBean().setUserLoginName(payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH));
            userAccountManager.getUserBean().setPassword(payLoad.getField(PASSWORD_PATH));
            userAccountManager.getUserBean().setConfirmPassword(payLoad.getField(PASSWORD_PATH));
            authenticationResult = helperManager.getUserHelper().authenticateLogin(userAccountManager.getUserBean().getUser(),
                    SFLoginTypeConstant.MANUAL, -ONE, (long) SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue());
            
            SFUtils.populateTwoFactorAuthFieldsFromAuthentication(authenticationResult, userAccountManager.getUserBean());
            
        } catch (Exception e) {
            result.put(FAILURE_REASON, e.getMessage());
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT
                    + method + "Error while authenticating user: " + e);
            if(isAddErrorRequired){
               addError(SFAPIServiceResponseError.ERROR_AUTHENTICATION, e.getMessage());
            }
        }
        stat.stop(methStatId, true);
        return authenticationResult;
    }
    

    private JSONObject processMergeUser(SFJsonObject payLoad) throws JSONException {
    	
    	final String method = COMPONENT + ".processMergeUser - ";
    	SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        
        JSONObject result = new JSONObject();
        String failureReason = null;
        boolean isMergeSuccessful = false;
        
    	try{
    		String fromUserId = payLoad.getField(REQUEST_FROM_USER_ID_PATH);
            String toUserId = payLoad.getField(REQUEST_TO_USER_ID_PATH);
            int iFromUserId = -1;
            int iToUserId = -1;
            
            iFromUserId = Integer.parseInt(fromUserId);
            
            iToUserId = Integer.parseInt(toUserId);
            
            
            SFUser fromUser=userAccountManager.getUser(iFromUserId);
    		SFUser toUser=userAccountManager.getUser(iToUserId);
    		
    		//check to make sure the users are in the tree
    		if(fromUser!=null && toUser!=null)
    		{				
    			// Setting the login name to display in success page
    			//setFromUserLoginName( fromUser.getUserLoginName() );
    			//setToUserLoginName( toUser.getUserLoginName() );
    			
    			userMergeService.mergeUser(iFromUserId, iToUserId);	
    			
    			SFUser userInSession = userAccountManager.getUserBean().getUser();
    			
    			if(userInSession != null){
	    			//mark the accounts so next time its pulled the data will refresh
	    			List accountListForToUser = userInSession.getAccounts();
	    			if(accountListForToUser != null){
	    				int len = accountListForToUser.size();
	    				for(int i=0;i<len;i++){
	    					SFAccount currentAccount = (SFAccount)accountListForToUser.get(i);
	    					currentAccount.setAllUsersPopulated(false);
	    				}
	    			}
    			}
    			
        		SFEvents.fireEvent(new SFUsersMerged(iFromUserId, iToUserId));
        		
        		isMergeSuccessful = true;
    		}
    		else{
    			failureReason = "UserIdsNotFoundInSession";
    			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,method + ": the given user ids are not found in the session. TO USER[" + toUserId + "], FROM USER["
    			              + fromUserId + "]");
    		}
    		
    	}
        catch(Exception e){
        	failureReason = "General-SystemUnavailable";

            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT
                    + method + "Error : ", e);
        }

        if (StringUtils.isNotEmpty(failureReason)) {
            result.put(FAILURE_REASON, failureReason);
            addError(SFAPIServiceResponseError.ERROR_MERGE_USER, failureReason);
            isMergeSuccessful = false;
            stat.stop(methStatId, false);
        }
        else{
        	stat.stop(methStatId,true);
        }

        result.put(IS_MERGE_SUCCESSFUL, isMergeSuccessful);
        
        return result;
    	
    		
    }
    

    /**
     * This method will test the username and password being entered during
     * the merge user flow
     *
     * borrowing code from SFUserConsolidationAction.handleLogin
     *
     * @param payLoad
     * @return JSONObject
     * @throws JSONException
     */
    private JSONObject processAuthenticateUserForMerge(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processAuthenticateUserForMerge - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        String failureReason = null;
        boolean isAuthenticated = false;
        String fromUserId = null;
        String fromUserFirstName = null;
        String fromUserLastName = null;
        String fromUserEmail = null;
        String fromUserPhone = null;
        String fromUserStreetAddress = null;
        String fromUserStreetAddress2 = null;
        String fromUserCity = null;
        String fromUserState = null;
        String fromUserCountry = null;
        String fromUserZip = null;
		
		String toUserId = null;
		String toUserFirstName = null;
        String toUserLastName = null;
        String toUserEmail = null;
        String toUserPhone = null;
        String toUserStreetAddress = null;
        String toUserStreetAddress2 = null;
        String toUserCity = null;
        String toUserState = null;
        String toUserCountry = null;
        String toUserZip = null;
        Integer failedLoginAttemptCountInt = null;
        String errorMessage = null;
        boolean isErrorSpecified = false;
		SFAuthenticationResult authenticationResult = null;
		SFUser fromUser = null;

        try{

        	//will treat the "to user" as the user currently in the session
        	//and the "from user" as the user that the person just entered into the UI


        	SFUser toUser=  userAccountManager.getUserBean().getUser();
        	
        	if(toUser.getUserId() == -1){
    			failureReason = "loginRequired";
    			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
    			isErrorSpecified = true;
            }
        	else if (toUser instanceof SFWholesaler) {
				//set the error message
				failureReason = "ToUserIsWholesaler";
			}
			else{
	        	String userLoginName = payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH);
	            String password = payLoad.getField(PASSWORD_PATH);

	        	boolean isValid = true;


				if ( password!=null && ( password.equals("")) && userLoginName!=null && (userLoginName.equals("")) )
				{
					isValid = false;
				}

				if(isValid){
					userLoginName=userLoginName.trim();
					password=password.trim();

					if ( userLoginName.equals("") )
					{
						isValid = false;
					}

					if ( password.equals("") )
					{
						isValid = false;
					}

					if(isValid){

						SFCredential credential=new SFCredential(userLoginName,password);
						fromUser= SFUser.create(-1);
						fromUser.setSfCredential(credential);

						fromUser.setFraudProtection(userAccountManager.getUserBean().getFraudProtection());


						//determine parent channel id, since user can only login to mt channels
						//belonging to a parent channel
						Long parentChannelId = (long)SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue();
				        if(parentChannelId != null){

							authenticationResult =
									helperManager.getUserHelper().authenticateLogin(fromUser, SFLoginTypeConstant.MANUAL, userAccountManager.getUserBean().getOriginatorId(), parentChannelId);


							if (authenticationResult != null && !authenticationResult.getIsAuthenticated()
					                && SFConfig.getInstance().getBlockCustomerLogin()) {

								failureReason = "GlobalLoginBlock";
					        }
							else{

								if ( null != authenticationResult && null != authenticationResult.getSFWholesaler() ){
									failureReason = "FromUserIsWholesaler";

								}
								else if ( authenticationResult != null && !authenticationResult.getIsAuthenticated() ) {
									failureReason = "InvalidUserNamePassword";
								}
								else{
									if (toUser.getUserId()==fromUser.getUserId()) {
										//same user can't be consolidated
										failureReason = "ToUserIsSameAsFromUser";
									}
									else{
										//TR102560 - block user consolidation for XXX if either user has XXX domains
										SFDomainHelper domainHelper = helperManager.getDomainHelper();
										if ((domainHelper.hasPendingPreRegDomains(toUser)) || domainHelper.hasPendingPreRegDomains(fromUser))
										{
											failureReason = "UserHasPendingPreRegDomain";
											SFLogger.printWarning("user has pending preRegDomain"+" From user id="+toUser.getUserId() + " To user id=" + fromUser.getUserId());
										}
										else{
											SFCredential credential2 =fromUser.getSfCredential();
											fromUser=userAccountManager.pullUser(credential2.getUserId(), credential2);
											if (fromUser!=null) {
											    //retain the credential
											    fromUser.setSfCredential(credential2);
                                                fromUser.setFraudProtection(userAccountManager.getUserBean().getFraudProtection());
												if (fromUser instanceof SFWholesaler) {
													failureReason = "FromUserIsWholesaler";
													SFLogger.printWarning("From user is wholesaler "+"id="+fromUser.getUserId());
													//errorHolder.addFormError ("SFUserConsolidationAction-ToUserIsWholesaler");
													//return postInvoke(request,response,true,"SFUserConsolidationAction");
												}
												else{
													if (fromUser.isChild() && toUser.isChild()) {
														failureReason = "ToUserIsWholesalerEndUser";
														SFLogger.printWarning("both users are wholesaler end user "+"id="+fromUser.getUserId());
														//errorHolder.addFormError ("SFUserConsolidationAction-ToUserIsEndUser");
													}
													else{

														//check if the from user has account holder relationship
														List accounts=userAccountManager.getAccounts(toUser.getUserId(),RoleEnum.PRIMARYUSER);
														if (null!=accounts) {
															//show the list of accounts where from user is account holder
															for (Iterator e=accounts.iterator();e.hasNext();) {
																SFAccount account=(SFAccount)e.next();
																userAccountManager.pullAccountWithProductLites(account.getAccountId(),toUser,false);
																//what to do in case of threshold exceeded
																//pull using filter by account holder name
																if (toUser.isAccountThresholdExceeded() || toUser.isProductsThresholdExceeded()) {
																	SFXMLSearchQuery searchQuery=new SFXMLSearchQuery();
																	searchQuery.setAccountHolderName(toUser.getFullName());
																	userAccountManager.pullAccountWithProductLites(account.getAccountId(),toUser,searchQuery,false);
																	//hard luck if limit exceeds - what are the chances
																}
															}

														}
														accounts=userAccountManager.getAccounts(fromUser.getUserId(),RoleEnum.PRIMARYUSER);
														if (null!=accounts) {
															//show the list of accounts where from user is account holder
															for (Iterator e=accounts.iterator();e.hasNext();) {
																SFAccount account=(SFAccount)e.next();
																userAccountManager.pullAccountWithProductLites(account.getAccountId(),fromUser,false);
																//what to do in case of threshold exceeded
																//pull using filter by account holder name
																if (fromUser.isAccountThresholdExceeded() || fromUser.isProductsThresholdExceeded()) {
																	SFXMLSearchQuery searchQuery=new SFXMLSearchQuery();
																	searchQuery.setAccountHolderName(fromUser.getFullName());
																	userAccountManager.pullAccountWithProductLites(account.getAccountId(),fromUser,searchQuery,false);
																	//hard luck if limit exceeds
																}
															}
														}

														fromUserId=String.valueOf(fromUser.getUserId());
														fromUserFirstName=String.valueOf(fromUser.getFirstName());
														fromUserLastName=String.valueOf(fromUser.getLastName());
														fromUserEmail=String.valueOf(fromUser.getEmail());
														fromUserPhone=String.valueOf(fromUser.getPhoneNum());
														SFAddress fromUserAddress = fromUser.getAddress();
														if(fromUserAddress != null){
															fromUserStreetAddress=String.valueOf(fromUserAddress.getStreetAddress());
															fromUserStreetAddress2=String.valueOf(fromUserAddress.getStreetAddress2());
															fromUserCity=String.valueOf(fromUserAddress.getCity());
															fromUserState=String.valueOf(fromUserAddress.getState());
															fromUserCountry=String.valueOf(fromUserAddress.getCountry());
															fromUserZip=String.valueOf(fromUserAddress.getZip());
														}
														
														
														toUserId=String.valueOf(toUser.getUserId());
														toUserFirstName=String.valueOf(toUser.getFirstName());
														toUserLastName=String.valueOf(toUser.getLastName());
														toUserEmail=String.valueOf(toUser.getEmail());
														toUserPhone=String.valueOf(toUser.getPhoneNum());
														SFAddress toUserAddress = toUser.getAddress();
														if(toUserAddress != null){
															toUserStreetAddress=String.valueOf(toUserAddress.getStreetAddress());
															toUserStreetAddress2=String.valueOf(toUserAddress.getStreetAddress2());
															toUserCity=String.valueOf(toUserAddress.getCity());
															toUserState=String.valueOf(toUserAddress.getState());
															toUserCountry=String.valueOf(toUserAddress.getCountry());
															toUserZip=String.valueOf(toUserAddress.getZip());
														}
														
														//show the user info along with accounts list where the fromUser is account holder, if any
														isAuthenticated = true;
													}
												}
											}
											else {
												//error in retrieving user info
												failureReason = "InvalidUserNamePassword";
											}

										}

								    }

								}
							}

							if (authenticationResult != null) {
								failedLoginAttemptCountInt = authenticationResult.getFailedLoginAttemptCount();
								errorMessage = authenticationResult.getErrorMessage();
							}
				        }
						else{
							failureReason = "InvalidUserNamePassword";
						}

					}
					else{
						failureReason = "InvalidUserNamePassword";
					}
				}
				else{
					failureReason = "InvalidUserNamePassword";
				}
			}
        }
        catch(Exception e){
        	failureReason = "General-SystemUnavailable";

            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT
                    + method + "Error : ",e);
        }

		if (failedLoginAttemptCountInt != null) {
			result.put(FAILED_LOGIN_ATTEMPT_COUNT, failedLoginAttemptCountInt.intValue());
			result.put(ERROR_MESSAGE, errorMessage);
		}

        if (StringUtils.isNotEmpty(failureReason)) {
            result.put(FAILURE_REASON, failureReason);
            if(!isErrorSpecified){
               addError(SFAPIServiceResponseError.ERROR_AUTHENTICATION, failureReason);
            }
            isAuthenticated = false;
            stat.stop(methStatId, false);
        }
        else{        				
        	if(isAuthenticated)
        	{
				if (authenticationResult != null
					&& SFConstant.TWO_FACTOR_AUTH_ENABLED.getDescription()
						.equalsIgnoreCase(authenticationResult.getTwoFactorAuthStatus())
					&& fromUser != null)
				{
					SFUserBean mergeUserBeanData = new SFUserBean();
					mergeUserBeanData.setUser(fromUser);
					mergeUserBeanData.setUserId(fromUser.getUserId());
					setMergeUserBeanData(mergeUserBeanData);
                    fromUser.setTwoFactorAuthStatus(authenticationResult.getTwoFactorAuthStatus());
                    fromUser.setTrustedPhoneNumber(authenticationResult.getTrustedPhoneNumber());
					SFUtils.populateTwoFactorAuthFieldsFromAuthentication(authenticationResult, mergeUserBeanData);

					// turn authenticated as false,
					// authentication will be performed during sms code validation
					isAuthenticated = false;

					String resultSMSCodeSending = sendSMSForMergeUser(payLoad);

					if (SMS_CODE_SENT.equals(resultSMSCodeSending))
					{
						result.put("failureReason", failureReason);
						result.put("twoFactorAuthStatus", authenticationResult.getTwoFactorAuthStatus());
						result.put("failedLoginAttemptCount", authenticationResult.getFailedLoginAttemptCount());
						result.put("trustedPhoneNumber", SFUtils.maskPhoneNumber(authenticationResult.getTrustedPhoneNumber()));
						result.put("sms2FACodeSent", true);
						result.put("userId", fromUser.getUserId());
						result.put("isLocked", false);
					}
					else
					{
						result.put("failureReason", resultSMSCodeSending);
						result.put("twoFactorAuthStatus", authenticationResult.getTwoFactorAuthStatus());
						result.put("failedLoginAttemptCount", authenticationResult.getFailedLoginAttemptCount());
						result.put("trustedPhoneNumber", SFUtils.maskPhoneNumber(authenticationResult.getTrustedPhoneNumber()));
						result.put("sms2FACodeSent", false);
					}
				}
			    else if (!SFStringUtil.isEmpty(fromUserId) && !SFStringUtil.isEmpty(toUserId)) {
				   //add from user
				   result.put(FROM_USER_ID, fromUserId);
				   result.put(FROM_USER_FIRST_NAME, fromUserFirstName);
				   result.put(FROM_USER_LAST_NAME, fromUserLastName);
				   result.put(FROM_USER_EMAIL, fromUserEmail);
				   result.put(FROM_USER_PHONE, fromUserPhone);
				   result.put(FROM_USER_STREET_ADDRESS, fromUserStreetAddress);
				   result.put(FROM_USER_STREET_ADDRESS_2, fromUserStreetAddress2);
				   result.put(FROM_USER_CITY, fromUserCity);
				   result.put(FROM_USER_STATE, fromUserState);
				   result.put(FROM_USER_COUNTRY, fromUserCountry);
				   result.put(FROM_USER_ZIP, fromUserZip);

				   //add to user
				   result.put(TO_USER_ID, toUserId);
				   result.put(TO_USER_FIRST_NAME, toUserFirstName);
				   result.put(TO_USER_LAST_NAME, toUserLastName);
				   result.put(TO_USER_EMAIL, toUserEmail);
				   result.put(TO_USER_PHONE, toUserPhone);
				   result.put(TO_USER_STREET_ADDRESS, toUserStreetAddress);
				   result.put(TO_USER_STREET_ADDRESS_2, toUserStreetAddress2);
				   result.put(TO_USER_CITY, toUserCity);
				   result.put(TO_USER_STATE, toUserState);
				   result.put(TO_USER_COUNTRY, toUserCountry);
				   result.put(TO_USER_ZIP, toUserZip);
			    }
        	}
        	stat.stop(methStatId, true);
        }

        result.put(AUTHENTICATED, isAuthenticated);
        
        return result;
    }

    private static final String SMS_CODE_SENT = "Sms2FACodeSent";

    private String sendSMSForMergeUser(SFJsonObject payLoad)
	{
		String result = null;
		String method = "sendSMSFor2ndUser() ";

		// call the acctmgr microservice
		try {
			JSONObject data = new JSONObject();
			String authyId = getMergeUserBeanData().getAuthyId();

			if(SFStringUtil.isEmpty(authyId)){
				throw new SFException("The authy id was empty but is required when 2FA is enabled. Cannot send sms code to trusted phone number");
			}
			else{

				data.put(AUTHY_ID, authyId);
				data.put("ipAddress", determineIpAddress());
				String sessId = getHashedSessionId();
				if(sessId == null){
					sessId = determineSessionIdWithoutHashing();
				}
				data.put("sessionId", sessId);
				int userId = getMergeUserBeanData().getUserId();
				String sUserId = Integer.toString(userId);
				data.put("originatorId", sUserId);
				data.put("personOrgId", sUserId);
				data.put("phoneNumber", getMergeUserBeanData().getUser().getTrustedPhoneNumber());

				SFLogger.printInfo(method + " - attempt to send sms code to trusted phone number.");

				String jwtToken = getMicroserviceJwtToken(payLoad);

				SFMsvcResponse responseObj;

				try {
					JSONObject requestWrapper = new JSONObject();
					JSONObject requestObj = new JSONObject();
					JSONObject requestInfo = new JSONObject();

					if (getMergeUserBeanData().getUserId() == -1) {
						throw new SFException("user info cannot be updated without a valid user login");
					}
					requestInfo.put("userId", getMergeUserBeanData().getUserId());
					requestInfo.put("userPassword", getUserPassword(getMergeUserBeanData().getUserId()));
					requestInfo.put(CLIENT_IPADDRESS, determineIpAddress());

					requestObj.put("requestInfo", requestInfo);
					requestObj.put("data", data);

					requestWrapper.put("request", requestObj);

					int personOrgId = getMergeUserBeanData().getUserId();

					StringBuilder methodNameSb = new StringBuilder();
					methodNameSb.append("sendSmsCode for 2nd user");
					methodNameSb.append(" - personOrgId("+personOrgId+")");
					methodNameSb.append(" - " + sessId);

					String formattedMethodName =  methodNameSb.toString();

				 	responseObj = SFMicroServiceUtil
							.getAcctmgrMsvcResponse(requestWrapper, formattedMethodName, "user", MS_SEND_SMSCODE_API_CALL, jwtToken);

					// check if  response from microservice is not success and add error in such case.
					if(!"success".equalsIgnoreCase(responseObj.getResponseInfo().getStatus())){
						List<String> msvcErrors = responseObj.getResponseInfo().getErrors();
						if(msvcErrors != null || !msvcErrors.isEmpty()) {
							StringBuffer errorBuf = new StringBuffer();
							for (String error : msvcErrors) {
								addError(error);
								errorBuf.append(error);
							}

							SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM, COMPONENT
									+ method + "Microservice Error : " + errorBuf.toString());
						}

					}

				} catch(Exception e) {
					SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, e.getMessage());
					throw new SFException("error occured in processing data to AcctmgrMsvc", e);
				}

				SFMsvcResponseInfo sfMsvcResponseInfo = responseObj.getResponseInfo();

				if("Success".equalsIgnoreCase(sfMsvcResponseInfo.getStatus())){

					String jsonDataOut = responseObj.getData();
					JSONObject jsonOutObj = new JSONObject(jsonDataOut);

					if (jsonOutObj.has(STATUS_ATTR)) {
						String smsStatus = jsonOutObj.getString(STATUS_ATTR);
						if ("success".equalsIgnoreCase(smsStatus)) {
							result = SMS_CODE_SENT;
							SFLogger.printInfo(method + " - sms code has been sent to trusted phone number.");
						} else {
							result = "sendSmsCodeFailed";
							SFLogger.printInfo(method + " - send SmsCode to trustedphone is failed !!");

							// log the actual error returned
							List<String> errors = sfMsvcResponseInfo.getErrors();
							StringBuffer errorBuf = new StringBuffer();
							if (errors != null) {
								for (String currentError : errors) {
									errorBuf.append(currentError);
								}
							}

							SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
									COMPONENT + method + "Error : " + errorBuf.toString());
						}
					}
					else {
						result = "There may have been a problem sending the sms code to the trusted phone number.., the status was not returned.";
					}

				}
				else{
					result = "There was an error when sending the sms code to the trusted phone number...";
				}
			}
		} catch (Exception ex) {
			result = "There was an error when sending the sms code to the trusted phone number. " + ex.getMessage();
			SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT
					+ method + "Error while sending 2fa sms code for userId ("+getMergeUserBeanData().getUserId()+"): " + ex);
		}
		return result;
	}

    /** default method **/
    private boolean validateLogInResult(SFAuthenticationResult authenticationResult, JSONObject result) throws JSONException 
    {
    	return validateLogInResult(authenticationResult, result, true);
    }
    

    /**
     * 
     * @param authenticationResult
     * @param result
     * @param isAddErrorRequired - allow calling method to control error handling
     * @return
     * @throws JSONException
     */
    private boolean validateLogInResult(SFAuthenticationResult authenticationResult, JSONObject result, boolean isAddErrorRequired) throws JSONException 
    {
        String failureReason = null;
        boolean isAuthenticated = true;
        if (authenticationResult == null) {
            failureReason = INVALID_USER_NAME_PASSWORD;
        } else if (authenticationResult != null && !authenticationResult.getIsAuthenticated()) {
            failureReason = "Error code : " + authenticationResult.getErrorCode() + ", Error Message : "
                    + authenticationResult.getErrorMessage();
            if (authenticationResult.getErrorCode() == PASSWORD_NOT_RESET) {
                failureReason = RESET_PASSWORD_NEEDED;
                userAccountManager.getUserBean().setResetPwdFlag(true);
                result.put(RESET_PASSWORD_ATTR, true);
            } else if (SFConfig.getInstance().getBlockCustomerLogin()) {
                failureReason = LOG_IN_UNAVAILABLE_MAINTENANCE;
            }
        }
        if (StringUtils.isNotEmpty(failureReason)) {
            result.put(FAILURE_REASON, failureReason);
            if(isAddErrorRequired){
               addError(SFAPIServiceResponseError.ERROR_AUTHENTICATION, failureReason);
            }
            isAuthenticated = false;
        }
        return isAuthenticated;
    }
    
    /** default method **/
    public boolean logInAndPopulateUserInfo(JSONObject result) throws JSONException {
    	return logInAndPopulateUserInfo(result, true, true);
    }

    /**
     * 
     * @param result
     * @param isAddErrorRequired - allow caller to control error handling
     * @return
     * @throws JSONException
     */
    public boolean logInAndPopulateUserInfo(JSONObject result, boolean isAddErrorRequired, boolean isSetLoggedIn) throws JSONException {
        final String method = COMPONENT + ".logInAndPopulateUserInfo - ";
        boolean isAuthenticated = false;
        try {
            SFUserBean userBean = userAccountManager.getUserBean();
            userAccountManager.querySFLoginUser(userBean, acctMgmtConfig.getMaxAccounts(),
                    acctMgmtConfig.getMaxProducts(), alertManager);
            if(isSetLoggedIn){
               userAccountManager.getUserBean().setIsLoggedIn(true);
               status.setIsAuthenticated(true);
            }
            //Reverting change in SOFT-104474
            userAccountManager.getUserBean().setIsNewCustomer(false);
            userAccountManager.getUserBean().setOriginatorId(userBean.getUserId());
            
            status.setContinueAsGuest(false);
            result.put(USER_ID_ATTR, userBean.getUser().getUserId());
            if (paidSearchCouponUtil != null) {
                result.put(NEW_USER_COUPON_REMOVED, paidSearchCouponUtil.removePaidSearchCoupons());
            }

            if (userBean.isPasswordPlusEnabled() && !userBean.getPasswordPlusInfo().getIsAuthenticated()) {
                result.put(PASSWORD_PLUS_LOGIN_REQUIRED, true);
                userBean.getPasswordPlusInfo().setOriginalLoginAction(COMPONENT);
            }

            shoppingCart.setCurrentPurchaseAccount(null);
            selectPurchaseAccount();
            loadUserPreference();
            isAuthenticated = true;
        } catch (SFFGException e) {
        	if(isAddErrorRequired){
               addError(SFAPIServiceResponseError.ERROR_AUTHENTICATION, e.getErrorMsgKey());
        	}
            result.put(FAILURE_REASON, e.getErrorMsgKey());
            SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,
                    method + " - Caught exception : " + e.getMessage(), e);
        }
        return isAuthenticated;
    }

    private JSONObject processGetLoginData(SFJsonObject payLoad) throws JSONException 
    {
        final String method = COMPONENT + ".processGetLoginData - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        result.put(USER, populateUser());
        result.put(USER_ACCOUNT_RELATIONS_ATTR,
                populateUserAccountRelationsToJson(userAccountManager.getUserBean().getUser().getUserAccountRelation()));
        populateProductProfileToJson(payLoad, result);
        populateLiteProductsJson(payLoad, result);
        stat.stop(methStatId, true);
        return result;
    }

    private JSONObject processGetCountriesList() throws SFFGException, JSONException 
    {
        final String method = COMPONENT + ".processGetCountriesList - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONArray countryList = new JSONArray();
        JSONObject result = new JSONObject();
        
        List<SFCountry> countryObjList = queryCountryList();
        
        if (countryObjList != null && !countryObjList.isEmpty()) {
            for (SFCountry country : (List<SFCountry>) countryObjList) {
                JSONObject countryObj = new JSONObject();
                countryObj.put(COUNTRY_CODE, country.getCountryCode().trim());
                countryObj.put(COUNTRY_NAME, country.getCountryName().trim());
                countryList.put(countryObj);
            }
        }

        result.put(CLIENT_COUNTRY_CODE, getCountryCodeOfClient());
        result.put(COUNTRY_LIST, countryList);
        stat.stop(methStatId, true);
        return result;
    }
    
    private List<SFCountry> queryCountryList()  throws SFFGException, JSONException {
    	List<SFCountry> countryList = null;
    	if (helperManager != null && helperManager.getUserHelper() != null){
    		countryList = helperManager.getUserHelper().getCountryCodes();
        }
    
        if(countryList == null
                || countryList.isEmpty()) {
        	
        	countryList = new ArrayList<SFCountry>();
        }
    
        return countryList;
    	
    }

	private JSONObject processValidateUKIPSTags(SFJsonObject payLoad) throws JSONException
	{
		final String method = COMPONENT + ".processValidateUKIPSTags - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);

		JSONObject result = new JSONObject();

		String tag = payLoad.getField(UK_IPS_TAG_PATH);

		boolean isValid = false;

		if (tag != null && tag.length() > 0)
		{
			ResourceBundle bundle = ResourceBundle.getBundle(UK_IPS_TAGS);
			Enumeration<String> enumeration = bundle.getKeys();
			List<String> tagList = Collections.list(enumeration);
			isValid = tagList.stream().anyMatch( t -> t.equals(tag));
		}

		result.put("valid", isValid);
		stat.stop(methStatId, true);
		return result;
	}

	private JSONObject processGetUsStates() throws JSONException
    {
        final String method = COMPONENT + ".processGetUsStates - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        JSONArray stateListArr = new JSONArray();
        ResourceBundle usStatesList = ResourceBundle.getBundle(US_STATES);
        Enumeration<String> enumeration = usStatesList.getKeys();
        List<String> usStateNameList = Collections.list(enumeration);
        Collections.sort(usStateNameList);
        for (String usStateName : usStateNameList) {
            JSONObject usState = new JSONObject();
            usState.put("stateCode", usStatesList.getString(usStateName));
            usState.put("stateName", usStateName);
            stateListArr.put(usState);
        }
        result.put(STATE_LIST, stateListArr);
        stat.stop(methStatId, true);
        return result;
    }

    private JSONObject processGetCaProvinces() throws JSONException 
    {
        final String method = COMPONENT + ".processGetCaProvinces - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        JSONArray stateListArr = new JSONArray();
        ResourceBundle caStatesList = ResourceBundle.getBundle(CA_PROVIENCES);
        Enumeration<String> enumeration = caStatesList.getKeys();
        List<String> caCodeList = Collections.list(enumeration);
        Collections.sort(caCodeList);
        for (String caCode : caCodeList) {
            JSONObject caState = new JSONObject();
            caState.put("stateCode", caCode);
            caState.put("stateName", caStatesList.getString(caCode));
            stateListArr.put(caState);
        }
        result.put(STATE_LIST, stateListArr);
        stat.stop(methStatId, true);
        return result;
    }

    private void populateLiteProductsJson(SFJsonObject payLoad, JSONObject result) throws JSONException {
        boolean retrieveLiteProducts = true;
        if (payLoad.getField(RETRIEVE_LITE_PRODUCTS_PATH) != null) {
            retrieveLiteProducts = Boolean.parseBoolean(payLoad.getField(RETRIEVE_LITE_PRODUCTS_PATH));
        }
        if (retrieveLiteProducts) {
            Collection<JSONObject> liteProducts = new ArrayList();
            List<SFAccount> accounts = userAccountManager.getUserBean().getUser().getAccounts();
            for (SFAccount sfaccount : accounts) {
                SFProductHolder productHolder = sfaccount.getProductCollection();
                List<SFSubscriptionProduct> productsList = productHolder.getProductList();
                if (!CollectionUtils.isEmpty(productsList)) {
                    for (SFSubscriptionProduct product : productsList) {
                        Map<String, Object> liteProduct = new LinkedHashMap<>();
                        if(product.getFGImplementationDetailInfo() != null) {
                            liteProduct.put(PROD_INSTANCE_ID, product.getFGImplementationDetailInfo().getFGProductInstanceId() == null ? "" : product.getFGImplementationDetailInfo().getFGProductInstanceId());
                            liteProduct.put(PARENT_PROD_INSTANCE_ID, product.getFGImplementationDetailInfo().getFGParentProductInstanceId());
                        }
                        liteProduct.put(ACCOUNT_ID_ATTR, product.getAccountId());
                        liteProduct.put(INSTANCE_NAME, product.getInstanceName());
                        liteProduct.put(DISPLAY_NAME, product.getProductDisplayName());
                        liteProduct.put(ATTRIBUTE_PRODUCT_CODE, product.getProductCode());
                        liteProduct.put(BILLABLE_TYPE, product.getBillableType());
                        liteProduct.put(IS_BILLABLE, product.getIsBillable());
                        liteProduct.put(CHANNEL_ID_ATTR, product.getChannelId());
                        liteProduct.put(STATUS_ATTR, product.getStatus());
                        liteProduct.put(CONFIGURED_STATE, product.getConfiguredState() != null ? product.getConfiguredState().getDescription() : null);
                        liteProduct.put(LIFE_CYCLE_STATE, product.getLifecycleState() != null ? product.getLifecycleState().getDescription() : null);
                        liteProduct.put(IS_ACTIVE, product.getIsActive());
                        liteProduct.put(EXPIRY_DATE, product.getExpiryDate());
                        liteProduct.put(AUTO_RENEW, product.isAutoRenewable());
                        liteProduct.put(AUTO_RENEW_TERM, product.getTerm());
                        liteProduct.put(SUBSCRIPTION_TYPE, product.getSubscriptionType());
                        liteProduct.put(ATTRIBUTES, populateLiteProductsAttributes(product));
                        liteProducts.add(new JSONObject(liteProduct));
                    }
                }
            }
            result.put(LITE_PRODUCTS, liteProducts);
        }
    }

    private Collection<JSONObject> populateLiteProductsAttributes(SFProduct product) {
        Collection<JSONObject> result = null;
        List<SFProductAttribute> attributeList = product.getProductAttributes();
        if (!CollectionUtils.isEmpty(attributeList)) {
            result = new ArrayList<>();
            for (SFProductAttribute attribute : attributeList) {
                Map<String, Object> productLiteAttributesMap = new LinkedHashMap<>();
                productLiteAttributesMap.put(ATTRIBUTE_ATTRIBUTE_CODE, attribute.getAttrCode());
                productLiteAttributesMap.put(ATTRIBUTE_ATTRIBUTE_ID, attribute.getAttrId());
                productLiteAttributesMap.put(ATTRIBUTE_UNIQUE_ID, attribute.getAttrUniqueId());
                productLiteAttributesMap.put(ATTRIBUTE_CHARGE_STATE, attribute.getChargeState());
                productLiteAttributesMap.put(ATTRIBUTE_DATA_TYPE, attribute.getDataType());
                productLiteAttributesMap.put(DEFAULT_QTY, attribute.getDefaultQty());
                productLiteAttributesMap.put(ATTRIBUTE_DEFERRED_CHARGE, attribute.getIsDeferredCharge());
                productLiteAttributesMap.put(ATTRIBUTE_DESCRIPTION, attribute.getDescription());
                productLiteAttributesMap.put(SFCoreAPIConstants.DISPLAY_TEXT, attribute.getDisplayText());
                productLiteAttributesMap.put(ATTRIBUTE_EXTERNAL_ID, attribute.getExternalId());
                productLiteAttributesMap.put(SFCoreAPIConstants.IS_BILLABLE, attribute.getIsBillable());
                productLiteAttributesMap.put(IS_MANDATORY, attribute.getMandatory());
                productLiteAttributesMap.put(MAX_QTY, attribute.getMaxQty());
                productLiteAttributesMap.put(SFCoreAPIConstants.NAME, attribute.getName());
                productLiteAttributesMap.put(SFCoreAPIConstants.PRICE, attribute.getPrice());
                productLiteAttributesMap.put(ATTRIBUTE_REGULAR_PRICE, attribute.getRegularPrice());
                productLiteAttributesMap.put(UOM, attribute.getUom());
                productLiteAttributesMap.put(VALUE, attribute.getValue());
                result.add(new JSONObject(productLiteAttributesMap));
            }
        }
        return result;
    }

    private void populateProductProfileToJson(SFJsonObject payLoad, JSONObject result) throws JSONException {
        boolean retrieveProductProfile = true;
        if (payLoad.getField(RETRIEVE_PRODUCT_PROFILE_PATH) != null) {
            retrieveProductProfile = Boolean.parseBoolean(payLoad.getField(RETRIEVE_PRODUCT_PROFILE_PATH));
        }
        if (retrieveProductProfile) {
            Map<String, Object> productProfileData = new LinkedHashMap<>();
            SFUser sfUser = userAccountManager.getUserBean().getUser();
            if (sfUser.getUserProductProfile() != null && sfUser.getUserProductProfile().getProductSummary() != null) {
                SFProductSummary summary = sfUser.getUserProductProfile().getProductSummary();
                productProfileData.put(COUNT_ALACARTE_EMAIL, summary.getAlacartEmailCount());
                productProfileData.put(COUNT_BUSINESS_PROFILE, summary.getBizProfileCount());
                productProfileData.put(COUNT_BUSINESS_QUICK_PROFILE, summary.getBusinessProfileCount());
                productProfileData.put(COUNT_BUSINESS_SPACE, summary.getBusinessSpaceCount());
                productProfileData.put(COUNT_CATCH_ALL, summary.getCatchAllCount());
                productProfileData.put(COUNT_CERT, summary.getCertCount());
                productProfileData.put(COUNT_CODE_GUARD, summary.getCodeGuardCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_BUSINESS_EMAIL, summary.getConfiguredAlacarteBusinessEmailCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_CATCH_ALL, summary.getConfiguredAlacarteCatchAllCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_EMAIL, summary.getConfiguredAlacartEmailCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_PERSONAL_EMAIL, summary.getConfiguredAlacartePersonalEmailCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_PROFESSIONAL_EMAIL, summary.getConfiguredAlacarteProfessionalEmailCount());
                productProfileData.put(COUNT_CONFIGURED_ALACARTE_PROFESSIONAL_EMAIL_PLUS, summary.getConfiguredAlacarteProfessionalEmailPlusCount());
                productProfileData.put(COUNT_CONFIGURED_DOMAIN_PACKAGE, summary.getConfiguredDomainPkgEmailCount());
                productProfileData.put(COUNT_CONFIGURED_ECOMMERCE_EMAIL, summary.getConfiguredECommerceEmailCount());
                productProfileData.put(COUNT_CONFIGURED_FREE_WEBSITE, summary.getConfiguredFreeWebsiteEmailCount());
                productProfileData.put(COUNT_CONFIGURED_HOSTING_CATCH_ALL, summary.getConfiguredHostingCatchAllCount());
                productProfileData.put(COUNT_CONFIGURED_HOSTING_EMAIL, summary.getConfiguredHostingEmailCount());
                productProfileData.put(COUNT_CONFIGURED_WEBSITE_EMAIL, summary.getConfiguredWebSitesEmailCount());
                productProfileData.put(COUNT_CONFIGURED_MESSAGE_GUARD, summary.getConfinguredMessageGuardCount());
                productProfileData.put(COUNT_DESIGN_SERVICE, summary.getDesignServiceCount());
                productProfileData.put(COUNT_DESIGN, summary.getDifmDesignCount());
                productProfileData.put(COUNT_DIFM_HOSTING, summary.getDifmHostingCount());
                productProfileData.put(COUNT_DOMAIN, summary.getDomainCount());
                productProfileData.put(COUNT_ECOMMERCE, summary.getECommerceCount());
                productProfileData.put(COUNT_FILE_SHARING, summary.getFileSharingCount());
                productProfileData.put(COUNT_CONFIGURED_FREE_EMAIL, summary.getConfiguredFreeWebsiteEmailCount());
                productProfileData.put(COUNT_GO_MOBI, summary.getGoMobiCount());
                productProfileData.put(COUNT_HOSTED_EXCHANGE, summary.getHostedExchangeCount());
                productProfileData.put(COUNT_HOST, summary.getHostingCount());
                productProfileData.put(COUNT_HOSTING_EMAIL, summary.getHostingEmailCount());
                productProfileData.put(COUNT_IC_ENABLED_SITES, summary.getIcEnabledWebsiteCount());
                productProfileData.put(COUNT_IC_PUBLISHED_SITES, summary.getIcPublishedWebsiteCount());
                productProfileData.put(COUNT_MESSAGE_GUARD, summary.getMessageGuardCount());
                productProfileData.put(COUNT_MYTIME_SUPPORT, summary.getMyTimeSupportCount());
                productProfileData.put(COUNT_OLM_BUNDLE, summary.getQuickProfileLocalCount());
                productProfileData.put(COUNT_PERFORMANCE_CLICKS, summary.getPerformanceClicksCount());
                productProfileData.put(COUNT_QUICK_PROFILE_LOCAL, summary.getQuickProfileLocalCount());
                productProfileData.put(COUNT_SEAL, summary.getSealCount());
                productProfileData.put(COUNT_SECURITY_PRODUCTS, summary.getSecurityProductsCount());
                productProfileData.put(COUNT_SEO_OPTIMIZER, summary.getSeoOptimizerCount());
                productProfileData.put(COUNT_SEO_PACKAGE, summary.getSeoPkgCount());
                productProfileData.put(COUNT_SHAREPOINT_HOSTING, summary.getSharePointHostingCount());
                productProfileData.put(COUNT_SITELOCK, summary.getSiteLockCount());
                productProfileData.put(COUNT_TOTAL_ALACARTE_BUSINESS_EMAIL, summary.getTotalAlacarteBusinessEmailCount());
                productProfileData.put(COUNT_ALACARTE_CATCH_ALL, summary.getTotalAlacarteCatchAllCount());
                productProfileData.put(COUNT_TOTAL_PERSONAL_EMAIL, summary.getTotalAlacartePersonalEmailCount());
                productProfileData.put(COUNT_TOTAL_ALACARTE_PROFESSIONAL_EMAIL, summary.getTotalAlacarteProfessionalEmailCount());
                productProfileData.put(COUNT_TOTAL_ALACARTE_PROFESSIONAL_PLUS_EMAIL, summary.getTotalAlacarteProfessionalEmailPlusCount());
                productProfileData.put(COUNT_TOTAL_CONFIGURED_EMAIL, summary.getTotalConfiguredEmailCount());
                productProfileData.put(COUNT_TOTAL_DOMAIN_PACKAGE, summary.getTotalDomainPkgEmailCount());
                productProfileData.put(COUNT_TOTAL_ECOMMERCE_EMAIL, summary.getTotalEcommerceEmailCount());
                productProfileData.put(COUNT_TOTAL_FREE_EMAIL, summary.getTotalFreeWebsiteEmailCount());
                productProfileData.put(COUNT_TOTAL_HOSTING_CATCH_ALL, summary.getTotalHostingCatchAllCount());
                productProfileData.put(COUNT_TOTAL_HOSTING_EMAIL, summary.getTotalHostingEmailCount());
                productProfileData.put(COUNT_TOTAL_MESSAGE_GUARD, summary.getTotalMessageGuardCount());
                productProfileData.put(COUNT_TOTAL_WEBSITES, summary.getTotalWebsiteCount());
                productProfileData.put(COUNT_TOTAL_WEBSITE_EMAIL, summary.getTotalWebSiteEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_ALACARTE_BUSINESS_EMAIL, summary.getUnconfiguredAlacarteBusinessEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_ALACARTE_CATCH_ALL, summary.getUnconfiguredAlacarteCatchAllCount());
                productProfileData.put(COUNT_UNCONFIGURED_ALACARTE_PERSONAL_EMAIL, summary.getUnconfiguredAlacartePersonalEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_ALACARTE_PROFESSIONAL_EMAIL, summary.getUnconfiguredAlacarteProfessionalEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_PROFESSIONAL_PLUS_EMAIL, summary.getUnconfiguredAlacarteProfessionalEmailPlusCount());
                productProfileData.put(COUNT_UNCONFIGURED_DOMAIN_PACKAGE, summary.getUnconfiguredDomainPkgEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_ECOMMERCE_EMAIL, summary.getUnconfiguredECommerceEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_FREE_WEBSITE_EMAIL, summary.getUnconfiguredFreeWebsiteEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_HOSTING_CATCH_ALL, summary.getUnconfiguredHostingCatchAllCount());
                productProfileData.put(COUNT_UNCONFIGURED_HOSTING_EMAIL, summary.getUnconfiguredHostingEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_HOSTING_PACKAGE_EMAIL, summary.getunconfiguredHostingPkgEmailCount());
                productProfileData.put(COUNT_UNCONFIGURED_MESSAGE_GUARD, summary.getUnconfiguredMessageGuardCount());
                productProfileData.put(COUNT_UNCONFIGURED_WEBSITE_EMAIL, summary.getUnconfiguredWebSiteEmailCount());
                productProfileData.put(COUNT_VPS, summary.getVpsCount());
                productProfileData.put(COUNT_WATCHDOG, summary.getWatchDogCount());
                productProfileData.put(COUNT_WEB_PRO, summary.getWebProCount());
                productProfileData.put(COUNT_WEBSITE, summary.getWebsiteCount());
                productProfileData.put(COUNT_WORDPRESS, summary.getWordPressHostingCount());
                result.put(PRODUCT_PROFILE, productProfileData);
            }
        }
    }

    private Map<String, Object> populateUser() throws JSONException {
        Map<String, Object> userDetails = new LinkedHashMap<>();
        SFUser user = userAccountManager.getUserBean().getUser();
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_TENANT, SFMTChannelConstants.DEFAULT_NSI_PARENT_CHANNEL_NAME);
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_CHANNELID, SFMTChannelConstants.DEFAULT_NSI_MT_CHANNEL_ID);
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_PARENTCHANNELID, SFUtils.getParentChannelId(SFMTChannelConstants.DEFAULT_NSI_PARENT_CHANNEL_NAME));
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_TYPE, user.getCustomerType());
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS, populateUserAddress());
        userDetails.put(SFESBConstant.TARGET_TYPE_PHONE, user.getPhoneNum());
        userDetails.put(SFESBConstant.TARGET_TYPE_EMAIL, user.getEmail());
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_IS_NEW_CUSTOMER, user.isNewCustomer());
        userDetails.put(SFESBConstant.RESPONSE_MAP_KEY_SEGMENT, user.getSegmentId());
        return userDetails;
    }

    private Object populateUserAddress() throws JSONException {
        Map<String, Object> addressMap = new LinkedHashMap<>();
        SFUser user = userAccountManager.getUserBean().getUser();
        addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, user.getStreetAddress());
        addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, user.getStreetAddress2());
        addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, user.getCity());
        addressMap.put(STATE_PROV, user.getState());
        addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, user.getCountry());
        addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, user.getZip());
        return addressMap;
    }

    private JSONObject processGetCustomerName() throws JSONException {
        JSONObject result = new JSONObject();
        SFUserBean userBean = userAccountManager.getUserBean();
        result.put(FIRST_NAME_ATTR, userBean.getFirstName());
        result.put(LAST_NAME_ATTR, userBean.getLastName());
        if (userBean.getIsLoggedIn() || (userBean.getIsNewCustomer() && userBean.getUserId() != ZERO)) {
            result.put(HASHED_ID, SFUtils.generateHashedValue(String.valueOf(userBean.getUserId())));
        }
        return result;
    }

    private void processCreateUser(SFJsonObject payLoad) {
        final String method = COMPONENT + ".processCreateUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        SFUserBean userBean = userAccountManager.getUserBean();
        if (!userBean.getIsLoggedIn()) {
            userBean.clear();
            shoppingCart.setCurrentPurchaseAccount(null);
            status.setContinueAsGuest(false);
            createAccountAndNewUser(userBean, shoppingCart);
            userBean.setFirstName(payLoad.getField(NEW_USER_FIRST_NAME_PATH));
            userBean.setLastName(payLoad.getField(NEW_USER_LAST_NAME_PATH));
            String email = payLoad.getField(NEW_USER_EMAIL_PATH);
            String userName = payLoad.getField(NEW_USER_LOGIN_NAME_PATH);
            // Set email-id as user id if available else system generated or provided value
            if (StringUtils.isEmpty(userName) && isUserIdAvailable(email)) {
                userName = email;
            }
            userBean.setUserLoginName(userName);
            userBean.getUser().setEmail(email);
            userBean.setPassword(payLoad.getField(NEW_USER_PASSWORD_PATH));
            userBean.setConfirmPassword(payLoad.getField(NEW_USER_PASSWORD_PATH)+EMPTY);
            userBean.setEmailNotify(Boolean.parseBoolean(payLoad.getField(NEW_USER_NOTIFY_EMAIL_PATH)));
            userBean.getUser().setParentChannelId((long) SFConstant.DEFAULT_PARENT_CHANNEL_ID.getValue());
            try {
                userBean.setOriginatorId(helperManager.getDataRepository().getOriginatorId());
                sessionTrackingInfo.setCurrentPage(CREATE_USER);
            } catch (SFFGException e) {
                SFLogger.printError( LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
					"Failed to get OriginatorId from FG", e);
				addError(SFAPIServiceResponseError.ERROR_USER_CREATION, "Failed to get OriginatorId from FG: " + e.getMessage());
            }
        } else {
            addError(SFAPIServiceResponseError.ERROR_USER_CREATION, "User already logged in, please log out and try again.");
        }
        stat.stop(methStatId, true);
    }

    public static void createAccountAndNewUser(SFUserBean userBean, SFShoppingCart shoppingCart, SFAccountTypeConstant accountType) {
        if (shoppingCart.getCurrentPurchaseAccount() == null) {
            if (!userBean.getIsLoggedIn()) {
                userBean.setIsNewCustomer(true);
                userBean.setUserId(ZERO);
            }
            SFAccount purchaseAccount = new SFAccount(ZERO);
            purchaseAccount.setAccountType(accountType);
            purchaseAccount.setNewAccount(true);
            purchaseAccount.setAccountHolder(userBean.getUser());
            shoppingCart.setCurrentPurchaseAccount(purchaseAccount);
        }
    }

    public static void createAccountAndNewUser(SFUserBean userBean, SFShoppingCart shoppingCart) {
        createAccountAndNewUser(userBean, shoppingCart, SFAccountTypeConstant.INDIVIDUAL);
    }

    private void processDomRegUserDetails(SFJsonObject payLoad) {
        SFUserBean userBean = userAccountManager.getUserBean();
        userBean.getUser().setFirstName(payLoad.getField(DOM_REG_USER_FIRST_NAME_PATH));
        userBean.getUser().setLastName(payLoad.getField(DOM_REG_USER_LAST_NAME_PATH));
        userBean.getUser().setEmail(payLoad.getField(DOM_REG_USER_EMAIL_PATH));
        userBean.getUser().setPhoneNum(payLoad.getField(DOM_REG_USER_PHONE_PATH));
        populateAddressToSFUser(userBean.getUser(), payLoad.getNode(DOM_REG_USER_ADDRESS_NODE_PATH));
        sessionTrackingInfo.setCurrentPage(DOMAIN_REG);
    }


    private JSONObject processSelectAccount(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processSelectAccount - ";
        Integer accountId = Integer.parseInt(payLoad.getField(ACCOUNT_ID_PATH));
        JSONObject result = null;
        SFAccount selectedAccount = selectPurchaseAccount(accountId);
        if (selectedAccount != null) {
            result = new JSONObject();
            try {
                queryAccountNames(userAccountManager.getUserBean().getUser(), accountId);
                result.put(SELECTED_ACCOUNT, populateAccount(selectedAccount));
            } catch (SFFGException e) {
                addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_FETCH_DATA_FROM_FG, e.getMessage());
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                        " error while querying data from FG", e);
            }
        }
        return result;
    }

    private SFAccount selectPurchaseAccount() {
        return selectPurchaseAccount(getPurchasableAccounts(userAccountManager.getUserBean()));
    }

    private SFAccount selectPurchaseAccount(Vector<SFAccount> purchasableAccounts) {
        SFAccount account = null;
        if (purchasableAccounts != null
                        && purchasableAccounts.size() == ONE) {
            account = selectPurchaseAccount((purchasableAccounts.get(ZERO)).getAccountId());
        }
        return account;
    }

    private SFAccount selectPurchaseAccount(Integer selectedAcctId) 
    {
        final String method = COMPONENT + ".selectPurchaseAccount - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        SFAccount selectedAccount = null;

        try {
            SFAccount pulledAccount = pullAccount(selectedAcctId);
            List users = userAccountManager.getUsers(pulledAccount.getAccountId(), RoleEnum.ACCOUNTHOLDER);
            // there should be only one user in the vector
            if (users == null || users.size() < ONE) {
                // there should be an account holder
                addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_SELECT_ACCOUNT, "No account holder found for account " + pulledAccount.getAccountId());
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + ": No account holder found for account " + pulledAccount.getAccountId());
            } else {
                SFUser accountHolder = (SFUser) users.get(ZERO);

                SFUser newAcctHolder = userAccountManager.pullUserWithoutRelations(accountHolder.getUserId(),
                        helperManager.getDataRepository().getSfSystemCredential());

                if (pulledAccount.getAccountChannelType() == SFAccountChannelConstant.ESE && (!userAccountManager.getUserBean().getUser().isCSRRep())) {
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + ": Selected ESE account " + pulledAccount.getAccountId());
                    addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_SELECT_ACCOUNT, "Selected ESE account" + pulledAccount.getAccountId());
                }

                pulledAccount.setAccountHolder(newAcctHolder);
                shoppingCart.setCurrentPurchaseAccount(pulledAccount);
                selectedAccount = pulledAccount;
            }

        } catch (SFFGException fge) {
            addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_SELECT_ACCOUNT, "Error while fetching data from FG: " + +selectedAcctId);
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + ": Error while fetching data from FG"
                    + selectedAcctId, fge);
        }
        stat.stop(methStatId, true);
        return selectedAccount;
    }

    private JSONObject processValidateAddress(SFJsonObject payLoad) throws JSONException 
    {
        final String method = COMPONENT + ".processValidateAddress - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        SFESBCommonService esbService = helperManager.getEsbCommonService();
        JSONObject result = new JSONObject();
        SFPerson sfUser = new SFPerson();
        populateAddressToSFUser(sfUser, payLoad.getNode(ADDRESS_PATH));
        Map<String, Object> resultMap = esbService.validateAddress(sfUser);
        if (resultMap != null) {
            if (!resultMap.containsKey(SFESBConstant.ERROR_TYPE_VALIDATION)) {
                result.put(VALID_ATTR, true);
                if (resultMap.containsKey(SFESBConstant.RESPONSE_MAP_KEY_CONFIDENCE)) {
                    result.put(CONFIDENCE_USA, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_CONFIDENCE));
                }
                Map<String, Object> addressMap = new LinkedHashMap<>();
                addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1));
                addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2));
                addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_CITY));
                addressMap.put(STATE_PROV, resultMap.get(SFESBConstant.RESPONSE_MAP_STATE_OR_PROVINCE));
                addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY));
                addressMap.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, resultMap.get(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE));

                result.put(ADDRESS_ATTR, new JSONObject(addressMap));

            } else {
                addError(SFAPIServiceResponseError.ERROR_INVALID_ADDRESS, resultMap.get(SFESBConstant.ERROR_TYPE_VALIDATION));
            }
        }
        stat.stop(methStatId, true);
        return result;
    }

    private JSONObject processCreateAccount(SFJsonObject payLoad) throws JSONException, SFInvalidDataException 
    {
        final String method = COMPONENT + ".processCreateAccount - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        try {
            int accountId = (int)helperManager.getUserHelper().createAccount(populateSFAccount(payLoad), userAccountManager.getUserBean().getUser());
            selectPurchaseAccount(accountId);
            result.put(ACCOUNT_ID_ATTR, accountId);
        } catch (SFFGException e) {
            addError(SFAPIServiceResponseError.ERROR_ACCOUNT_CREATION, e.getMessage());
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                    " error while querying data from FG", e);
        }
        stat.stop(methStatId, true);
        return result;
    }

    private JSONObject processIsUserIdAvailable(SFJsonObject payLoad) throws JSONException 
    {
        final String method = COMPONENT + ".processIsUserIdAvailable - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        String userLoginName = payLoad.getField(REQUEST_USER_LOGIN_NAME_PATH);
        JSONObject result = new JSONObject();
        result.put(AVAILABLE_ATTR, isUserIdAvailable(userLoginName));
        stat.stop(methStatId, true);
        return result;
    }

    private boolean isUserIdAvailable(String userLoginName) {
        final String method = COMPONENT + ".isUserIdAvailable - ";
        boolean isAvailable = false;
        try {
            isAvailable = helperManager.getUserHelper().isLoginNameAvailable(userLoginName);
        } catch (SFFGException e) {
            addWarning(SFAPIServiceResponseError.ERROR_UNABLE_TO_FETCH_DATA_FROM_FG, e.getMessage());
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                    " error while querying data from FG", e);
        }
        return isAvailable;
    }

    private JSONObject processHasMultipleAccounts() throws JSONException {
        JSONObject result = new JSONObject();
        SFUser user = userAccountManager.getUserBean().getUser();
        boolean multipleAccounts = false;
        if (user != null && user.getAccounts() != null && user.getAccounts().size() > 1) {
            multipleAccounts = true;
        }
        result.put(HAS_MULTIPLE_ACCOUNTS_ATTR, multipleAccounts);
        return result;
    }

    /**
     * Processes and get accounts method and returns accounts in response
     *
     * @return the accounts response
     * @throws JSONException the {@link JSONException}
     */
    private JSONObject processGetAccounts() throws JSONException 
    {
        final String method = COMPONENT + ".processGetAccounts - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();
        try {
            queryAccountNames(userAccountManager.getUserBean().getUser(), null);
            Vector<SFAccount> purchasableAccounts = getPurchasableAccounts(userAccountManager.getUserBean());
            selectPurchaseAccount(purchasableAccounts);
            populateAccounts(purchasableAccounts, result);
        } catch (SFFGException e) {
            addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_FETCH_DATA_FROM_FG, e.getMessage());
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                    " error while querying data from FG", e);
        }
        stat.stop(methStatId, true);
        return result;
    }

    // This api is used to get account lists for AM Home Page usage.
    private JSONObject processGetAccountsListLite(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processGetAccountsListLite - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        boolean isErrorSpecified = false;

        JSONObject result = new JSONObject();
        int startIndex = payLoad.getNumericField(START_INDEX_PATH) != null ? payLoad.getNumericField(START_INDEX_PATH)
                : ZERO;
        int requestedSize = payLoad.getNumericField(REQUESTED_SIZE_PATH) != null
                ? payLoad.getNumericField(REQUESTED_SIZE_PATH) : FIFTY;
        SFUser user = userAccountManager.getUserBean().getUser();
        String failureReason = null;
        if (user.getUserId() == -1) {
            failureReason = "loginRequired";
            addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
            isErrorSpecified = true;
        } else {
            try {


                if (user.isAccountThresholdExceeded() || user.isProductsThresholdExceeded()) {
                    // Large user
                    SFSortDirection direction = SFSortDirection.ASCE;
                    String sortCriteria = payLoad.getField(REQUESTED_SORT_CRITERIA) != null
                            ? payLoad.getField(REQUESTED_SORT_CRITERIA) : SORT_BY_ACCOUNT_NAME;
                    int sortName = SortType.BY_ACCOUNT_NAME.getCode();

                    SFFGQueryResult<SFUserAccount> queryResult = helperManager.getUserHelper()
                            .queryAccountsForUser(user.getUserId(), startIndex, requestedSize, sortName, direction);
                    Collection<JSONObject> accounts = populateAccounts(queryResult.getResults());
                    result.put(ACCOUNTS_ATTR, accounts);
                    result.put("length", queryResult.getTotalRowsAvailable());
                    result.put("startIndex", startIndex);
                    result.put("requestedSize", requestedSize);
                } else { // small user
                    populateAccountsListLite(user.getUserId(), user.getAccounts(), result, startIndex, requestedSize);
                    result.put("startIndex", startIndex);
                    result.put("requestedSize", requestedSize);
                    result.put("requestedSize", requestedSize);
                }
            } catch (SFFGException e) {
                failureReason = e.getMessage();
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                        method + " error while querying data from FG", e);
            } catch (Exception excep) {
                failureReason = excep.getMessage();
                SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: ", excep);
            }
        }
        if (StringUtils.isNotEmpty(failureReason)) {
            result.put(FAILURE_REASON, failureReason);
            if (!isErrorSpecified) {
                addError(SFAPIServiceResponseError.ERROR_GET_ACCOUNTS, failureReason);
            }
            stat.stop(methStatId, false);
        } else {
            stat.stop(methStatId, true);
        }

        return result;
    }

    private void populateAccountsListLite(int userId, List<SFAccount> sfAccounts, JSONObject result, int startIndex, int requestedSize) throws JSONException, SFException {
        Collection<JSONObject> accounts = new ArrayList<>();
        int size = 0;

        if (sfAccounts.isEmpty()) {
            SFLogger.printInfo("populateAccountsListLite: " + " accountsList is empty ");
        }
        if (sfAccounts != null && !sfAccounts.isEmpty()) {
            size = sfAccounts.size();
            if (size <= requestedSize) {
                requestedSize = size;
            }
            if (startIndex < 0) {
                startIndex = 0;
            }
            if (startIndex > size) {
                startIndex = size;
            }
            int requestedCount = startIndex + requestedSize;
            if (requestedCount > size) {
                requestedCount = size;
            }

            for (int i = startIndex; i < requestedCount; i++) {
                SFAccount account = sfAccounts.get(i);
                if (null != account) {
                    account = pullAccount(account.getAccountId());
                    if (null != account) {
                        Map<String, Object> accountDetails = new LinkedHashMap<>();
                        accountDetails.put(ACCOUNT_ID_ATTR, account.getAccountId());
                        if (account.getAccountName() != null) {
                            accountDetails.put(ACCOUNT_NAME_ATTR, account.getAccountName());
                        } else {
                            accountDetails.put(ACCOUNT_NAME_ATTR, EMPTY);
                        }
                        if (account.getAccountHolder() != null) {
                            accountDetails.put(ACCOUNT_HOLDER_NAME, account.getAccountHolder().getFullName());
                        } else {
                            accountDetails.put(ACCOUNT_HOLDER_NAME, EMPTY);
                        }
                        accounts.add(new JSONObject(accountDetails));
                    }
                }
            }
        }
        result.put(ACCOUNTS_ATTR, accounts);
        result.put("length", size);
    }

    private JSONObject processGetAccountsList(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".processGetAccountsList - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		
		boolean isErrorSpecified = false;

		JSONObject result = new JSONObject();
		int startIndex = payLoad.getNumericField(START_INDEX_PATH) != null ? payLoad.getNumericField(START_INDEX_PATH)
				: ZERO;
		int requestedSize = payLoad.getNumericField(REQUESTED_SIZE_PATH) != null
				? payLoad.getNumericField(REQUESTED_SIZE_PATH) : FIFTY;
		String sortCriteria = payLoad.getField(REQUESTED_SORT_CRITERIA) != null
				? payLoad.getField(REQUESTED_SORT_CRITERIA) : SORT_BY_ACCOUNT_NAME;
		String sortDirection = payLoad.getField(REQUESTED_SORT_DIRECTION) != null
				? payLoad.getField(REQUESTED_SORT_DIRECTION) : SORT_DIRECTION_DEFAULT;
		SFUser user = userAccountManager.getUserBean().getUser();
		String failureReason = null;
		if (user.getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
		} else {
			try {

				SFSortDirection direction = SFSortDirection.ASCE;
				if (SFSortDirection.DESC.name().equalsIgnoreCase(sortDirection)) {
					direction = SFSortDirection.DESC;
				}
				if (SFSortDirection.ASCE.name().equalsIgnoreCase(sortDirection)) {
					direction = SFSortDirection.ASCE;
				}
				int sortName = 0;
				if (user.isAccountThresholdExceeded() || user.isProductsThresholdExceeded()) {
					// Large user
					if (SORT_BY_ACCOUNT_HOLDER_NAME.equalsIgnoreCase(sortCriteria)) {
						sortName = SortType.BY_ACCOUNT_HOLDER_NAME.getCode();
					}
					if (SORT_BY_ACCOUNT_NAME.equalsIgnoreCase(sortCriteria)) {
						sortName = SortType.BY_ACCOUNT_NAME.getCode();
					}
					if (SORT_BY_USER_ROLE_NAME.equalsIgnoreCase(sortCriteria)) {
						sortName = SortType.BY_RELATIONSHIP_NAME.getCode();
					}
					if (SORT_BY_NO_OF_PRODUCTS.equalsIgnoreCase(sortCriteria)) {
						sortName = SortType.BY_PRODUCT_COUNT.getCode();
					}
					SFFGQueryResult<SFUserAccount> queryResult = helperManager.getUserHelper()
							.queryAccountsForUser(user.getUserId(), startIndex, requestedSize, sortName, direction);
					Collection<JSONObject> accounts = populateAccounts(queryResult.getResults());
					result.put(ACCOUNTS_ATTR, accounts);
					result.put("length", queryResult.getTotalRowsAvailable());
					result.put("startIndex", startIndex);
					result.put("requestedSize", requestedSize);
				} else { // small user

					queryAccountNames(userAccountManager.getUserBean().getUser(), null);
					List<SFAccount> accounts = user.getAccounts();
					populateAccounts(user.getUserId(), accounts, result, startIndex, requestedSize,
							sortCriteria, direction);
					result.put("startIndex", startIndex);
					result.put("requestedSize", requestedSize);
				}
			} catch (SFFGException e) {
				failureReason = e.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
						method + " error while querying data from FG", e);
			} catch (Exception excep) {
				failureReason = excep.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: ", excep);
			}
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_GET_ACCOUNTS, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		return result;
	}

	 //large user logic
	 private Collection<JSONObject> populateAccounts(List<SFUserAccount> sfUserAccounts) throws SFException{
    	Collection<JSONObject> accounts = new ArrayList<>();
    	
    	for(SFUserAccount account: sfUserAccounts ){
            Map<String, Object> accountDetails = new LinkedHashMap<>();
            
            if (account != null) {
            	if(account.getRelationshipName().equalsIgnoreCase("BILLING_ADDRESS")){
            		SFLogger.printInfo("Billing contact is not allowed");
            	}else{
	            	if (account.getAccountName() != null) {
	            		accountDetails.put(ACCOUNT_NAME_ATTR,account.getAccountName());
	            	}else {
	            		accountDetails.put(ACCOUNT_NAME_ATTR,EMPTY);
	            	}
	                accountDetails.put(ACCOUNT_ID_ATTR, account.getAccountId());
	                if (account.getAccountHolderName() != null) {
	                    accountDetails.put(ACCOUNT_HOLDER_NAME, account.getAccountHolderName());
	                }else{
	                	  accountDetails.put(ACCOUNT_HOLDER_NAME, EMPTY);
	                }
	                if (account.getAccountHolderEmail() != null) {
	                	accountDetails.put(EMAIL_ATTR, account.getAccountHolderEmail());
	                }
	                else{
	                	accountDetails.put(EMAIL_ATTR, EMPTY);
	                }
	                accountDetails.put(PRODUCTS_COUNT,account.getProductCount() ); 
	                
	                if (account.getRelationshipName() != null) {
	                	String result = EMPTY;
	                	if (account.getRelationshipName().equalsIgnoreCase(RelationshipTypeCode.ACCOUNTHOLDER.getDescription())) {
	                        result = ACCOUNTHOLDER_DISPLAYNAME;
	                    } else if (account.getRelationshipName().equalsIgnoreCase(RelationshipTypeCode.ACCOUNT_ADMINISTRATOR.getDescription())) {
	                        result = ADMINCONTACT_DISPLAYNAME;
	                    } else if (account.getRelationshipName().equalsIgnoreCase(RelationshipTypeCode.PRIMARYUSER.getDescription())) {
	                        result = PRIMARYUSER_DISPLAYNAME;                        
	                    } else if (account.getRelationshipName().equalsIgnoreCase(RelationshipTypeCode.PRODUCT_CONFIGURATOR.getDescription())) {
	                        result = TECHCONTACT_DISPLAYNAME;
	                    } else if (account.getRelationshipName().equalsIgnoreCase(RelationshipTypeCode.TECHCONTACT.getDescription())) {
                            result = TECHCONTACT_DISPLAYNAME;
                        } /*else if (account.getRelationshipName().equalsIgnoreCase("BILLING_ADDRESS")) {
	                        result = BILLINGCONTACT_DISPLAYNAME;
	                    }*/
	
	                	accountDetails.put(ROLE_ATTR, result);
	                	
	                }
            	
            	
                else{
                	accountDetails.put(ROLE_ATTR, EMPTY);
                }
                
                accounts.add(new JSONObject(accountDetails));
            	}
            } else {
                throw new SFException("account is null");
            }
           
        }
		return accounts;
	}

	/**
	 * return only the default payment method for the account
	 *
	 * @param payLoad
	 * @return
	 * @throws JSONException
	 */
	public JSONObject processGetDefaultPaymentMethodForAccount(SFJsonObject payLoad) throws JSONException
	{
		return processGetPaymentsForAccount(payLoad, true);
	}

	/**
	 * this is the default implementation, select all payment methods
	 *
     * Has the responsibility of implementing the method getPaymentsForAccount
     *
     * @param payLoad JSON request
     * @return JSON response
     * @throws JSONException
     */
	public JSONObject processGetPaymentsForAccount(SFJsonObject payLoad) throws JSONException
	{
		return processGetPaymentsForAccount(payLoad, false);
	}


	/**
     * Has the responsibility of implementing the method getPaymentsForAccount
     *
     * @param payLoad JSON request
     * @return JSON response
     * @throws JSONException
     */
    public JSONObject processGetPaymentsForAccount(SFJsonObject payLoad, boolean isSelectDefaultOnly) throws JSONException
    {
        final String method = COMPONENT + ".processGetPaymentsForAccount - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        String accountId = payLoad.getField(ACCOUNT_ID_PATH);
        JSONObject result = new JSONObject();
        try {
            SFAccount account = pullAccount(Integer.parseInt(accountId));
            if (account != null) {

            	if(isSelectDefaultOnly){
            		if (SFPaymentTypeEnum.CREDITCARD.toString().equalsIgnoreCase(account.getDefaultPaymentType().toString())) {
            			result.put(CREDIT_CARDS_ATTR, populateCreditCards(account.getCreditCards(), true));
            		}
            		else if (SFPaymentTypeEnum.PAYPAL.toString().equalsIgnoreCase(account.getDefaultPaymentType().toString())) {
            			//we have started managing paypal in a map, so can move toward
    	                //adding as a collection similar to the way cc is.
    	                result.put(PAYPALS_ATTR, populatePayPals(account.getPayPalMap(), true));
            		}
            		else if (SFPaymentTypeEnum.ACH.toString().equalsIgnoreCase(account.getDefaultPaymentType().toString())) {
            			result.put(ACH, populateACHInfo(account.getACHMap(), true));
            		}
            	}
            	else{
	                result.put(CREDIT_CARDS_ATTR, populateCreditCards(account.getCreditCards(), false));

	                //this should be considered deprecated, use payPals output which is a collection
	                result.put(PAYPAL_ATTR, populatePayPal(account.getPayPalInfo()));

	                //we have started managing paypal in a map, so can move toward
	                //adding as a collection similar to the way cc is.
	                result.put(PAYPALS_ATTR, populatePayPals(account.getPayPalMap(), false));
	                result.put(ACH, populateACHInfo(account.getACHMap(), false));
            	}
            }
        } catch (SFFGException e) {
            addError(SFAPIServiceResponseError.ERROR_UNABLE_TO_FETCH_DATA_FROM_FG, e.getMessage());
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method +
                    " error while querying data from FG", e);
        }
        stat.stop(methStatId, true);
        return result;
    }

    /**
     * Populates accounts information to response
     *
     * @param sfAccounts the list of {@link SFAccount}
     * @param result     the {@link JSONObject}
     * @throws JSONException the {@link JSONException}
     * @throws SFFGException the {@link SFFGException}
     */
    private void populateAccounts(List<SFAccount> sfAccounts, JSONObject result) throws JSONException, SFFGException {
        Collection<JSONObject> accounts = new ArrayList<>();
        if (sfAccounts != null && !sfAccounts.isEmpty()) {
            for (SFAccount accountWithOutInfo : sfAccounts) {
                Map<String, Object> accountDetails = populateAccount(pullAccount(accountWithOutInfo.getAccountId()));
                accounts.add(new JSONObject(accountDetails));
            }
        }
        result.put(ACCOUNTS_ATTR, accounts);
    }
    
    //small user logic
    private void populateAccounts(int userId,List<SFAccount> sfAccounts, JSONObject result, int startIndex,int requestedSize,String sortCriteria,SFSortDirection sortDirection) throws JSONException, SFException {
        Collection<JSONObject> accounts = new ArrayList<>();
        int size = 0;
        
        if (sfAccounts.isEmpty() ){
        	SFLogger.printInfo("populateAccounts: " + " accountsList is empty ");
        }
        if (sfAccounts != null && !sfAccounts.isEmpty()) {
        	size = sfAccounts.size();
    		if (size <= requestedSize) {
    			requestedSize = size;
    		}
    		if (startIndex < 0) {
    			startIndex = 0;
    		}
    		if (startIndex > size) {
    			startIndex = size;
    		}
    		int requestedCount = startIndex + requestedSize;
    		if(requestedCount > size){
    			requestedCount = size;
    		}
    		boolean reverse = false;
			if(sortDirection.name().equalsIgnoreCase("DESC")){
				reverse = true;
			}
			
    		if(SORT_BY_ACCOUNT_HOLDER_NAME.equalsIgnoreCase(sortCriteria)){
    			SFAccountHolderSortCriterion<SFAccount>.AccountHolderComparator displayNameComparator = 
					new SFAccountHolderSortCriterion<SFAccount>(sortDirection).new AccountHolderComparator();
    			ComparatorChain comp = new ComparatorChain();
                comp.addComparator(displayNameComparator, reverse);
    			Collections.sort(sfAccounts, comp);
    		}
    		if(SORT_BY_ACCOUNT_NAME.equalsIgnoreCase(sortCriteria)){
    			SFAccountNameSortCriterion<SFAccount>.AccountNameComparator accountNameComparator = 
    					new SFAccountNameSortCriterion<SFAccount>(sortDirection).new AccountNameComparator();
    			ComparatorChain comp = new ComparatorChain();
                comp.addComparator(accountNameComparator, reverse);
        		Collections.sort(sfAccounts, comp);
        		Collections.sort(sfAccounts, NullComparators.atEnd(wrapMe));
				if (reverse) {
					Collections.sort(sfAccounts, ALPHABETICAL_ORDER.reversed());
				} else {
					Collections.sort(sfAccounts, ALPHABETICAL_ORDER);
				}
        		}
    		if(SORT_BY_USER_ROLE_NAME.equalsIgnoreCase(sortCriteria)){
        		SFRoleTypeSortCriterion<SFAccount>.RoleTypeComparator roleTypeComparator = 
    					new SFRoleTypeSortCriterion<SFAccount>(sortDirection, userId, userAccountManager).new RoleTypeComparator();
        		ComparatorChain comp = new ComparatorChain();
                comp.addComparator(roleTypeComparator, reverse);
        		Collections.sort(sfAccounts, comp);
        		}
    		if(SORT_BY_NO_OF_PRODUCTS.equalsIgnoreCase(sortCriteria)){
    			SFNoOfProductsSortCriterion<SFAccount>.NoOfProductsComparator noOfProductsComparator = 
    					new SFNoOfProductsSortCriterion<SFAccount>(sortDirection).new NoOfProductsComparator();
    			ComparatorChain comp = new ComparatorChain();
                comp.addComparator(noOfProductsComparator, reverse);
        		Collections.sort(sfAccounts, comp);
           		}
        	for (int i = startIndex; i < requestedCount; i++) {
        		SFAccount currentAccount = sfAccounts.get(i);
        		if(currentAccount != null){
        		   currentAccount = pullAccount(currentAccount.getAccountId());
        		}
        		Map<String, Object> accountDetails = populateAccountFields(userId,currentAccount);
                accounts.add(new JSONObject(accountDetails));
			}
        
         }
        result.put(ACCOUNTS_ATTR, accounts);
        result.put("length", size);
    }

    private Map<String, Object> populateAccountFields(int userId,SFAccount account) throws SFException {
        Map<String, Object> accountDetails = new LinkedHashMap<>();

        if (account != null) {
        	RoleEnum role = userAccountManager.getRelation(userId, account.getAccountId());
        	if (role.equals(RoleEnum.BILLINGCONTACT)) {
        		SFLogger.printInfo("billing contact is not allowed");
        	}else{
        	if (account.getAccountName() != null) {
        		accountDetails.put(ACCOUNT_NAME_ATTR,account.getAccountName());
        	}else {
        		accountDetails.put(ACCOUNT_NAME_ATTR,EMPTY);
        	}
            accountDetails.put(ACCOUNT_ID_ATTR, account.getAccountId());
            if (account.getAccountHolder() != null) {
                accountDetails.put(ACCOUNT_HOLDER_NAME, account.getAccountHolder().getFullName());
            }else{
            	  accountDetails.put(ACCOUNT_HOLDER_NAME, EMPTY);
            }
            if (account.getAccountHolder()!=null && account.getAccountHolder().getEmail() != null) {
            	accountDetails.put(EMAIL_ATTR, account.getAccountHolder().getEmail());
            }
            else{
            	accountDetails.put(EMAIL_ATTR, EMPTY);
            }
            accountDetails.put(PRODUCTS_COUNT,SFUtils.countAcceptableProductsForProductListAPI(account) ); 
            
            
            String result=null;
            if (role != null) {
                if (role.equals(RoleEnum.ACCOUNTHOLDER)) {
                    result = ACCOUNTHOLDER_DISPLAYNAME;
                } else if (role.equals(RoleEnum.ADMINCONTACT)) {
                    result = ADMINCONTACT_DISPLAYNAME;
                } else if (role.equals(RoleEnum.PRIMARYUSER)) {
                    // according to TR 21967 if the user is both account
                    // holder and primary user, then we display account holder
                    // as the role
                    if(account.getAccountHolder()!=null){
	                    int holderId = account.getAccountHolder().getUserId();
	
	                    if (holderId == userId) {
	                        result = ACCOUNTHOLDER_DISPLAYNAME;
	                    } else {
	                        result = PRIMARYUSER_DISPLAYNAME;
	                    }
                    }else{
                    	result = PRIMARYUSER_DISPLAYNAME;
                    }
                } else if (role.equals(RoleEnum.TECHCONTACT)) {
                    result = TECHCONTACT_DISPLAYNAME;
                }
                accountDetails.put(ROLE_ATTR, result);
            }
        	}
            
            
            
        } else {
            throw new SFException("account is null");
        }
        return accountDetails;
    }
    
    
    private Map<String, Object> populateAccount(SFAccount account) throws SFFGException {
        Map<String, Object> accountDetails = new LinkedHashMap<>();

        if (account != null) {
            SFUser accountHolder = account.getAccountHolder();
            accountDetails.put(ACCOUNT_ID_ATTR, account.getAccountId());
            accountDetails.put(FIRST_NAME_ATTR, account.getAccountHolder().getFirstName());
            accountDetails.put(LAST_NAME_ATTR,account.getAccountHolder().getLastName());
            accountDetails.put(TYPE_ATTR, ACCOUNT_TYPE.get(account.getAccountType()));
            accountDetails.put(CHANNEL_ID_ATTR, account.getAccountChannelId());
            accountDetails.put(PHONE, account.getAccountHolder().getPhoneNum());
            accountDetails.put(ACCOUNT_NAME_ATTR, account.getAccountName());
            if (account.getAccountHolder() != null) {
                accountDetails.put(ACCOUNT_HOLDER_NAME, account.getAccountHolder().getFullName());
            }
            accountDetails.put(DEFAULT_PAYMENT_TYPE_ATTR, account.getDefaultPaymentType() != null ?
                    account.getDefaultPaymentType().toString() : null);
            CustomerStatus status = CustomerStatus.lookupInstanceByCode(account.getStatus());
            accountDetails.put(STATUS_ATTR, status != null ? status.getDescription() : null);
            accountDetails.put(USER_ACCOUNT_RELATIONS_ATTR, populateUserAccountRelationsToJson(account.getUserAccountRelations()));
            accountDetails.put(CREDIT_CARDS_ATTR, populateCreditCards(account.getCreditCards(), false));
            accountDetails.put(ACH, populateACHInfo(account.getACHMap(), false));
            accountDetails.put(PAYPAL_ATTR, populatePayPal(account.getPayPalInfo()));
            accountDetails.put(AVAILABLE_NEXUS_INFO, populateNexusDetails(accountHolder));
            accountDetails.put(SELECTED, shoppingCart.getCurrentPurchaseAccount() != null
                    && account.getAccountId() == shoppingCart.getCurrentPurchaseAccount().getAccountId());
        } else {
            throw new SFFGException();
        }
        return accountDetails;
    }

    private List<String>  populateNexusDetails(SFUser accountHolder) {
        List<String> availableNexusInfo = new ArrayList<>();
        if (accountHolder.getDoteuUserCategory() != null) {
            availableNexusInfo.add(NEXUS_TYPE_EU);
        }
        if (accountHolder.getDotusUsageIntent() != null
                        && accountHolder.getDotusUserCategory() != null) {
            availableNexusInfo.add(NEXUS_TYPE_US);
        }
        if (accountHolder.getDotcaUserCategory() != null) {
            availableNexusInfo.add(NEXUS_TYPE_CA);
        }
        if (accountHolder.getDotnycContactId() != null) {
            availableNexusInfo.add(NEXUS_TYPE_NYC);
        }
        return availableNexusInfo;
    }

    /**
     * Populates user account relations to response
     *
     * @param userAccountRelations the list of {@link SFUserAccountRelation}
     */
    private Collection<JSONObject> populateUserAccountRelationsToJson(List<SFUserAccountRelation> userAccountRelations) {
        Collection<JSONObject> accountRelations = null;
        if (userAccountRelations != null && !userAccountRelations.isEmpty()) {
            accountRelations = new ArrayList<>();
            for (SFUserAccountRelation accountRelation : userAccountRelations) {
                Map<String, Object> accountRelationDetails = new LinkedHashMap<>();
                accountRelationDetails.put(ACCOUNT_ID_ATTR, accountRelation.getAccountId());
                accountRelationDetails.put(USER_ID_ATTR, accountRelation.getUserId());
                accountRelationDetails.put(ROLE_ATTR, Role.getCodeByRole(accountRelation.getRole()));
                accountRelations.add(new JSONObject(accountRelationDetails));
            }
        }
        return accountRelations;
    }

    /**
     * Populates the credit card information to response
     *
     * @param creditCardsMap the collection of {@link SFUserCreditCard}
     */
    private Collection<JSONObject> populateCreditCards(Map<String, SFUserCreditCard> creditCardsMap, boolean isSelectDefaultOnly) {
        Collection<JSONObject> jsonCreditCards = new ArrayList<>();
        Collection<SFUserCreditCard> creditCards = filterEmptyAndDuplicateCreditCardsIfAny(creditCardsMap);
        if (!CollectionUtils.isEmpty(creditCards)) {
            for (SFUserCreditCard creditCard : creditCards) {
                boolean isOk = true;
                if (isSelectDefaultOnly) {
                    //only choose if it is primary
                    if (!SFCreditCardConstant.PRIMARY.getDescription().equalsIgnoreCase(creditCard.getCreditCardPriorityLevel())) {
                        isOk = false;
                    }
                }

                if (isOk) {
                    Map<String, Object> ccDetails = new LinkedHashMap<>();
                    ccDetails.put(CARD_HOLDER_NAME_ATTR, creditCard.getCardholdersName());
                    ccDetails.put(MASKED_NUMBER_ATTR, creditCard.getMaskedCreditCardNumber());
                    ccDetails.put(TYPE_ATTR, creditCard.getCreditCardType());
                    ccDetails.put(EMAIL_ATTR, creditCard.getEmail());
                    ccDetails.put(EXPIRATION_MONTH_ATTR, creditCard.getExpirationMonth());
                    ccDetails.put(EXPIRATION_YEAR_ATTR, creditCard.getExpirationYear());
                    ccDetails.put(MASKED_CVV2_ATTR, !SFStringUtil.isEmpty(creditCard.getCVV2()) ? creditCard.getCVV2() : "xxx");
                    ccDetails.put(ADDRESS_ATTR, populateAddressToJson(creditCard));
                    ccDetails.put(PRIORITY_ATTR, creditCard.getCreditCardPriorityLevel());
                    ccDetails.put(CURRENCY_CODE_ATTR, creditCard.getCurrencyCode());
                    ccDetails.put(IS_EXPIRED_ATTR, creditCard.getIsExpired());
                    if (creditCard.getWalletItemId() != null) {
                        ccDetails.put(WALLET_ID, creditCard.getWalletItemId());
                    }
                    jsonCreditCards.add(new JSONObject(ccDetails));
                }

            }
        }
        return jsonCreditCards;
    }

    private Collection<SFUserCreditCard> filterEmptyAndDuplicateCreditCardsIfAny(Map<String,
            SFUserCreditCard> creditCardsMap) {
        Map<String, SFUserCreditCard> lookupKeyAndCardMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(creditCardsMap)) {
            for (Map.Entry<String, SFUserCreditCard> creditCardEntry : creditCardsMap.entrySet()) {
                SFUserCreditCard creditCard = creditCardEntry.getValue();
                if (StringUtils.isNotEmpty(creditCard.getMaskedCreditCardNumber())) {
                    String lookUpKey = getCreditCardLookupKey(creditCard);
                    /**
                     * If lookupKeyAndCardMap already contains same lookup key and existing one in map contains
                     * walletId don't override existing one
                     */
                    if (lookupKeyAndCardMap.containsKey(lookUpKey) && !isWalletIdExists(creditCard)
                            && isWalletIdExists(lookupKeyAndCardMap.get(lookUpKey))) {
                        creditCard = lookupKeyAndCardMap.get(lookUpKey);
                    }
                    lookupKeyAndCardMap.put(lookUpKey, creditCard);
                }
            }
        }
        return lookupKeyAndCardMap.values();
    }

    private String getCreditCardLookupKey(SFUserCreditCard creditCard) {
        return creditCard.getMaskedCreditCardNumber() + PIPE + creditCard.getCreditCardType();
    }

    private boolean isWalletIdExists(SFUserCreditCard creditCard) {
        return creditCard.getWalletItemId() != null && creditCard.getWalletItemId() > ZERO;
    }

    private Collection<JSONObject> populateACHInfo(Map<String, SFACHInfo> achMap, boolean isSelectDefaultOnly) {
		Collection<JSONObject> achInfos = null;
		if (achMap != null) {
			achInfos = new ArrayList<>();
			for (Map.Entry<String, SFACHInfo> entry : achMap.entrySet()) {
				if (entry != null && entry.getValue() != null && entry.getValue().getLastFourDigits() != null) {
					SFACHInfo ach = entry.getValue();

					boolean isOk = true;
                	if(isSelectDefaultOnly){
                		//only choose if it is primary
                		if (!SFACHConstant.PRIMARY.getDescription().equalsIgnoreCase(ach.getAchPriorityLevel())){
                			isOk = false;
                		}
                	}

                	if(isOk){
						Map<String, Object> achData = new LinkedHashMap<>();

						achData.put(FIRST_NAME_ATTR, ach.getFirstName());
						achData.put(LAST_NAME_ATTR, ach.getLastName());
						achData.put(LAST_FOUR_DIGITS, ach.getLastFourDigits());
						//deprecated - user priority
						achData.put(PRIORITY_LEVEL, StringUtils.isNotEmpty(ach.getAchPriorityLevel()) ? ach.getAchPriorityLevel() : EMPTY);
						//adding to help make field naming more consistent
						achData.put(PRIORITY_ATTR, StringUtils.isNotEmpty(ach.getAchPriorityLevel()) ? ach.getAchPriorityLevel() : EMPTY);
						achData.put(ACCOUNT_NUMBER, ach.getBankAccountNumber());
						achData.put(ROUTING_NUMBER, ach.getRoutingNumber());
						achData.put(EC_ACCOUNT_TYPE, ach.getEcAccountType());
						achData.put(WALLET_ID, ach.getWalletItemId());
						achData.put(ADDRESS_ATTR, populateACHAddressToJson(ach));
						achInfos.add(new JSONObject(achData));
                	}
				}
			}
		}
		return achInfos;
	}
    

    /**
     * Populates credit card address information to response
     *
     * @param creditCard the {@link SFUserCreditCard}
     */
    public Map<String, Object> populateAddressToJson(SFUserCreditCard creditCard) {
        Map<String, Object> addressDetails = new LinkedHashMap<>();
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, creditCard.getStreetAddress());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, EMPTY);
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, creditCard.getCity());
        addressDetails.put(STATE_PROV, creditCard.getStateprov());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, creditCard.getCountry());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, creditCard.getCardPostalCode());
        return addressDetails;
    }
    /**
     * Populates ACH address information to response
     *
     * @param achInfo the {@link SFACHInfo}
     */
    private Map<String, Object> populateACHAddressToJson(SFACHInfo achInfo) {
        Map<String, Object> addressDetails = new LinkedHashMap<>();
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1, achInfo.getAddress1());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2, achInfo.getAddress2());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_CITY, achInfo.getCity());
        addressDetails.put(STATE_PROV, achInfo.getState());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY, achInfo.getCountry());
        addressDetails.put(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE, achInfo.getZipCode());
        return addressDetails;
    }

    private Map<String, Object> populatePayPal(SFPayPalInfo payPalInfo) {
        Map<String, Object> payPal = null;
        if (payPalInfo != null) {
            payPal = new LinkedHashMap<>();
            payPal.put(BILLING_AGREEMENT_ID_ATTR, payPalInfo.getBAID());
            payPal.put(FIRST_NAME_ATTR, payPalInfo.getFirstName());
            payPal.put(LAST_NAME_ATTR, payPalInfo.getLastName());
            payPal.put(EMAIL_ATTR, payPalInfo.getEmail());
            payPal.put(MASKED_EMAIL, payPalInfo.getMaskedEmail());
            if(payPalInfo.getWalletItemId() != null){
               payPal.put(WALLET_ID, payPalInfo.getWalletItemId());
            }
            //deprecated
			payPal.put(PRIORITY_LEVEL, StringUtils.isNotEmpty(payPalInfo.getPaypalPriorityLevel())
					? payPalInfo.getPaypalPriorityLevel() : EMPTY);
			//adding to help make field naming more consistent
			payPal.put(PRIORITY_ATTR, StringUtils.isNotEmpty(payPalInfo.getPaypalPriorityLevel())
					? payPalInfo.getPaypalPriorityLevel() : EMPTY);
			

        }
        return payPal;
    }
    
    private Collection<JSONObject> populatePayPals(Map<String, SFPayPalInfo> payPalMap, boolean isSelectDefaultOnly) {
    	    	
    	Collection<JSONObject> paypals = null;
        if (payPalMap != null && !payPalMap.isEmpty()) {
        	paypals = new ArrayList<>();
            for (Map.Entry<String, SFPayPalInfo> payPalEntry : payPalMap.entrySet()) {
            	SFPayPalInfo currentPaypal = payPalEntry.getValue();
            	if (currentPaypal != null && !SFStringUtil.isEmpty(currentPaypal.getEmail())) {

            		boolean isOk = true;
                	if(isSelectDefaultOnly){
                		//only choose if it is primary
                		if (!SFPayPalConstant.PRIMARY.getDescription().equalsIgnoreCase(currentPaypal.getPaypalPriorityLevel())){
                			isOk = false;
                		}
                	}

                	if(isOk){
	            		Map<String, Object> payPal = null;
	                    payPal = new LinkedHashMap<>();
	                    payPal.put(BILLING_AGREEMENT_ID_ATTR, currentPaypal.getBAID());
	                    payPal.put(FIRST_NAME_ATTR, currentPaypal.getFirstName());
	                    payPal.put(LAST_NAME_ATTR, currentPaypal.getLastName());
	                    payPal.put(EMAIL_ATTR, currentPaypal.getEmail());
	                    payPal.put(MASKED_EMAIL, currentPaypal.getMaskedEmail());
	                    if(currentPaypal.getWalletItemId() != null){
	                       payPal.put(WALLET_ID, currentPaypal.getWalletItemId());
	                    }
	                    //deprecated - use priority
	        			payPal.put(PRIORITY_LEVEL, StringUtils.isNotEmpty(currentPaypal.getPaypalPriorityLevel())
	        					? currentPaypal.getPaypalPriorityLevel() : EMPTY);
	        			//adding to make field naming more consistent
	        			payPal.put(PRIORITY_ATTR, StringUtils.isNotEmpty(currentPaypal.getPaypalPriorityLevel())
	        					? currentPaypal.getPaypalPriorityLevel() : EMPTY);

	        			paypals.add(new JSONObject(payPal));
                	}
                }
            }
        }
        return paypals;
    }

    public static void populateAddressToSFUser(SFUser sfUser, Map<String, String> addressNode) {
        sfUser.setStreetAddress(addressNode.get(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1));
        sfUser.setStreetAddress2(addressNode.get(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS2));
        sfUser.setCity(addressNode.get(SFESBConstant.RESPONSE_MAP_KEY_CITY));
        sfUser.setState(addressNode.get(STATE_PROV));
        sfUser.setCountry(addressNode.get(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY));
        sfUser.setZip(addressNode.get(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE));
    }

    private SFAccount populateSFAccount(SFJsonObject payLoad) {
        SFAccount account = new SFAccount();
        String accountType = payLoad.getField(CREATE_ACCOUNT_TYPE_PATH);
        SFAccountTypeConstant accountTypeConst = SFAccountTypeConstant.INDIVIDUAL;
        if (ENTERPRISE.equalsIgnoreCase(accountType) || BUSINESS.equalsIgnoreCase(accountType)) {
            accountTypeConst = SFAccountTypeConstant.BUSINESS;
        }
        account.setBillingContact(new SFBillingContact());
        account.setAccountType(accountTypeConst);
        account.setDefaultPaymentType(SFPaymentTypeEnum.valueOf(payLoad.getField(CREATE_ACCOUNT_DEFAULT_PAYMENT_TYPE_PATH).toUpperCase()));
        account.setCreditCard(populateAndGetSFCreditCard(payLoad, CREATE_ACCOUNT_PRIMARY_CREDIT_CARD_NODE_PATH, userAccountManager.getUserBean(),account));
        populateUserAccountRelationsToSFAccount(payLoad, account);
        return account;
    }

    public static SFUserCreditCard populateAndGetSFCreditCard(SFJsonObject payLoad, String jsonPath, SFUserBean userBean, SFAccount account) {
        Map<String, Object> creditCardDetails = payLoad.getNode(jsonPath);
        SFUserCreditCard creditCard = new SFUserCreditCard();
        creditCard.setCardholdersName(getAsString(creditCardDetails.get(CARD_HOLDER_NAME_ATTR)));
        creditCard.setCreditCardNumber(getAsString(creditCardDetails.get(NUMBER_ATTR)));
        if (creditCardDetails.get(TYPE_ATTR) != null) {
            creditCard.setCreditCardType(getAsString(creditCardDetails.get(TYPE_ATTR)).toUpperCase());
        }
        if (SFChannelUtil.isBlueHostChannel()
                && SFStringUtil.isEmpty(creditCard.getCardholdersName())) {
            creditCard.setCardholdersName(userBean.getFullName());
        }
        creditCard.setEmail(getAsString(creditCardDetails.get(EMAIL_ATTR)));
        creditCard.setExpirationMonth(getAsString(creditCardDetails.get(EXPIRATION_MONTH_ATTR)));
        creditCard.setExpirationYear(getAsString(creditCardDetails.get(EXPIRATION_YEAR_ATTR)));
        creditCard.setCVV2(getAsString(creditCardDetails.get(CVV2_ATTR)));
        String priorityLevel = getAsString(creditCardDetails.get(PRIORITY_ATTR));
        if (StringUtils.isEmpty(priorityLevel)) {
            priorityLevel = EMPTY;
            if (!SFWalletUtils.isUseWalletForPurchase(account)) {
                priorityLevel = PRIMARY_CARD;
            }
        }
        creditCard.setCreditCardPriorityLevel(priorityLevel);
        creditCard.setCurrencyCode(getAsString(creditCardDetails.get(CURRENCY_CODE_ATTR)));
        creditCard.setTransientTokenData(getAsString(creditCardDetails.get(TRANSIENT_PAYMENT_TOKEN_DATA)));
        creditCard.setTransientTokenId(getAsString(creditCardDetails.get(TRANSIENT_PAYMENT_TOKEN_ID)));
        String phoneNumber = getAsString(creditCardDetails.get(PHONE));
        if (SFStringUtil.isEmpty(userBean.getUser().getPhoneNum()) && SFStringUtil.isNotEmpty(phoneNumber)) {
            userBean.getUser().setPhoneNum(phoneNumber);
        }
        populateCreditCardAddress(creditCardDetails.get(ADDRESS_ATTR), creditCard, userBean);
        return creditCard;
    }

    public static void populateCreditCardAddress(Object addressObject, SFUserCreditCard creditCard, SFUserBean userBean) {
        if (addressObject instanceof LinkedHashMap) {
            LinkedHashMap<String, Object> address = (LinkedHashMap<String, Object>) addressObject;
            creditCard.setStreetAddress(getAsString(address.get(SFESBConstant.RESPONSE_MAP_KEY_ADDRESS1)));
            creditCard.setCity(getAsString(address.get(SFESBConstant.RESPONSE_MAP_KEY_CITY)));
            creditCard.setStateprov(getAsString(address.get(STATE_PROV)));
            creditCard.setCountry(getAsString(address.get(SFESBConstant.RESPONSE_MAP_KEY_COUNTRY)));
            creditCard.setCardPostalCode(getAsString(address.get(SFESBConstant.RESPONSE_MAP_KEY_POSTAL_CODE)));
        } else {
            creditCard.setStreetAddress(userBean.getUser().getStreetAddress());
            creditCard.setCity(userBean.getUser().getCity());
            creditCard.setStateprov(userBean.getUser().getState());
            creditCard.setCountry(userBean.getUser().getCountry());
            creditCard.setCardPostalCode(userBean.getUser().getZip());
        }

    }

    private void populateUserAccountRelationsToSFAccount(SFJsonObject payLoad, SFAccount account) {
        List<Object> relationObjects = payLoad.getMultipleFields(CREATE_ACCOUNT_USER_ACCOUNT_RELATIONS_NODE_PATH);
        if (relationObjects != null) {
            List<SFUserAccountRelation> accountRelations = new ArrayList<>();

            for (Object relationObject : relationObjects) {
                SFUserAccountRelation userAccountRelation = new SFUserAccountRelation();
                if (relationObject instanceof LinkedHashMap) {
                    Map<String, String> relations = (LinkedHashMap) relationObject;
                    userAccountRelation.setUserId(userAccountManager.getUserBean().getUserId());
                    userAccountRelation.setRole(Role.getRoleByCode(relations.get(ROLE_ATTR)));
                }
                accountRelations.add(userAccountRelation);
            }
            account.setUserAccountRelations(accountRelations);
        }

    }

    public static String getAsString(Object object) {
        String result = null;
        if (object != null) {
            result = String.valueOf(object);
        }
        return result;
    }

    private void queryAccountNames(SFUser user, Integer accountId) throws SFFGException 
    {
        final String method = COMPONENT + ".queryAccountNames - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        SFXMLSearchQuery searchQuery = new SFXMLSearchQuery();
        searchQuery.setUserId(user.getUserId());
        if (accountId != null) {
            searchQuery.setAccountId(accountId);
        }
        helperManager.getUserHelper()
                    .getAccountRelationsForUserByXmlFilter(user, searchQuery, true);
        stat.stop(methStatId, true);
    }

    public Vector<SFAccount> getPurchasableAccounts(SFUserBean user) {
        Map accountRel = userAccountManager.getAccountRelations(user.getUser().getUserId());
        Vector<SFAccount> purchasableAccounts = new Vector<>();

        if (accountRel != null) {
            Iterator relations = accountRel.keySet().iterator();
            while (relations.hasNext()) {
                RoleEnum role = (RoleEnum) relations.next();
                Vector accounts = (Vector) accountRel.get(role);

                for (int i = ZERO; i < accounts.size(); i++) {
                    SFAccount acct = (SFAccount) accounts.elementAt(i);

                    if (!purchasableAccounts.contains(acct)) {
                        if (checkPurchasePermission(user.getUser(), acct.getAccountId())) {
                            purchasableAccounts.add(acct);
                        }
                    }
                }
            }
        }

        return purchasableAccounts;
    }

    /**
     * This method checks if the current user can purchase into the specified
     * account
     *
     * @param user   the logged in user
     * @param acctId the account Id to check
     * @return boolean
     */
    private boolean checkPurchasePermission(SFUser user, int acctId) {
        boolean isAllowedToPurchase = false;
        RoleEnum roleEnum = userAccountManager.getRelation(user.getUserId(), acctId);
        SFAccount account = userAccountManager.getAccount(acctId);

        if (roleEnum == null || account == null) return false;

        SFPermissionRequest permission =
                new SFPermissionRequest(TaskEnum.PURCHASE,
                        roleEnum, ResourceEnum.ALLPRODUCTS);

        Map constraintData = new Hashtable();
        constraintData.put(SFRetailAccountConstraintChecker.ACCOUNT_TYPE,
                account.getAccountChannelType());
        constraintData.put(SFSuspendedAccountConstraintChecker.ACCOUNT_STATUS,
                new Integer(account.getStatus()));
        //TR 59615 ESE and AccountModel Changes
        //to check if account type is ESE or user is CSR rep.
        constraintData.put(SFESEAccountConstraintChecker.ACCOUNT_TYPE,
                account.getAccountChannelType());
        constraintData.put(SFESEAccountConstraintChecker.IS_CSR,
                userAccountManager.getUserBean().getUser().isCSRRep());

        if (accessController.checkPermission(permission, constraintData)) {
            isAllowedToPurchase = true;
        }
        return isAllowedToPurchase;
    }

    /**
	 *   Loading flags related to user display preferences
	 */
	public void loadUserPreference()
	{
        final String method = COMPONENT + ".loadUserPreference - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

		SFUserPreference userPref = null;
		String methodName = "SFLoginAction.loadUserPreference - ";

		try
		{
			userPref = helperManager.getUserHelper().queryUserPreference(userAccountManager.getUserBean().getSfCredential(),
					userAccountManager.getUserBean().getUser());

			userAccountManager.getUserBean().setUserPreference(userPref);
		}
		catch (SFFGException e)
		{
			SFLogger.printError( LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, methodName +
					"Failed to get user data from FG", e);
		}
		stat.stop(methStatId, true);
	}

    private String getCountryCodeOfClient() 
    {
        final String method = COMPONENT + ".getCountryCodeFromIP";
        
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        String countryCode = null;
        
        if (SFStringUtil.isEmpty(countryCode)) {
            countryCode = UNITED_STATES_CODE;
        }
        stat.stop(methStatId, true);
        return countryCode.toUpperCase();
    }

    public boolean authenticatePasswordPlus(String securityCode) {
        final String method = COMPONENT + ".authenticatePasswordPlus - ";
        SFPasswordPlusInfo sfPasswordPlusInfo = getPasswordPlusInfo();
        sfPasswordPlusInfo.setSecurityCode(securityCode);
        boolean result = false;
        try {
			int responseCode = passwordPlusService.authenticatePasswordPlus(sfPasswordPlusInfo);

			if (ReasonType.lookupByCode(responseCode) == ReasonType.SUCCESS) {
				result = true;
			} else if (ReasonType.lookupByCode(responseCode) == ReasonType.TEMPORARILY_UNAVAILABLE) {
				addError(SFAPIServiceResponseError.ERROR_PP_AUTH_TIMEOUT);
			} else if(ReasonType.lookupByCode(responseCode) == ReasonType.USER_IS_LOCKED) {
				addError(SFAPIServiceResponseError.ERROR_PP_AUTH_FAILURE);
				SFLogger.printDebug( method + " userId: " + sfPasswordPlusInfo.getUserId() + " is locked.");
			}else {
				addError(SFAPIServiceResponseError.ERROR_PP_AUTH_FAILURE);
			}
		}
		catch(SFException e) {
			addError(SFAPIServiceResponseError.ERROR_PP_AUTH_FAILURE);
			SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, COMPONENT
                    + method + SFLog4JLogger.getReducedStackTrace(e));
		}
        if(userAccountManager.getUserBean() != null){
        	userAccountManager.getUserBean().setIsLoggedIn(result);
        }
        else{
        	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL, method + " user bean object is null, cannot set loggedIn.");
        }
       	sfPasswordPlusInfo.setIsAuthenticated(result);

        return result;
    }

    private SFPasswordPlusInfo getPasswordPlusInfo() {
		SFPasswordPlusInfo ppInfo = userAccountManager.getUserBean().getPasswordPlusInfo();

		// Reset password flow uses SFUserBeanPassword bean
		if (ppInfo.getOriginalLoginAction() == null) {
			if (!SFStringUtil.isEmpty(userBeanPassword.getPasswordPlusInfo().getOriginalLoginAction())) {
				ppInfo = userBeanPassword.getPasswordPlusInfo();
				setIntHubLogInfo(ppInfo, userBeanPassword);
				return ppInfo;
			}
		}

		// populate logging information
		setIntHubLogInfo(ppInfo, userAccountManager.getUserBean());
		return ppInfo;
	}

	private void setIntHubLogInfo(SFPasswordPlusInfo info, SFUserBean useBeanInstance) {
		//useBeanInstance FraudProtection is null thus use uerBean
		info.setIpAddress(determineIpAddress());
		//no originatorId in userBean nor in useBeanInstance; use userId
		info.setOriginatorId(new Long(useBeanInstance.getUserId()));
		info.setClientId(new Long(ClientIdCodes.STOREFRONT.getCode()));
		info.setUserId(useBeanInstance.getUserId());
		info.setUserName(useBeanInstance.getUserLoginName());
	}
	
    private SFUser getUser(String userId) throws SFFGException {
        return userAccountManager.pullUser(Integer.parseInt(userId), userAccountManager.getUserBean().getSfCredential());
    }

    public SFAccount pullAccount(int accountId) throws SFFGException {
        return userAccountManager.pullAccount(accountId,
                userAccountManager.getUserBean().getSfCredential(), true);
    }

    private String getLogInMethod() {
        String logInMethod = EMPTY;
        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean.getIsLoggedIn()) {
            if (userBean.isAutoLogin()) {
                logInMethod = AUTO_LOGIN;
            } else {
                logInMethod = TRADITIONAL_LOGIN;
            }
        }
        if (userBean.getIsNewCustomer()) {
            logInMethod = NEW_CUSTOMER;
        }
        return logInMethod;
    }

    public String getHashedSessionId() {
        return SFUtils.generateHashedValue(helperManager.getSessionOriginInfo().getCustSessionId());
    }

    /**
     * @param userAccountManager the {@link SFUserAccountManager}
     */
    public void setUserAccountManager(SFUserAccountManager userAccountManager) {
        this.userAccountManager = userAccountManager;
    }

    /**
     * The {@link SFUserAccountManager} to set
     *
     * @param helperManager the {@link SFUserAccountManager}
     */
    public void setHelperManager(SFHelperManager helperManager) {
        this.helperManager = helperManager;
    }

    public void setAcctMgmtConfig(SFAcctMgmtConfig acctMgmtConfig) {
        this.acctMgmtConfig = acctMgmtConfig;
    }

    public void setAlertManager(SFAlertManager alertManager) {
        this.alertManager = alertManager;
    }

    public void setShoppingCart(SFShoppingCart shoppingCart) {
        this.shoppingCart = shoppingCart;
    }

    public void setAccessController(SFAccessController accessController) {
        this.accessController = accessController;
    }

    public void setStatus(SFStatus status) {
        this.status = status;
    }

    public void setPaidSearchCouponUtil(SFPaidSearchCouponUtil paidSearchCouponUtil) {
        this.paidSearchCouponUtil = paidSearchCouponUtil;
    }

    public void setPasswordPlusService(SFPasswordPlusService passwordPlusService) {
        this.passwordPlusService = passwordPlusService;
    }

    public void setUserBeanPassword(SFUserBean userBeanPassword) {
        this.userBeanPassword = userBeanPassword;
    }

    public void setSessionTrackingInfo(SFSessionTrackingInfo sessionTrackingInfo) {
        this.sessionTrackingInfo = sessionTrackingInfo;
    }
    
	boolean isAccountHolder(int userId) {
		List users = userAccountManager.getAccounts(userId, RoleEnum.ACCOUNTHOLDER);
		// there should be only one user in the vector
		if (users == null || users.size() < ONE) {
			// there should be an account holder
			return false;
		} else {
			SFAccount accountHolder = (SFAccount) users.get(ZERO);
			return true;
		}

	}
	
	/**
	 * The purpose of this method is to detect if
	 * the user is a registrant for any of the accounts
	 * associated with them.
	 * 
	 * This is use in the new Account Center, to 
	 * help detect what the person is allowed to do
	 * 
	 * @param user
	 * @return isRegistrant - boolean
	 */
	private boolean isRegistrant(SFUser user) throws SFException {
		final String method = COMPONENT + ".isRegistrant() - ";
		boolean isRegistrant = false;
		
		if(user == null){
			throw new SFException(method + "The user object cannot be passed in as null");
		}
		
		//first will try to detect if they are an account holder in any account
		boolean isAccountHolder = isAccountHolder(user.getUserId());
 	   
 	    if(isAccountHolder){
		
		    //the person is an account holder, now see if there is account
 	    	//that the person is an account holder for has a domain
			List accountList =  user.getAccounts();
			
			if(accountList != null && accountList.size() > 0){
				int size = accountList.size();
				
				//loop through the user's accounts
				for(int i=0;i<size;i++){
					SFAccount currentAccount = (SFAccount)accountList.get(i);
					
					//if the user is the account holder, and the account has any domains
					//then the user is a registrant
					if(accountApiService != null){
						isRegistrant = accountApiService.isUserRegistrantForAccount(currentAccount, user);
						if(isRegistrant){
							break;
						}
					}
					else{
						SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, method + " account api service is not configured");
					}
				}
			}
 	   }
 	    
 	   return isRegistrant;
		
	}
	

	public SFCountryList getCountryList()
	{
		return countryList;
	}

	public void setCountryList(SFCountryList countryList)
	{
		this.countryList = countryList;
	}


    public SFUpdateUserInfoValidationUtil getSfUpdateUserInfoValidationUtil() {
        return sfUpdateUserInfoValidationUtil;
    }

    public void setSfUpdateUserInfoValidationUtil(SFUpdateUserInfoValidationUtil sfUpdateUserInfoValidationUtil) {
        this.sfUpdateUserInfoValidationUtil = sfUpdateUserInfoValidationUtil;
    }

    public SFUpdateAccountHolderInfoUtil getUpdateAccountHolderInfoUtil() {
		return updateAccountHolderInfoUtil;
	}

	public void setUpdateAccountHolderInfoUtil(SFUpdateAccountHolderInfoUtil updateAccountHolderInfoUtil) {
		this.updateAccountHolderInfoUtil = updateAccountHolderInfoUtil;
	}
	
	public SFUserMergeService getUserMergeService() {
		return userMergeService;
	}

	public void setUserMergeService(SFUserMergeService userMergeService) {
		this.userMergeService = userMergeService;
	}

	public SFForgotUserNameUtil getSfForgotUserNameUtil() {
		return sfForgotUserNameUtil;
	}

	public void setSfForgotUserNameUtil(SFForgotUserNameUtil sfForgotUserNameUtil) {
		this.sfForgotUserNameUtil = sfForgotUserNameUtil;
	}
	
	/**
	 * determines if the user is associated with a specified account id
	 * @param user
	 * @param accountId
	 * @return boolean
	 */
	boolean isAccountFoundForUser(SFUser user, Integer accountId){
		boolean isMatchFound = false;
		if (user != null && accountId != null && user.getAccounts() != null) {
			for (Iterator iter = user.getAccounts().iterator(); iter.hasNext();) {
				SFAccount acct = (SFAccount) iter.next();
				if (acct.getAccountId() == accountId.intValue()) {
					isMatchFound = true;
					break;
				}
			}
		}
		return isMatchFound;
	}
    
	private JSONObject processGetProductsList(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".processGetProductsList - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject result = new JSONObject();
		String failureReason = null;
		boolean isErrorSpecified = false;
				
		SFUser user = userAccountManager.getUserBean().getUser();
		if (user.getUserId() == -1) {
			failureReason = "loginRequired";
			addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
			isErrorSpecified = true;
		} else {

			int startIndex = payLoad.getNumericField(START_INDEX_PATH) != null
					? payLoad.getNumericField(START_INDEX_PATH) : ZERO;
			int requestedSize = payLoad.getNumericField(REQUESTED_SIZE_PATH) != null
					? payLoad.getNumericField(REQUESTED_SIZE_PATH) : FIFTY;
			String sortCriteria = payLoad.getField(REQUESTED_SORT_CRITERIA) != null
					? payLoad.getField(REQUESTED_SORT_CRITERIA) : SORT_BY_PRODUCT_TYPE;
			String sortDirection = payLoad.getField(REQUESTED_SORT_DIRECTION) != null
					? payLoad.getField(REQUESTED_SORT_DIRECTION) : SORT_DIRECTION_DEFAULT;
			Integer accountId = payLoad.getNumericField(ACCOUNT_ID_PATH);

			try {
				if (accountId == null) {
					failureReason = "AccountIdIsEmpty";
					result.put(FAILURE_REASON, failureReason);
					addError(SFAPIServiceResponseError.REQUEST_ACCOUNT_ID_NOT_FOUND, failureReason);
					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId has to be specified on the request.");
					stat.stop(methStatId, false);
					return result;
				}
				SFSortDirection direction = SFSortDirection.ASCE;
				if (SFSortDirection.DESC.name().equalsIgnoreCase(sortDirection)) {
					direction = SFSortDirection.DESC;
				}
				if (SFSortDirection.ASCE.name().equalsIgnoreCase(sortDirection)) {
					direction = SFSortDirection.ASCE;
				}
				boolean match = false;
				if (user.getAccounts() != null) {
					match = isAccountFoundForUser(user, accountId);
				}else{
					SFLogger.printDebug(method + ": user "+ user.getUserId()+" does not have accounts ");
				}
				if (!match) {
					failureReason = "AccountIdDoesNotBelongToUser";
					result.put(FAILURE_REASON, failureReason);
					addError(SFAPIServiceResponseError.ERROR_ACCOUNT_ID_NOT_RELATED, accountId);
					SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: AccountId "+accountId+" does not belong to user "+ user.getUserId() );
					stat.stop(methStatId, false);
					return result;

				}
				if (user.isAccountThresholdExceeded() || user.isProductsThresholdExceeded()) {
					int sortCode = 0;
					// Large user
						if (SORT_BY_PRODUCT_TYPE.equalsIgnoreCase(sortCriteria)) {
							sortCode = SortType.BY_PRODUCT_TYPE.getCode();
						}
						if (SORT_BY_RENEW_DATE.equalsIgnoreCase(sortCriteria)) {
							sortCode = SortType.BY_EXP_DATE.getCode();
						}
						
						SFFGQueryResult<SFProductLite> queryResult = helperManager.getUserHelper()
								.queryProductsForSingleAccount(accountId, startIndex, requestedSize, sortCode, direction);
						Collection<JSONObject> products = populateProducts(queryResult.getResults());
						result.put(PRODUCTS_ATTR, products);
						//result.put("length", queryResult.getTotalRowsAvailable());
						if(!products.isEmpty()){
							result.put("length", products.size());
						}else{
							result.put("length", ZERO);
						}
						result.put("startIndex", startIndex);
						result.put("requestedSize", requestedSize);
					
				} else { // small user
					
					SFAccount account = pullAccount(accountId);
					populateProducts(account, result, startIndex, requestedSize, sortCriteria, direction);
					result.put("startIndex", startIndex);
					result.put("requestedSize", requestedSize);

				}
			} catch (SFFGException exp) {
				failureReason = exp.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
						method + " error while querying data from FG", exp);
			} catch (Exception e) {
				failureReason = e.getMessage();
				SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + " error: ", e);
			}
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_GET_PRODUCTS, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		return result;
	}

	private Collection<JSONObject> populateProducts(List<SFProductLite> sfProductLites) throws SFException {
		Collection<JSONObject> products = new ArrayList<>();
		List<SFProductLite> productsList = SFUtils.getAcceptableProductsForProductListAPI(sfProductLites);
		
	    if(productsList.isEmpty()){
			SFLogger.printInfo("populateProducts: getAcceptableProducts" + " productsList is empty ");
		}
		
		if (!productsList.isEmpty()) {
    	for(SFProductLite product: productsList ){
            Map<String, Object> productDetails = new LinkedHashMap<>();

            if (product != null) {
            	if (product.getProdType() != null && product.getProdType().startsWith("domain")) {
            		String[] prodType = product.getProdType().split(" ");
            		if(prodType!=null  && prodType.length>0){
            			productDetails.put(PRODUCT_TYPE_ATTR,prodType[0]);
            		}else{
            			productDetails.put(PRODUCT_TYPE_ATTR,EMPTY);
            		}
            	}else if(product.getProdType() != null){
            		productDetails.put(PRODUCT_TYPE_ATTR,product.getProdType());
            	}else {
            		productDetails.put(PRODUCT_TYPE_ATTR,EMPTY);
            	}
               
                if (product.getProdInstName() != null) {
                	productDetails.put(PRODUCT_IDENTIFIER, product.getProdInstName());
                }else{
                	productDetails.put(PRODUCT_IDENTIFIER, EMPTY);
                }
                if (product.getExpirationDate() != null) {
                	SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
                	String renewDate = null;
                	int daysLeft = SFUtils.getNumberOfDaysToExpiration(product);
                	if(product.isAutoRenewFlag() &&  daysLeft > 0){
                		renewDate = AUTO_RENEWS_ON_DISPLAYNAME +formatter.format(product.getExpirationDate());
                	} else {
                		if( daysLeft <= 0){
                	  		renewDate = EXPIRED_DISPLAYNAME +formatter.format(product.getExpirationDate());
                		}else if (daysLeft >0){
                			renewDate = EXPIRES_ON_DISPLAYNAME +formatter.format(product.getExpirationDate());
                		}
                	}
                	
                	
                	productDetails.put(RENEW_DATE, renewDate);
                }
                else{
                	productDetails.put(RENEW_DATE, EMPTY);
                }
                productDetails.put(MANAGE_LINK, SFUtils.constructManageLink(product));
                
                products.add(new JSONObject(productDetails));
                
            } else {
                throw new SFException("product is null");
            }
           
        }
		}
		return products;

	
	}
	
	
		private void populateProducts(SFAccount account, JSONObject result, int startIndex, int requestedSize,
			String sortCriteria, SFSortDirection direction) throws JSONException {
		Collection<JSONObject> products = new ArrayList<>();
		int size = 0;
		
		List<SFProduct> productsList = SFUtils.getAcceptableProductsForProductListAPI(account);
		
	    if(productsList.isEmpty()){
			SFLogger.printInfo("populateProducts: getAcceptableProducts" + " productsList is empty ");
		}
		
		if (!productsList.isEmpty()) {
			size = productsList.size();

			if (size <= requestedSize) {
				requestedSize = size;
			}
			if (startIndex < 0) {
				startIndex = 0;
			}
			if (startIndex > size) {
				startIndex = size;
			}
			int requestedCount = startIndex + requestedSize;
    		if(requestedCount > size){
    			requestedCount = size;
    		}
			if (SORT_BY_PRODUCT_TYPE.equalsIgnoreCase(sortCriteria)) {
				SFProductTypeSortCriterion<SFProduct>.ProductTypeComparator productTypeComparator = new SFProductTypeSortCriterion<SFProduct>(
						direction).new ProductTypeComparator();
				boolean reverse = false;
				if(direction.name().equalsIgnoreCase("DESC")){
					reverse = true;
				}
				ComparatorChain comp = new ComparatorChain();
	            comp.addComparator(productTypeComparator, reverse);
				Collections.sort(productsList, comp);
			}
			if (SORT_BY_RENEW_DATE.equalsIgnoreCase(sortCriteria)) {
				SFExpirationSortCriterion<SFProduct>.ExpirationComparator expirationComparator = new SFExpirationSortCriterion<SFProduct>(
						direction).new ExpirationComparator();
				boolean reverse = false;
				if(direction.name().equalsIgnoreCase("DESC")){
					reverse = true;
				}
				ComparatorChain comp = new ComparatorChain();
	            comp.addComparator(expirationComparator, reverse);
				Collections.sort(productsList, comp);				
			}
			
			SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
			for (int i = startIndex; i < requestedCount; i++) {
				Map<String, Object> productDetails = new LinkedHashMap<>();
				String productTypeDisplayName = productsList.get(i).getProductTypeDisplayName();
				String productDisplayName = productsList.get(i).getProductDisplayName();
				productDetails.put(PRODUCT_TYPE_ATTR,productTypeDisplayName); //serviceType
				productDetails.put(PRODUCT_IDENTIFIER, productDisplayName); 
				// UI is handling TM and Registered symbols. They asked not to send ; for these two words
				if(productTypeDisplayName!=null && productTypeDisplayName.contains("&trade;")){
					String name = productTypeDisplayName.replace("&trade;", "&trade");
					productDetails.put(PRODUCT_TYPE_ATTR,name);					
				}
				if(productDisplayName!=null && productDisplayName.contains("&trade;")){
					String name = productDisplayName.replace("&trade;", "&trade");
					productDetails.put(PRODUCT_IDENTIFIER,name);					
				}
				
				if(productTypeDisplayName!=null && productTypeDisplayName.contains("&reg;")){
					String name = productTypeDisplayName.replace("&reg;", "&reg");
					productDetails.put(PRODUCT_TYPE_ATTR,name);					
				}
				if(productDisplayName!=null && productDisplayName.contains("&reg;")){
					String name = productDisplayName.replace("&reg;", "&reg");
					productDetails.put(PRODUCT_IDENTIFIER,name);					
				}
				//override for SiteLock
				if(productsList.get(i) instanceof SFSiteLock){
					productDetails.put(PRODUCT_TYPE_ATTR, SFUtils.getSiteLockDisplayName(productsList.get(i)));	
					productDetails.put(PRODUCT_IDENTIFIER, SFUtils.getSiteLockDisplayName(productsList.get(i)));
				}
				//override for SFGenericVendorProduct
				if(productsList.get(i) instanceof SFGenericVendorProduct){
					productDetails.put(PRODUCT_TYPE_ATTR, ((SFGenericVendorProduct)productsList.get(i)).getProductDisplayName());						
				}
				
				String domainame = SFUtils.getDomainNameFromProduct(productsList.get(i));
				if(!SFStringUtil.isEmpty(domainame)){
					if(!domainame.equalsIgnoreCase("-1")){
						productDetails.put(PRODUCT_IDENTIFIER, domainame);   
					}
				}
				
				//String renewDate = SFProductUtils.getExpiryDateDisplay(DATE_FORMAT, productsList.get(i), true);
				String renewDate = null;
				IManagedSubscription sProduct = (IManagedSubscription) productsList.get(i);
				int daysLeft = sProduct.getNumberOfDaysToExpiration();
				if (sProduct.getAutoRenewal() && daysLeft > 0) {
					renewDate = AUTO_RENEWS_ON_DISPLAYNAME +formatter.format(sProduct.getExpiryDate());
				} else {
					if (daysLeft > 0) {
						renewDate = EXPIRES_ON_DISPLAYNAME + formatter.format(sProduct.getExpiryDate());
					} else if (daysLeft <= 0) {
						if(sProduct.getExpiryDate()!=null){
							renewDate = EXPIRED_DISPLAYNAME + formatter.format(sProduct.getExpiryDate());
							}else{
								renewDate = EXPIRED_DISPLAYNAME ;
							}
					}
				}
				productDetails.put(RENEW_DATE, renewDate);
				productDetails.put(MANAGE_LINK, SFUtils.constructManageLink(account,productsList.get(i)));

				products.add(new JSONObject(productDetails));
			}

		}
		result.put(PRODUCTS_ATTR, products);
		result.put("length", size);

	}
	
	
	
	public SFAccountAPIService getAccountApiService() {
		return accountApiService;
	}

	public void setAccountApiService(SFAccountAPIService accountApiService) {
		this.accountApiService = accountApiService;
	}

	public SFAMLoginHomePageUtil getAmLoginHomePageUtil() {
		return amLoginHomePageUtil;
	}

	public void setAmLoginHomePageUtil(SFAMLoginHomePageUtil amLoginHomePageUtil) {
		this.amLoginHomePageUtil = amLoginHomePageUtil;
	}

	private JSONObject verifyPermission(SFJsonObject payLoad) throws Exception {
		final String method = COMPONENT + ".verifyPermission - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		String failureReason = null;
		JSONObject result = new JSONObject();
		boolean isVerified = false;
		boolean isErrorSpecified = false;
		
		String password = null;

		try {
		    password = !SFStringUtil.isEmpty(payLoad.getField(CURRENT_PASSWORD_PATH))?payLoad.getField(CURRENT_PASSWORD_PATH):payLoad.getField(PASSWORD_PATH);

			if (SFStringUtil.isEmpty(password)) {
				SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "empty password");
				failureReason = "Invalid Input - password is null or empty";
				throw new Exception("password is null or empty");
			}
			SFUser currentUser = userAccountManager.getUserBean().getUser();
			SFUserBean currentUserBean = userAccountManager.getUserBean();
			if (currentUser.getUserId() == -1) {
				failureReason = "loginRequired";
				addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
				isErrorSpecified = true;
			} else {
				if (password.equalsIgnoreCase(currentUserBean.getPassword())
						&& !currentUserBean.getIsPasswordEncrypted()) {
					isVerified = true;
                } else if (currentUserBean.getIsPasswordEncrypted()) {
                    /**
                     * when stored password is encrypted, perform user entered password to encrypt and match both encrypted password.
                     * ex. BH user password stored in session with encrypted form.
                     * */
                    MD5 md5 = new MD5();
                    md5.Update(password);
                    if (currentUserBean.getPassword().equals(md5.asHex())) {
                        isVerified = true;
                    }
                }
                if (!isVerified) {
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR_USER, method + "password does not match");
                    failureReason = "password does not match";
                }
			}
		} catch (Exception e) {
			isVerified = false;
			failureReason = "General-SystemUnavailable";
			  SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM,
	                    method + e.getMessage(), e);
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			if(!isErrorSpecified){
			   addError(SFAPIServiceResponseError.ERROR_VERIFY_PERMISSION, failureReason);
			}
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}

		result.put(IS_VERIFIED, isVerified);

		stat.stop(methStatId, true);
		return result;
	}

	private static Comparator<SFAccount> ALPHABETICAL_ORDER = new Comparator<SFAccount>() {
	    public int compare(SFAccount sfa1, SFAccount sfa2) {
	    	int res = 0;
			if (sfa2.getAccountName() != null && sfa1.getAccountName()!= null) {
				 res = String.CASE_INSENSITIVE_ORDER.compare(sfa1.getAccountName(), sfa2.getAccountName());
				if (res == 0) {
					res = sfa1.getAccountName().compareTo(sfa2.getAccountName());
				}
			}
			return res;
	    }
	};
	
	static class NullComparators {
		static <T> Comparator<SFAccount> atEnd(final Comparator<SFAccount> comparator) {
			return new Comparator<SFAccount>() {

				public int compare(SFAccount sfa1, SFAccount sfa2) {
					if (sfa1.getAccountName() == null && sfa2.getAccountName() == null) {
						return 0;
					}

					if (sfa1.getAccountName()  == null) {
						return 1;
					}

					if (sfa2.getAccountName() == null) {
						return -1;
					}

					return comparator.compare(sfa1 , sfa2);
				}
			};
		}
	}
	
	Comparator<SFAccount> wrapMe = new Comparator<SFAccount>() {
	      public int compare(SFAccount sfa1, SFAccount sfa2) {
	          return sfa1.getAccountName().compareTo(sfa2.getAccountName());
	      }
	};

    public SFCalcLowUserLeftNavTabsInfo getSfCalcLowUserLeftNavTabsInfo() {
        return sfCalcLowUserLeftNavTabsInfo;
    }

    public void setSfCalcLowUserLeftNavTabsInfo(SFCalcLowUserLeftNavTabsInfo sfCalcLowUserLeftNavTabsInfo) {
        this.sfCalcLowUserLeftNavTabsInfo = sfCalcLowUserLeftNavTabsInfo;
    }

    public SFCalcLargeUserLeftNavTabsInfo getSfCalcLargeUserLeftNavTabsInfo() {
        return sfCalcLargeUserLeftNavTabsInfo;
    }
    
    public SFQuickLinksHolder getQuickLinksHolder() {
		return quickLinksHolder;
	}

	public void setQuickLinksHolder(SFQuickLinksHolder quickLinksHolder) {
		this.quickLinksHolder = quickLinksHolder;
	}

	public void setSfCalcLargeUserLeftNavTabsInfo(SFCalcLargeUserLeftNavTabsInfo sfCalcLargeUserLeftNavTabsInfo) {
        this.sfCalcLargeUserLeftNavTabsInfo = sfCalcLargeUserLeftNavTabsInfo;
    }
	
	public void setProductManager(SFProductManager productManager) {
		this.productManager = productManager;
	}
	
	public SFProductManager getProductManager(){
		return this.productManager;
	}

    public SFDomainSearchService getDomainSearchService() {
		return domainSearchService;
	}

	public void setDomainSearchService(SFDomainSearchService domainSearchService) {
		this.domainSearchService = domainSearchService;
	}

	private JSONObject processGetLeftNavBarTabsInfoMethod(SFJsonObject payLoad) throws JSONException{
        final String method = COMPONENT + ".processGetLeftNavBarTabsInfo - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);

        JSONObject result = new JSONObject();

        SFUserBean userBean = userAccountManager.getUserBean();

        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
            result.put(FAILURE_REASON, "loginRequired");
            addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
            stat.stop(methStatId, false);
            return result;
        }

        ILeftNavBarTabsInfo leftNavBarTabsInfo;
        if (userBean.getUser().isLargeUser()){
            leftNavBarTabsInfo = getSfCalcLargeUserLeftNavTabsInfo();
        }
        else {
            leftNavBarTabsInfo = getSfCalcLowUserLeftNavTabsInfo();
        }
        leftNavBarTabsInfo.init();
//
        JSONObject tabInfo = new JSONObject();
        // Domain Tabs
        addTabInfoToList(tabInfo, "DomainsTab", leftNavBarTabsInfo.showDomainsTab());
        // Domain Sub Tabs
        addTabInfoToList(tabInfo, "ManageDomainNamesSubTab", leftNavBarTabsInfo.showManageDomainNamesSubTab());
        addTabInfoToList(tabInfo, "ViewFreeWebsiteAddressSubTab", leftNavBarTabsInfo.showViewFreeWebsiteAddressSubTab());
        addTabInfoToList(tabInfo, "ManageNameServersSubTab", leftNavBarTabsInfo.showManageNameServersSubTab());
        addTabInfoToList(tabInfo, "SunrisesSubTab", leftNavBarTabsInfo.showSunrisesSubTab());
        addTabInfoToList(tabInfo, "RegisterNewSubTab", leftNavBarTabsInfo.showRegisterNewSubTab());
        addTabInfoToList(tabInfo, "TransferExistingSubTab", leftNavBarTabsInfo.showTransferExistingSubTab());
        addTabInfoToList(tabInfo, "TransferStatusSubTab", leftNavBarTabsInfo.showTransferStatusSubTab());
        addTabInfoToList(tabInfo, "KeywordAlertsSubTab", leftNavBarTabsInfo.showKeywordAlertsSubTab());
        addTabInfoToList(tabInfo, "DomainDropDownList", leftNavBarTabsInfo.showDomainDropDownList());

        // Web Hosting Tab
        addTabInfoToList(tabInfo, "HostingTab", leftNavBarTabsInfo.showHostingTab());
        // Web Hosting Sub Tabs
        addTabInfoToList(tabInfo, "ManageHostingSubTab", leftNavBarTabsInfo.showManageHostingSubTab());
        addTabInfoToList(tabInfo, "HostingDropDownList", leftNavBarTabsInfo.showHostingDropDownList());

        // E-Mail Tab
        addTabInfoToList(tabInfo, "EMailTab", leftNavBarTabsInfo.showEMailTab());
        addTabInfoToList(tabInfo, "ConfiguredEmailTab", leftNavBarTabsInfo.showConfiguredEmailTab());
        // E-Mail Sub Tabs
        addTabInfoToList(tabInfo, "EmailServicesSubTab", leftNavBarTabsInfo.showEmailServicesSubTab());
        addTabInfoToList(tabInfo, "AdminSettingsSubTab", leftNavBarTabsInfo.showAdminSettingsSubTab());
        addTabInfoToList(tabInfo, "EmailDropDownList", leftNavBarTabsInfo.showEmailDropDownList());

        // Web Site Packages Tab
        addTabInfoToList(tabInfo, "WebSitePackagesTab", leftNavBarTabsInfo.showWebSitePackagesTab());
        // Web Site Packages Sub Tab
        addTabInfoToList(tabInfo, "ManageWebSitePackagesSubTab", leftNavBarTabsInfo.showManageWebSitePackagesSubTab());
        addTabInfoToList(tabInfo, "WebSiteDropDownList", leftNavBarTabsInfo.showWebSiteDropDownList());

        // nsBusinessSpace
        addTabInfoToList(tabInfo, "BizSpaceTab", leftNavBarTabsInfo.showBizSpaceTab());
        addTabInfoToList(tabInfo, "ManageBizSpaceSubTab", leftNavBarTabsInfo.showManageBizSpaceSubTab());

        // Online Marketing Tab
        addTabInfoToList(tabInfo, "OnlineMarketingTab", leftNavBarTabsInfo.showOnlineMarketingTab());
        // Online Marketing Sub Tab
        addTabInfoToList(tabInfo, "ManageOnlineMarketingSubTab", leftNavBarTabsInfo.showManageOnlineMarketingSubTab());
        addTabInfoToList(tabInfo, "OnlineMarketingDropDownList", leftNavBarTabsInfo.showOnlineMarketingDropDownList());

        addTabInfoToList(tabInfo, "BusinessProfileTab", leftNavBarTabsInfo.showBusinessProfileTab());
        addTabInfoToList(tabInfo, "ManageBusinessProfileSubTab", leftNavBarTabsInfo.showManageBusinessProfileSubTab());
        addTabInfoToList(tabInfo, "ManageQuickProfileLocalSubTab", leftNavBarTabsInfo.showManageQuickProfileLocalSubTab());
        addTabInfoToList(tabInfo, "BusinessProfileDropDownList", leftNavBarTabsInfo.showBusinessProfileDropDownList());

        // E-Commerce Tab
        addTabInfoToList(tabInfo, "ECommerceTab", leftNavBarTabsInfo.showECommerceTab());
        addTabInfoToList(tabInfo, "ecomLegacyOnly", leftNavBarTabsInfo.showEComLegacyOnlyTab()); // for ecommlegacy only products SOFT-104915
        // E-Commerce Sub Tab
        addTabInfoToList(tabInfo, "ManageECommerceSubTab", leftNavBarTabsInfo.showManageECommerceSubTab());
        addTabInfoToList(tabInfo, "ECommerceDropDownList", leftNavBarTabsInfo.showECommerceDropDownList());

        // AdultBlock Tab
        addTabInfoToList(tabInfo, "AdultBlockTab", leftNavBarTabsInfo.showAdultBlockTab());
        // adultblock Sub Tab
        addTabInfoToList(tabInfo, "ManageAdultBlockSubTab", leftNavBarTabsInfo.showManageAdultBlockSubTab());
        addTabInfoToList(tabInfo, "AdultBlockDropDownList", leftNavBarTabsInfo.showAdultBlockDropDownList());

        // SSL Products Tab
        addTabInfoToList(tabInfo, "SSLProductsTab", leftNavBarTabsInfo.showSSLProductsTab());
        // SSL Product Sub Tab
        addTabInfoToList(tabInfo, "ManageSSLProductsSubTab", leftNavBarTabsInfo.showManageSSLProductsSubTab());
        addTabInfoToList(tabInfo, "SSLProductsDropDownList", leftNavBarTabsInfo.showSSLProductsDropDownList());

        // Design Services Tab
        addTabInfoToList(tabInfo, "DesignServiceTab", leftNavBarTabsInfo.showDesignServiceTab());
        // Design Service Sub Tab
        addTabInfoToList(tabInfo, "ManageDesignServicesSubTab", leftNavBarTabsInfo.showManageDesignServicesSubTab());
        addTabInfoToList(tabInfo, "DesignServiceDropDownList", leftNavBarTabsInfo.showDesignServiceDropDownList());

        // Web Design Editor Tab
        addTabInfoToList(tabInfo, "WebDesignEditorTab", leftNavBarTabsInfo.showWebDesignEditorTab());

        // Web Pro Tab
        addTabInfoToList(tabInfo, "WebProTab", leftNavBarTabsInfo.showWebProTab());

        // wordpress hosting
        addTabInfoToList(tabInfo, "WPHostingTab", leftNavBarTabsInfo.showWPHostingTab());
        addTabInfoToList(tabInfo, "ManageWPHostingSubTab", leftNavBarTabsInfo.showManageWPHostingSubTab());
        addTabInfoToList(tabInfo, "WPHostingDropDownList", leftNavBarTabsInfo.showWPHostingDropDownList());

        // sharepoint hosting
        addTabInfoToList(tabInfo, "SPHostingTab", leftNavBarTabsInfo.showSPHostingTab());
        addTabInfoToList(tabInfo, "ManageSPHostingSubTab", leftNavBarTabsInfo.showManageSPHostingSubTab());
        addTabInfoToList(tabInfo, "SPHostingDropDownList", leftNavBarTabsInfo.showSPHostingDropDownList());

        addTabInfoToList(tabInfo, "FileSharingTab", leftNavBarTabsInfo.showFileSharingTab());
        addTabInfoToList(tabInfo, "FileSharingSubTab", leftNavBarTabsInfo.showFileSharingSubTab());
        addTabInfoToList(tabInfo, "FileSharingDropDownList", leftNavBarTabsInfo.showFileSharingDropDownList());

        //VPS Hosting Tab
        addTabInfoToList(tabInfo, "VPSHostingTab", leftNavBarTabsInfo.showVPSHostingTab());
        addTabInfoToList(tabInfo, "VPSHostingDropDownList", leftNavBarTabsInfo.showVPSHostingDropDownList());

        // goMobi Tab
        addTabInfoToList(tabInfo, "GoMobiTab", leftNavBarTabsInfo.showGoMobiTab());
        addTabInfoToList(tabInfo, "ManageGoMobiSubTab", leftNavBarTabsInfo.showManageGoMobiSubTab());
        addTabInfoToList(tabInfo, "GoMobiDropDownList", leftNavBarTabsInfo.showGoMobiDropDownList());

        //MyTime Support Tab
        addTabInfoToList(tabInfo, "MyTimeSupportTab", leftNavBarTabsInfo.showMyTimeSupportTab());
        addTabInfoToList(tabInfo, "ManageMyTimeSupportSubTab", leftNavBarTabsInfo.showManageMyTimeSupportSubTab());
        addTabInfoToList(tabInfo, "MyTimeSupportDropDownList", leftNavBarTabsInfo.showMyTimeSupportDropDownList());

        //ContactMe Tab
        addTabInfoToList(tabInfo, "ContactMeTab", leftNavBarTabsInfo.showContactMeTab());
        addTabInfoToList(tabInfo, "ManageContactMeSubTab", leftNavBarTabsInfo.showManageContactMeSubTab());
        addTabInfoToList(tabInfo, "ContactMeDropDownList", leftNavBarTabsInfo.showContactMeDropDownList());

        //North Social Tab
        addTabInfoToList(tabInfo, "NorthSocialTab", leftNavBarTabsInfo.showNorthSocialTab());
        addTabInfoToList(tabInfo, "ManageNorthSocialSubTab", leftNavBarTabsInfo.showManageNorthSocialSubTab());
        addTabInfoToList(tabInfo, "NorthSocialDropDownList", leftNavBarTabsInfo.showNorthSocialDropDownList());

        // MS365 Tab
        addTabInfoToList(tabInfo, "MS365Tab", leftNavBarTabsInfo.showMS365Tab());
        addTabInfoToList(tabInfo, "MS365SubTab", leftNavBarTabsInfo.showMS365SubTab());
        addTabInfoToList(tabInfo, "MS365DropDownList", leftNavBarTabsInfo.showMS365DropDownList());

        //Accounts
        addTabInfoToList(tabInfo, "AccountsList", leftNavBarTabsInfo.showAccountsList());

        // Web Reputation Monitor tab
        addTabInfoToList(tabInfo, "WebReputationMonitorTab", leftNavBarTabsInfo.showWebReputationMonitorTab());
        addTabInfoToList(tabInfo, "WebReputationMonitorList", leftNavBarTabsInfo.showWebReputationMonitorList());
        addTabInfoToList(tabInfo, "SYDTab", leftNavBarTabsInfo.showSYDTab());

        //Domain Backorders
        addTabInfoToList(tabInfo, "ManageDomainBackorderSubTab", leftNavBarTabsInfo.showManageDomainBackorderSubTab());

        // Google Workspace
        addTabInfoToList(tabInfo, "GoogleWorkspaceTab", leftNavBarTabsInfo.showGoogleWorkspaceTab());

        result.put("tabInfo", tabInfo);
        stat.stop(methStatId, true);
        return result;
    }

    /**
     *
     * @param tabInfo - an existing list this method will add to
     * @param alias - name of the tab
     * @param shouldShow - yes or no
     * @throws JSONException
     */
    private void addTabInfoToList(JSONObject tabInfo, String alias, String shouldShow) throws JSONException {
        tabInfo.put(alias, SFTabInfo.YES.equalsIgnoreCase(shouldShow));
    }

    private JSONObject processStoreReminderDismissal(SFJsonObject payLoad) throws JSONException, SFFGException {
        final String method = COMPONENT + ".processStoreReminderDismissal - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        String failureReason = null;

        JSONObject result = new JSONObject();
        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
            result.put(FAILURE_REASON, "loginRequired");
            addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
            stat.stop(methStatId, false);
            return result;
        }

        String reminderName = payLoad.getField(REMINDER_NAME_PATH);
        Long dismissalDateMs = System.currentTimeMillis();

        SFUser sfUser = userBean.getUser();
        if (RENEWAL_CENTER_REMINDER_NOTIFICATION_DISMISS.equalsIgnoreCase(reminderName)) {
            sfUser.setLastRenewalCenterNotificationDismissal(dismissalDateMs);
        }
        else if (RENEWAL_CENTER_REMINDER_POPUP_DISMISS.equalsIgnoreCase(reminderName)) {
            sfUser.setLastRenewalCenterPopUpDismissal(dismissalDateMs);
        }
        else {
            result.put(FAILURE_REASON, "invalidData");
            addError(SFAPIServiceResponseError.REQUEST_INVALID_DATA, "reminderName");
            stat.stop(methStatId, false);
            return result;
        }

        try {
            helperManager.getUserHelper().updateIndividualInfo(sfUser);
        }
        catch (SFFGException e) {
            failureReason = e.getMessage();
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                    method + " - call to FG failed ", e);
        } catch (Exception ex) {
            failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex, ex);
        }

        if (failureReason != null){
            result.put(FAILURE_REASON, failureReason);
        }
        stat.stop(methStatId, failureReason != null);
        return result;
    }

    private JSONObject processGetWalletItemsForUser(SFJsonObject payLoad) throws JSONException, SFFGException {
        final String method = COMPONENT + ".processGetWalletItemsForUser - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        String failureReason = null;

        JSONObject result = new JSONObject();
        SFUserBean userBean = userAccountManager.getUserBean();
        if (userBean == null || !userBean.getIsLoggedIn() || userBean.getUser().getUserId() == -1) {
            result.put(FAILURE_REASON, "loginRequired");
            addError(SFAPIServiceResponseError.SESSION_NOT_LOGGED_IN);
            stat.stop(methStatId, false);
            return result;
        }

        Integer requestedSize = payLoad.getNumericField(REQUESTED_SIZE_PATH) !=null ? payLoad.getNumericField(REQUESTED_SIZE_PATH) : FIFTY;

        Collection<SFWalletItem> sfWalletItems = null;
        try {
            sfWalletItems = userBean.getUserAcctManager().getHelperManager().getUserHelper().getWalletItemsForUser(userBean.getUser(), requestedSize);
        }
        catch (SFFGException e) {
            failureReason = e.getMessage();
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM,
                    method + " - call to FG failed ", e);
        } catch (Exception ex) {
            failureReason = "General-SystemUnavailable";
            SFLogger.printError(LogEntryTypeEnum.ERROR_MEDIUM_PROGRAM, method + "Error : " + ex, ex);
        }

        if (sfWalletItems != null){
            Calendar endOfLastMonth = DateUtil.getEndOfLastMonth();

            Collection<JSONObject> walletsJSON = new ArrayList<>();
            for (SFWalletItem sfWalletItem : sfWalletItems){
                JSONObject walletJson = new JSONObject();
                Long walletItemId = sfWalletItem.getWalletItemId();
                walletJson.put("walletItemId", walletItemId);
                walletJson.put("accountId", sfWalletItem.getAccountId());
                // set payment fields
                try {
                    walletJson.put("paymentType", sfWalletItem.getPaymentTypeValue());
                } catch (BadPaymentTypeException e) {
                    addError(SFAPIServiceResponseError.ERROR_INVALID_PAYMENT_TYPE_ID, sfWalletItem.getPaymentType(), walletItemId, e.getMessage());
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR, method
                            + " invalid payment type, cannot determine value for wallet item: " + walletItemId + " paymentTypeId: " + sfWalletItem.getPaymentType(), e);
                }
                walletJson.put("paypalEmail", sfWalletItem.getPaypalEmail());
                try {
                    walletJson.put("ccCardType", sfWalletItem.getCcCardTypeValue());
                } catch (BadCreditCardTypeException e) {
                    addError(SFAPIServiceResponseError.ERROR_INVALID_CREDIT_CARD_TYPE_ID, sfWalletItem.getPaymentType(), walletItemId, e.getMessage());
                    SFLogger.printError(LogEntryTypeEnum.ERROR_MINOR, method
                            + " invalid credit card type, cannot determine value for wallet item: " + walletItemId, e);
                }
                walletJson.put("ccExpDate", sfWalletItem.getCcExpDate());
                walletJson.put("paymentExpired", (sfWalletItem.getCcExpDate() != null && !sfWalletItem.getCcExpDate().after(endOfLastMonth.getTime())));
                walletJson.put("bankName", sfWalletItem.getBankName());
                walletJson.put("lastFourDigits", sfWalletItem.getLastFourDigits());
                walletsJSON.add(walletJson);
            }
            result.put("walletItems", walletsJSON);
        }

        if (failureReason != null){
            result.put(FAILURE_REASON, failureReason);
        }
        stat.stop(methStatId, failureReason != null);
        return result;
    }

	private JSONObject processGetAllEligibleContactsForWhois(SFJsonObject payLoad) throws JSONException {
		final String method = COMPONENT + ".processGetAllEligibleContactsForWhois - ";
		SFMethodStatistics stat = SFMethodStatistics.getInstance();
		long methStatId = stat.start(method);
		JSONObject result = new JSONObject();
		Integer accountId = Integer.parseInt(payLoad.getField(ACCOUNT_ID_PATH));
		String failureReason = null;
		Map<String, List<SFUser>> accessList = new HashMap<String, List<SFUser>>();
		JSONArray whoIsContacts = new JSONArray();
		SFAccount sfAccount = null;
		try {
			sfAccount = productManager.getUserAccountManager().getAccount(accountId);
			if (sfAccount != null) {
				populateAccessList(RoleEnum.TECHCONTACT, accessList, sfAccount);
				populateAccessList(RoleEnum.ADMINCONTACT, accessList, sfAccount);
				populateAccessList(RoleEnum.PRIMARYUSER, accessList, sfAccount);

				if (!productManager.getUserAccountManager().isAccountHavingSamePrimaryAndAccountHolder(sfAccount.getAccountId())) {
					populateAccessList(RoleEnum.ACCOUNTHOLDER, accessList, sfAccount);
				}

				if (!accessList.isEmpty()) {
					for (Entry<String, List<SFUser>> entry : accessList.entrySet()) {
						List<SFUser> sfUsersList = (List<SFUser>) entry.getValue();
						for (int i = 0; i < sfUsersList.size(); i++) {
							SFUser sfUser = sfUsersList.get(i);
							JSONObject contactTypeJson = new JSONObject();
							JSONObject contactInfo = new JSONObject();
							String contactType = entry.getKey();
							contactInfo.put("fullName", sfUser.getFullName());
							contactInfo.put("email", sfUser.getEmail());
							contactInfo.put("contactId", sfUser.getUserId());
							contactTypeJson.put("contactInfo", contactInfo);
							contactTypeJson.put("whoisContactType", contactType);
							whoIsContacts.put(contactTypeJson);
						}
					}
				}

				result.put("whoisContacts", whoIsContacts);
				result.put("accountId", sfAccount.getAccountId());
				result.put("accountName", sfAccount.getAccountHolder().getFullName());
			} else {
				failureReason = "no Account found related to accountId";
				SFLogger.printWarning("Fail to look up account, account id is null"
						+ "Account is not related to user id: " + productManager.getUserAccountManager().getUserBean().getUserId());
				throw new SFException(method + "Fail to look up account, account id is null "
						+ "Account is not related to user id: " + productManager.getUserAccountManager().getUserBean().getUserId());

			}
		} catch (Exception e) {
			failureReason = "Failed to getAllEligibleContactsForWhois due to exception: " + e.getMessage();
			SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, COMPONENT + method
					+ " - error: Error occured while GetAllEligibleContactsForWhois ::" + sfAccount.getAccountId());
		}
		if (StringUtils.isNotEmpty(failureReason)) {
			result.put(FAILURE_REASON, failureReason);
			addError(SFAPIServiceResponseError.ERROR_GET_ALL_ELIGIBLE_CONTACTS_FOR_WHOIS, failureReason);
			stat.stop(methStatId, false);
		} else {
			stat.stop(methStatId, true);
		}
		return result;
	}

    @SuppressWarnings("unchecked")
	private void populateAccessList(RoleEnum role , Map<String, List<SFUser>> accessList,SFAccount sfAccount) {
		List<SFUser> users=userAccountManager.getUsers(sfAccount.getAccountId(),role);
		if (users!=null && users.size()>0) {
			accessList.put(role.toString(),users);
		}
	}

	private List<String> getAccountRandomActiveDomainNames(SFAccount sfAccount) {
		final String meth = COMPONENT + ".getAccountRandomActiveDomainNames(SFAccount)";
		List<String> randomActiveDomains = new ArrayList<>();
		List<SFProduct> productsList = new ArrayList<>();
		Set<String> domSet = new HashSet<String>();
		domSet.add(SFProductCodeConstant.DOM.getDescription());
		productsList.addAll(sfAccount.getProductCollection().getAllProductsStartWithProductCodeSequence(domSet));

		if (!CollectionUtils.isEmpty(productsList)) {
			//Collections.sort(productsList, new ProductPurchaseDateComparator());
			Collections.shuffle(productsList);
			for (int index = 0; index < productsList.size(); index++) {

				boolean already = false;
				try {
					// The following tries to add only one variant of a
					// domain
					// for example: for domainTest.biz, domainTest.info,
					// domainTest.com,
					// only domainTest.biz will be added to domainNames list

					String domain[] = productsList.get(index).getDomainName().split("\\.");
					already = randomActiveDomains.stream().anyMatch(d -> domain[0].equalsIgnoreCase(d.split("\\.")[0]));
				} catch (Exception e) {
					// Exception might occur during comparison if product is
					// not domain
				}
				if (!already) {
					if (productsList.get(index).getDomainName() != null
							&& productsList.get(index).getDomainName().trim().length() > 0) {
						randomActiveDomains.add(productsList.get(index).getDomainName());
					}
				}

				if (SFConfig.getInstance().getAlternativeDomainDisplaySize() == randomActiveDomains.size()) {
					break;
				}
			}
		}
		SFLogger.printDebug(meth + "AccountId : "+sfAccount+ "randomActiveDomainNames: " + randomActiveDomains);
		return randomActiveDomains;
	}
    
	public List<SFAccount> getAccounts() {
		return userAccountManager.getAccounts(userAccountManager.getUserBean().getUser().getUserId());
	}
	
	private List<String> searchForAlternativeDomains(List<String> randomActiveDomainNames, String[] recommendedTlds) {
		final String meth = COMPONENT + ".searchForAlternativeDomains";

		List<SFSearchObject> availableDomains = new ArrayList<SFSearchObject>();
		List<String> alternativeDomains = new ArrayList<>();
		// Domain Search
		try {
			List<SFSearchObject> searchObjectDomainList = buildDomainList(randomActiveDomainNames, recommendedTlds);
			domainSearchService.searchBulkDomains(searchObjectDomainList, false);

			for (SFSearchObject newSo : searchObjectDomainList) {
				if (newSo != null && newSo.getIsAvailable() == true) {
					if (SFArrayUtil.isArrayElement(newSo.getDomainName(),
							randomActiveDomainNames.toArray(new String[randomActiveDomainNames.size()]))) {
						newSo.setSelected(true);
					}
					availableDomains.add(newSo);
				}
			}

		} catch (Exception e) {
			SFLogger.getInstance().logError(LogEntryTypeEnum.ERROR_MEDIUM_SYSTEM,
					meth + " - domain search failed, cannot show available TLDs", e);
		}

		if (!CollectionUtils.isEmpty(availableDomains)) {
			availableDomains.forEach(domain ->{
				alternativeDomains.add(domain.getDomainName());
			});
		}
		SFLogger.printDebug(meth + "alternative available Domains List: " + alternativeDomains);
		return alternativeDomains;
	}
	
	
	/**
	 * this method used to sprin available alternative doamins to get only 3
	 * domains.
	 * 
	 * @param alternativeDomains
	 * @return
	 */
	private List<String> spinDomains(List<String> alternativeDomains) {
		final String meth = COMPONENT + ".spinDomains";
		List<String> spinnerDomains = new ArrayList<>();
		if (!CollectionUtils.isEmpty(alternativeDomains)) {
			boolean already = true;
			for (String domain : alternativeDomains) {
				String domains[] = domain.split("\\.");
				already = spinnerDomains.stream().anyMatch(d -> domains[0].equalsIgnoreCase(d.split("\\.")[0]));
				if (spinnerDomains.size() < 3 && !already) {
					spinnerDomains.add(domain);

					if (spinnerDomains.size() == 3) {
						break;
					}

				}
			}
          
			// check if spinnerDomains size still less than 3 then spin to get
			// max 3.
			if (spinnerDomains.size() < 3) {
				for (String domain : alternativeDomains) {
					if (spinnerDomains.size() < 3 && !spinnerDomains.contains(domain)) {
						spinnerDomains.add(domain);

						if (spinnerDomains.size() == 3) {
							break;
						}
					}
				}
			}
		}
		// set domains to list
		amAlternativeDomains.clear();
		setAMAlternativeDomains(spinnerDomains);
		SFLogger.printDebug(meth + "spinner Domains List: " + spinnerDomains);
		return spinnerDomains;
	}

	/**
	 * Builds a list of SFSearchObjects for the given domains and TLD list.
	 * Search object list follows TLD ordering.
	 * 
	 * @param olderActiveDomainNames
	 *            List of domain name without TLD
	 * @param tlds
	 *            String[] of TLDs to apply
	 * @return Vector of SFSearchObjects
	 */
	private List<SFSearchObject> buildDomainList(List<String> olderActiveDomainNames, String[] tlds) {
		List<SFSearchObject> searchObjs = new ArrayList<SFSearchObject>(tlds.length);
		olderActiveDomainNames.forEach(domain -> {
			for (String tld : tlds) {
				SFSearchObject so = new SFSearchObject(SFUtils.extractSecondLevelDomain(domain) + tld);
				so.setOriginal(true);
				searchObjs.add(so);
			}

		});

		return searchObjs;
	}
	
	private synchronized void setAMAlternativeDomains(List<String> newValue) {
		if (newValue == null) {
			amAlternativeDomains = new ArrayList<String>();
		} else {
			amAlternativeDomains = newValue;
		}
	}

	public synchronized List<String> getAMAlternativeDomains() {
		return amAlternativeDomains;
	}


	public SFUserBean getMergeUserBeanData() {
		return mergeUserBeanData;
	}
	public void setMergeUserBeanData(SFUserBean mergeUserBeanData) {
		this.mergeUserBeanData = mergeUserBeanData;
	}

    private JSONObject processAuthenticateWithJwtTokenForSSO(SFJsonObject payLoad) throws JSONException {
        final String method = COMPONENT + ".processAuthenticateWithJwtTokenForSSO - ";
        SFMethodStatistics stat = SFMethodStatistics.getInstance();
        long methStatId = stat.start(method);
        JSONObject result = new JSONObject();
        JSONObject newResult = new JSONObject();
        JSONObject newPayloadResult = new JSONObject();
        SFJsonObject newPayload = null;
       
        String token = payLoad.getField(TOKEN_PATH);
        
        try {
            int userId = validateLoginJWTToken(token, SFConfig.getInstance().getPublicKey());
            String password = getUserPassword(userId);
            SFUser user = getUser(String.valueOf(userId));
            result.put("userLoginName", user.getUserLoginName());
            result.put("password", password);
            newPayloadResult.put("request",result);
            newPayload = new SFJsonObject(newPayloadResult);
            userAccountManager.getUserBean().setIsPasswordEncrypted(true);
            JSONObject jsonObj = processAuthenticateUserLogin(newPayload);
            if (jsonObj != null) {
                if (jsonObj.get("authenticated").equals(true)) {
                    newResult.put(AM_LOGIN_HOMEPAGE, BH_BASE_URL.concat(jsonObj.get("amLoginHomePage").toString()));
                    newResult.put(AUTHENTICATED, true);
                } else {
                    newResult.put(AUTHENTICATED, false);
                }
            }
        }  catch(Exception e){
        	newResult.put(STATUS_ATTR, "Failure");
        	newResult.put(AUTHENTICATED, false);
            SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, method + " - unknown exception occurred.", e);
        }
        stat.stop(methStatId, true);
        return newResult;
    }

    public int validateLoginJWTToken(String jwtToken, String publicKey) throws Exception {
        final String method = COMPONENT + ".validateLoginJWTToken - ";
        SignatureAlgorithm sa = null;
        JSONObject payloadJsonObject = null;
        JSONObject headerJsonObject = null;
        String algorithm = "RSA";
        PublicKey generatedPublic = null;
        Base64.Decoder decoder = Base64.getDecoder();
        DecodedJWT jwt = JWT.decode(jwtToken);
        if (jwt == null) {
            return 0;
        }
        String payload = new String(decoder.decode(jwt.getPayload()));
        String header = new String(decoder.decode(jwt.getHeader()));
        String signature = jwt.getSignature();
        byte[] decoded = new byte[0];
        if (payload.isEmpty() && header.isEmpty()) {
            return 0;
        }
        try {
            payloadJsonObject = new JSONObject(payload);
            headerJsonObject = new JSONObject(header);
        } catch (JSONException err) {
            // LOGGER.error(Logger.EVENT_FAILURE, "Error :" + err.toString());
            throw new JSONException(err.getMessage());
        }
        int userIdInToken = getUserId(payloadJsonObject);
        if (payloadJsonObject.has("sub")) {
            if (payloadJsonObject.get("sub").toString().toLowerCase().contains("jarvis")) {

                if (!payloadJsonObject.has("scope") || !payloadJsonObject.get("scope").toString().equalsIgnoreCase("login")) {
                    SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, method+
                            " payload does not have scope field or payload does not have valid scope for login.");
                    throw new Exception("Generic error");
                }
                decoded = Base64.getDecoder().decode(publicKey);
                generatedPublic = getPublicKey(algorithm, generatedPublic, decoded);
                if ("RS256".equals(headerJsonObject.get("alg"))) {
                    sa = SignatureAlgorithm.RS256;
                    String tokenWithoutSignature = jwt.getHeader() + "." + jwt.getPayload();
                    DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, generatedPublic);
                    if (!validator.isValid(tokenWithoutSignature, signature)) {
                    	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, "Invalid access token");
                    	throw new Exception("Generic error");
                    } else {
                        if (jwt.getExpiresAt().before(new Date())) {
                        	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, "token is expired");
                        	throw new Exception("Generic error");
                        }
                        //LOGGER.info(Logger.EVENT_SUCCESS, "valid access token");
                        return userIdInToken;
                    }
                } else {
                	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,  "algorithm "+ headerJsonObject.get("alg")+" is currently not supported");
                	throw new Exception("Generic error");
                }

            } else {
            	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM, 
                "payload does not have JARVIS in sub field.");
            	throw new Exception("Generic error");
            }
        } else {
        	SFLogger.printError(LogEntryTypeEnum.ERROR_CRITICAL_PROGRAM,  "payload does not have sub field.");
        	throw new Exception("Generic error");
        }
        
    }

    private PublicKey getPublicKey(String algorithm, PublicKey generatedPublic, byte[] decoded) {
        KeyFactory kf;
        try {
            kf = KeyFactory.getInstance(algorithm);
            generatedPublic = kf.generatePublic(new X509EncodedKeySpec(decoded));
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return generatedPublic;
    }

    public int getUserId(JSONObject payloadJsonObject) throws JSONException {
        String sub = (String) payloadJsonObject.get("sub");
        String[] subStr = sub.split(":");
        //"sub":"urn:jarvis:webcom:account:67529321"
        if (subStr != null && subStr.length == 5 && subStr[4] != null) {
            return Integer.valueOf(subStr[4]);
        } else {
            return 0;
        }

    }

}