Add Site Account

Add Site Account

Overview

This section provides details on how you can enable consumers to add accounts to your application.

NOTE: This section assumes that your consumer is adding an account for the first time. For adding sites after at least one account has been added, see Subsequent Add Site Account Operations.

For flow diagram, refer to the Add Site Account page.

Use Case

Mary is a consumer of your application. Mary has BankIQ checking and savings accounts and has not yet added either of them to your application. She decides to add them both.

Prerequisites

  1. Make sure the IP addresses from which you are going to make API calls are whitelisted (approved) at Yodlee. Whitelisting in general applies to any API calls to be made by developer (whether aggregation services, account verification services, account information services, and so on). 
  2. Have the cobrand credentials available (these would have been issued to you by Yodlee). 

Steps

Step 1: Get the CobrandContext

See the Prerequisites section and Step 1: Getting the CobrandContext in Non-SSO Login section in Authentication Services.

Step 2: Register a User

Use the register3 API to register the consumer on the Yodlee platform.

The call will return UserInfo object from which the UserContext can be retrieved. UserContext is a mandatory argument for other subsequent calls that act on a user’s accounts.

Step 3: Locating Sites

In this step, you will present a list of sites to your consumer. The consumer can then pick the site to be added to the Yodlee system. You can use one or more of the following approaches:

  1. Show the list of all sites available (that are enabled for your customer).
  2. Show a list of popular sites.
  3. Show a list of sites that match the search criteria entered by the consumer.

The following table summarizes these available approaches respectively. See the use cases below to understand the consumer experience of each of these approaches.

Function Description Service API
Get all sites Returns a list of all sites

com.yodlee.ext.traversal.SiteTraversal

getAllSites
Get popular sites Returns a list of popular sites com.yodlee.ext.traversal.SiteTraversal getPopularSites
Get a filtered list of sites based on a string Returns a site account based on a string com.yodlee.ext.traversal.SiteTraversal searchSite

Use Cases

  1. Mary has a bank account with BankIQ. Mary logs in to your application to add an account. Mary is shown all sites available for the customer and she selects the one she wants to add. Use the getAllSites API for providing this kind of user experience.
  2. Mary has a bank account with BankIQ. Mary logs in to Yodlee PersonalFinance application to add the account. Mary is shown all the popular sites depending on the preference set. Use the getPopularSites API for providing this kind of user experience.
  3. Mary has a bank account with BankIQ. Mary logs in to Yodlee PersonalFinance application to add the account. Mary tries searching the BankIQ site by providing the string: IQ. She finds multiple accounts that contain the IQ string and picks BankIQ from the list. Use the searchSite API for providing this kind of user experience. 

Step 4: Get Login Form Fields for the Site

Once the consumer selects an account to add, you will need to prompt for the consumer’s credentials for the selected site. You can use the getSiteLoginForm API  of com.yodlee.core.accountmanagement.SiteAccountManagement service to get a login form for the selected site. The same can be presented to your consumer prompting them to enter their credentials.

Step 5: Adding an Account

Use the addSiteAccount API of com.yodlee.core.accountmanagement.SiteAccountManagement service to accomplish this step.

  • UserContext – This can be retrieved from the UserInfo object obtained in Step 2. (Refer to the code snippet)
  • siteId – This is the identifier of the site that the consumer wants to add.
  • credentialFields – List of user credentials that were entered by the consumer in Step 4.

A successful execution of this API initiates a refresh for the item with a priority of com.yodlee.soap.core.refresh.Refresh.REFRESH_PRIORITY_HIGH. Refresh is a process of gathering the data from the site for the item. The account and transaction data will be gathered and stored in the Yodlee database.

Some sites require more than just username/password to log in. Those sites use what is called multifactor authentication (MFA). They require that the consumer answer additional security questions after the username is entered and before the password is entered or after the username and password are entered together. In these cases, one more check is required to identify if the site uses MFA or not. If MFA, the refresh flow will involve additional steps. There are 3 types of MFA: Token, Image, and Security Questions & Answers.

Use the getMFAResponseForSite and putMFARequestForSite  APIs  of com.yodlee.core.refresh.Refresh to handle the MFA sites.

Step 6: Getting the Status of Refresh

The next step is to check the status of the account refresh. You may want to indicate the refresh status to your consumers.
You can fetch the status of refresh by using the getSiteRefreshInfo API of com.yodlee.core.refresh.Refresh service. The API takes the UserContext as parameter and returns an array of RefreshInfo objects corresponding to the items belonging to the UserContext specified.

Step 7: Viewing Added Accounts

To display the list of aggregated accounts, you can use either the getAllSiteAccounts API or the getSiteAccounts API of com.yodlee.core.accountmanagement.SiteAccountManagement service. The following use cases provide additional details that may help you decide which of them to use.

  1. Mary has a savings account and checking account at IQBank. She also has investment accounts at other financial institutions. She has added all these accounts to your application. She wants to see all these accounts listed in your application. Your application allows Mary to see all her accounts by calling the getAllSiteAccounts API.
  2. Mary has bank and card accounts with BankIQ. Mary logs into BankIQ and finds her banking accounts displayed in one page and loan accounts displayed in the other page of the application. Mary adds BankIQ in Yodlee PersonalFinance application. Mary finds her card and bank account details displayed in your application. Your application can specify a list of siteIds and display details of all those sites in one place by using getSiteAccounts API.

Code Snippet

/* Getting the co-brand context */
/ * CobrandCredentials obtained from yodlee */
String cobrandLoginName = "cobrand"; String cobrandPassword = "password";

/* Do cobrand login with the rest of the parameters to get cobrand context */
CobrandCredentials cobCreds = new CobrandPasswordCredentials(cobrandLoginName,cobrandPassword);
CobrandLogin cobrandLoginProxy = (CobrandLogin) ProxyFactory.createProxy(CobrandLogin.class.getName());
CobrandContext cobCtx = cobrandLoginProxy.loginCobrand(cobrandId,appId,locale,tncVersion,cobCreds);

/* Registering the user */
UserProfile userProfile = new UserProfile() ;
userProfile.setEmailAddress("abc@yodlee.com") ;
userProfile.setFirstName("abc") ;
UserCredentials credentials = new PasswordCredentials("userName","password") ;
UserInfo userInfo = registration.register(cobrandContext,credentials , userProfile, null) ;
/* Show the list of sites to user */
com.yodlee.ext.traversal.SiteTraversal   SiteTraversalProxy = (SiteTraversal)     ProxyFactory.createProxy(com.yodlee.ext.traversal.SiteTraversal.class.getName());
Option 1: To show all available sites.
try {
            System.out.println("Retrieving sites...");
            SiteInfo[] siteInfos = SiteTraversalProxy.getAllSites(cobCtx).getElements();
            System.out.println(siteInfos.length + " sites retrieved");
            System.out.println("----------------------------------");
            for(SiteInfo siteInfo : siteInfos) {
System.out.println("[" + siteInfo.getDefaultDisplayName() + "] - " + siteInfo.getSiteId());
            }
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }Option 2: To show popular sites, based on certain filter criteria.
try {
            PopularSiteFilter filter = new PopularSiteFilter();
            filter.setSiteLevel(PopularSiteLevel.POPULAR_COUNTRY);
            SiteInfo[] siteInfos = SiteTraversalProxy.getPopularSites(userCtx, filter).getElements();
            
System.out.println("Listing sites with level of popularity: " + filter.getSiteLevel().getValue());
            for(SiteInfo siteInfo : siteInfos) {
System.out.println("[" + siteInfo.getDefaultDisplayName() + "] - " + siteInfo.getSiteId());
            }
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
Option 3: To show sites based on user search.
SiteInfo[] siteInfos=SiteTraversalProxy.searchSite(userInfo.getUserContext(), siteSearchString);
/*This list of siteInfos could be shown to user so that he could select the site he wants to add. */

System.out.print("Please enter a search string: ");
        String searchString = “User entered search string”
        
        try {
            SiteInfo[] siteInfos = SiteTraversalProxy.searchSite(userCtx, searchString).getElements();
            System.out.println(siteInfos.length + " sites retrieved");
            
            System.out.println("Sites retrieve with text " + searchString + "*");
            for(SiteInfo siteInfo : siteInfos) {
                System.out.println("[" + siteInfo.getDefaultDisplayName() + "] - " + siteInfo.getSiteId());
            }
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
/* To view Site detailed Information */
try {
            if(siteId == null) {
                System.out.print("Please enter site ID: "); siteId = IOUtils.readLong();
            }
            
            SiteFilter filter = new SiteFilter();
            filter.setSiteId(siteId);
            
            SiteInfo siteInfo = SiteTraversalProxy.getSiteInfo(getCobrandContext(), filter);
            
            System.out.println("-- Site: " + siteInfo.getDefaultDisplayName());
            System.out.println("-- URL: " + siteInfo.getBaseUrl());
            System.out.println("-- Organization: " + siteInfo.getDefaultOrgDisplayName());
            
            System.out.print("-- Containers: ");
            for(ContainerInfo container : siteInfo.getEnabledContainers().getElements()) {
                System.out.print(container.getContainerName() + " ");
            }
            
            System.out.println("\n-- Content Services: ");
            for(ContentServiceInfo csInfo : siteInfo.getContentServiceInfos().getElements()) {
                System.out.println("Container: " + csInfo.getContainerInfo().getContainerName());
                System.out.println("Registration URL: " + csInfo.getRegistrationUrl());
                System.out.println("Login URL: " + csInfo.getLoginUrl());
                System.out.println("MFA Type: " + (csInfo.getMfaType() != null ? csInfo.getMfaType() : ""));
            }
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
/* Show login form and prompt for credentials */ com.yodlee.core.accountmanagement.SiteAccountManagement siteAccountMgtProxy = (com.yodlee.core.accountmanagement.SiteAccountManagement)
ProxyFactory.createProxy(com.yodlee.core.accountmanagement.SiteAccountManagement.class.getName());
try {
            if(siteId == null) {
                System.out.print("Please enter site ID: ");
                siteId = [Site Id]
            }
            
            Form loginForm = siteAccountMgtProxy.getSiteLoginForm(getCobrandContext(), siteId);
            
             } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }

/* Adding the selected site account */
// Adding site account
            SiteAccountInfo saInfo = siteAccountMgtProxy.addSiteAccount(userCtx, siteInfo.getSiteId(),SchemaUtils.asList(userInputValues));
            
            System.out.println("\t---> Site account succesfully added! \n" +
                               "\t---> New site account ID is: " + saInfo.getSiteAccountId() + "\n");
            
    if(checkForMFAEnabled(saInfo)) refreshSiteAccount(saInfo);/* Utility Methods for site account addition */
private boolean checkForMFAEnabled(SiteAccountInfo siteAccount) throws Exception {
        SiteFilter siteFilter = new SiteFilter();
        siteFilter.setSiteId(siteAccount.getSiteInfo().getSiteId());
        SiteInfo siteInfo = SiteTraversalProxy.getSiteInfo(getCobrandContext(), siteFilter);
        
        for(ContentServiceInfo csi : siteInfo.getContentServiceInfos().getElements()) {
            if(csi.getMfaCoverage() != null)
                return true;
        }

        return false;
    }

public void refreshSiteAccount(SiteAccountInfo siteAccount) throws Exception {
        if(siteAccount == null)
            siteAccount = requestSiteAccountWhenNull();    
        
        RefreshParameters refreshParameters = new RefreshParameters();
        refreshParameters.setRefreshPriority(RefreshHelper.REFRESH_PRIORITY_HIGH);
        refreshParameters.setForceRefresh(true);
        
        if(checkForMFAEnabled(siteAccount))
            refreshParameters.setRefreshMode(RefreshMode.MFA_REFRESH_MODE);
        else refreshParameters.setRefreshMode(RefreshMode.NORMAL_REFRESH_MODE);    
        
        System.out.print("Starting site account refresh for site account " + siteAccount.getSiteAccountId());
        SiteRefreshInfo refreshInfo = refreshProxy.startSiteRefresh(userCtx,
                                                                            siteAccount.getSiteAccountId(),
                                                                            refreshParameters);
        System.out.println("..." + refreshInfo.getSiteRefreshStatus().getValue());
        
        if(refreshInfo.getUpdateInitTime() == 0) {
            System.out.println("The refresh is either complete or is not all triggered");
            return;
        }

        if(refreshInfo.getSiteRefreshMode().equals(RefreshMode.NORMAL_REFRESH_MODE))
            handleNonMFASiteRefresh(refreshInfo, siteAccount);
        else if(refreshInfo.getSiteRefreshMode().equals(RefreshMode.MFA_REFRESH_MODE))
            handleMFARefresh(siteAccount.getSiteAccountId());
        
        boolean done = false;

        while(! done) {
            refreshInfo = refreshProxy.getSiteRefreshInfo(userCtx, siteAccount.getSiteAccountId());
            System.out.println("..." + refreshInfo.getSiteRefreshStatus());

            if(refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_COMPLETED) ||
                    refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_TIMED_OUT) ||
                    refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_NEVER_INITIATED)) {
                
            }
        }
        
    }

    public void handleNonMFASiteRefresh(SiteRefreshInfo refreshInfo, SiteAccountInfo siteAccount) throws Exception {
        if(refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.LOGIN_FAILURE)) {
            System.out.println("Refresh cannot continue due to invalid credentials.  Status: " +
                                refreshInfo.getSiteRefreshStatus().getValue());
        }
            
        long startTime = new Date().getTime();
        long currTime = startTime;
        
        while(! refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_COMPLETED) &&
                ! refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_CANCELLED) &&
                ! refreshInfo.getSiteRefreshStatus().equals(SiteRefreshStatus.REFRESH_TIMED_OUT)) {
            System.out.print("\tChecking refresh status...");
            refreshInfo = refreshProxy.getSiteRefreshInfo(userCtx, siteAccount.getSiteAccountId());
            System.out.println(refreshInfo.getSiteRefreshStatus().getValue());
            
            if(currTime - startTime < REFRESH_TIMEOUT_MIILIS) {
                Thread.sleep(SLEEP_MILLIS);
                currTime = new Date().getTime();
            } else {
                System.out.println("Refresh timeout");
                refreshProxy.stopSiteRefresh(userCtx,
                                                    siteAccount.getSiteAccountId(),
                                                    RefreshHelper.STOP_REFRESH_REASON_TIMEDOUT);
                break;
            }    
        }
    }

private void handleMFARefresh(Long siteAccountId) throws Exception {
        try {
            MFARefreshInfo mfaRefreshInfo = refreshProxy.getMFAResponseForSite(userCtx, siteAccountId);
            
            while(mfaRefreshInfo.isRetry()) {
                mfaRefreshInfo = refreshProxy.getMFAResponseForSite(userCtx, siteAccountId);
                //TODO: Do we need a delay here?
            }
            
            if(mfaRefreshInfo.isIsMessageAvailable()) {
                MFAFieldInfo fieldInfo = mfaRefreshInfo.getFieldInfo();
                if(fieldInfo == null)
                    return; //TODO: Confirm this
                
                MFAUserResponse mfaUserResponse = null;
                if(fieldInfo instanceof TokenIdFieldInfo) { //TokenIdFieldInfo
                    System.out.print(((TokenIdFieldInfo)fieldInfo).getDisplayString() + ":");
                    String tokenId = IOUtils.readStr();
                    mfaUserResponse = new MFATokenResponse(tokenId);
                } else if(fieldInfo instanceof ImageFieldInfo) { //ImageFieldInfo
                    System.out.println(((ImageFieldInfo)fieldInfo).getDisplayString() + ":");
                    String imageStr = IOUtils.readStr();
                    mfaUserResponse = new MFAImageResponse(imageStr);
                } else if(fieldInfo instanceof SiteLoginOptionsFieldInfo) { //SiteLoginOptionsFieldInfo
                    SiteLoginOptionsFieldInfo sloFieldInfo = (SiteLoginOptionsFieldInfo) fieldInfo;
                    QuestionAndAnswerValues[] questionsAndAnswers = sloFieldInfo.getSiteLoginOptionsAndValues().getElements();
                    mfaUserResponse = handleMFAQuestionAndAnswers(questionsAndAnswers);
                } else if(fieldInfo instanceof SecurityQuestionFieldInfo) { //SecurityQuestionFieldInfo
                    SecurityQuestionFieldInfo sqFieldInfo = (SecurityQuestionFieldInfo) fieldInfo;
                    QuestionAndAnswerValues[] questionsAndAnswers = sqFieldInfo.getQuestionAndAnswerValues().getElements();
                    mfaUserResponse = handleMFAQuestionAndAnswers(questionsAndAnswers);
                }
                
                boolean success = refreshProxy.putMFARequestForSite(this.userCtx, mfaUserResponse , siteAccountId);
                
                if(success == true)
                    handleMFARefresh(siteAccountId);
            } else {
                if(mfaRefreshInfo.getErrorCode().equals(0L))
                    System.out.println("Error while updating this account");
            }
        } catch (Exception e) {
            System.err.println("An error has ocurred.  Hence, aborting refresh operation.  " +
                                "Error: " + e.getMessage());
            
            try {
                refreshProxy.stopSiteRefresh(this.userCtx,
                                                    siteAccountId,
                                                    RefreshHelper.STOP_REFRESH_REASON_USER_ABORTED);
            } catch(Exception ex) {
                throw ex;
            }
            
            throw e;
        }
    }

    private MFAUserResponse handleMFAQuestionAndAnswers(
            QuestionAndAnswerValues[] questionsAndAnswers) throws Exception {
        if(questionsAndAnswers == null)
            throw new Exception("Invalid question and answers: " + questionsAndAnswers);
        
        MFAUserResponse mfaUserResponse = null;
        List qaDetails = new ArrayList(questionsAndAnswers.length);
        
        for(QuestionAndAnswerValues qna : questionsAndAnswers) {
            if(qna instanceof SingleQuesSingleAnswerValues) {
                SingleQuesSingleAnswerValues question = (SingleQuesSingleAnswerValues) qna;
                System.out.print(question.getQuestion() + ": ");
                String answer = IOUtils.readStr();
                
                QuesAndAnswerDetails questionDetails = new QuesAndAnswerDetails();
                questionDetails.setQuestion(question.getQuestion());
                questionDetails.setAnswer(answer);
                questionDetails.setMetaData(question.getMetaData());
                questionDetails.setAnswerFieldType(question.getQuestionFieldType());
                questionDetails.setIsRequired(question.getIsRequired());
                questionDetails.setQuestionFieldType(question.getQuestionFieldType());
                
                qaDetails.add(questionDetails);
            } else if(qna instanceof SingleQuesMultiAnswerOptionsValues) {
                SingleQuesMultiAnswerOptionsValues question =(SingleQuesMultiAnswerOptionsValues) qna;
                System.out.print(question.getQuestion() + ": ");
                String[] answers = question.getAnswerOptions().getElements();
                
                for(int i=0; i;>