public class SaltEdgeSDK {

    private static SaltEdgeSDK instance;
    private Context applicationContext;
    private String appId;
    private String appSecret;
    private String returnToUrl;
    private boolean loggingEnabled;

    public static SaltEdgeSDK getInstance() {
        if (instance == null) {
            instance = new SaltEdgeSDK();
        }
        return instance;
    }

    public String getAppId() {
        return appId;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public static String getReturnToUrl() {
        return getInstance().returnToUrl;
    }

    public static void setReturnToUrl(String returnToUrl) {
        getInstance().returnToUrl = returnToUrl;
    }

    public static boolean isLoggingEnabled() {
        return getInstance().loggingEnabled;
    }

    public Context getContext() {
        return applicationContext;
    }

    /**
     * Initialize Salt Edge SDK for access to Salt Edge API
     *
     * @param applicationContext Application context
     * @param clientAppId unique app id
     * @param clientAppSecret unique ap secret
     * @param returnToUrl the URL the user will be redirected to. The return_to URL should not exceed 2040 characters.
     */
    public void init(Context applicationContext,
                     String clientAppId,
                     String clientAppSecret,
                     String returnToUrl
    ) {
        init(applicationContext, clientAppId, clientAppSecret, returnToUrl, false);
    }

    /**
     * Initialize Salt Edge SDK for access to Salt Edge API
     *
     * @param applicationContext Application context
     * @param clientAppId unique app id
     * @param clientAppSecret unique ap secret
     * @param enableLogging flag, which enable or disable network logging
     * @param returnToUrl the URL the user will be redirected to. The return_to URL should not exceed 2040 characters.
     */
    public void init(
            Context applicationContext,
            String clientAppId,
            String clientAppSecret,
            String returnToUrl,
            boolean enableLogging
    ) {
        init(applicationContext, clientAppId, clientAppSecret, returnToUrl, false, enableLogging);
    }

    private void init(Context applicationContext,
                      String clientAppId,
                      String clientAppSecret,
                      String returnToUrl,
                      boolean actAsPartner,
                      boolean enableLogging
    ) {
        if (clientAppId.isEmpty()) {
            throw new RuntimeException(SEConstants.ERROR_CLIENT_APP_ID_IS_NULL);
        }
        if (clientAppSecret.isEmpty()) {
            throw new RuntimeException(SEConstants.ERROR_CLIENT_APP_SECRET_IS_NULL);
        }
        this.applicationContext = applicationContext;
        this.appId = clientAppId;
        this.appSecret = clientAppSecret;
        this.loggingEnabled = enableLogging;
        this.returnToUrl = returnToUrl;
    }

    public static void printToLogcat(String tag, String message) {
        if (isLoggingEnabled()) {
            Log.d(tag, message);
        }
    }
} public interface ConnectSessionResult {
    /**
     * Callback method is invoked when Create Connect Session operation finished with success
     *
     * @param connectUrl String which should be opened in WebView to create connection
     */
    void onSuccess(String connectUrl);

    /**
     * Callback method is invoked when Create Connect Session operation finished with error
     *
     * @param errorMessage String which describes occurred error
     */
    void onFailure(String errorMessage);
} public class SEAttempt extends BaseModel implements Serializable {

    /**
     * Attempt fetch scopes constants
     *
     * @see <a href="https://docs.saltedge.com/account_information/v5/#transactions-attributes">Transaction Attributes</a>
     */
    public final static String FETCH_SCOPE_HOLDER_INFO = "holder_info";
    public final static String FETCH_SCOPE_ACCOUNTS = "accounts";
    public final static String FETCH_SCOPE_TRANSACTIONS = "transactions";

    @SerializedName(SEConstants.KEY_API_MODE)
    private String apiMode;

    @SerializedName(SEConstants.KEY_API_VERSION)
    private String apiVersion;

    @SerializedName(SEConstants.KEY_AUTOMATIC_FETCH)
    private Boolean automaticFetch;

    @SerializedName(SEConstants.KEY_DAILY_REFRESH)
    private Boolean dailyRefresh;

    @SerializedName(SEConstants.KEY_CATEGORIZATION)
    private String categorization;

    @SerializedName(SEConstants.KEY_DEVICE_TYPE)
    private String deviceType;

    @SerializedName(SEConstants.KEY_REMOTE_IP)
    private String remoteIp;

    @SerializedName(SEConstants.KEY_EXCLUDE_ACCOUNTS)
    private String[] excludeAccounts;

    @SerializedName(SEConstants.KEY_IDENTIFY_MERCHANT)
    private Boolean identifyMerchant;

    @SerializedName(SEConstants.KEY_USER_PRESENT)
    private Boolean userPresent;

    @SerializedName(SEConstants.KEY_FAIL_AT)
    private String failAt;

    @SerializedName(SEConstants.KEY_FAIL_ERROR_CLASS)
    private String failErrorClass;

    @SerializedName(SEConstants.KEY_FAIL_MESSAGE)
    private String failMessage;

    @SerializedName(SEConstants.KEY_FETCH_SCOPES)
    private String[] fetchScopes;

    @SerializedName(SEConstants.KEY_FINISHED)
    private Boolean finished;

    @SerializedName(SEConstants.KEY_FINISHED_RECENT)
    private Boolean finishedRecent;

    @SerializedName(SEConstants.KEY_FROM_DATE)
    private String fromDate;

    @SerializedName(SEConstants.KEY_INTERACTIVE)
    private Boolean interactive;

    @SerializedName(SEConstants.KEY_LOCALE)
    private String locale;

    @SerializedName(SEConstants.KEY_PARTIAL)
    private Boolean partial;

    @SerializedName(SEConstants.KEY_STORE_CREDENTIALS)
    private Boolean storeCredentials;

    @SerializedName(SEConstants.KEY_SUCCESS_AT)
    private String successAt;

    @SerializedName(SEConstants.KEY_TO_DATE)
    private String toDate;

    @SerializedName(SEConstants.KEY_SHOW_CONSENT_CONFIRMATION)
    private Boolean showConsentConfirmation;

    @SerializedName(SEConstants.KEY_CONSENT_TYPES)
    private String[] consentTypes;

    @SerializedName(SEConstants.KEY_CONSENT_PERIOD_DAYS)
    private Integer consentPeriodDays;

    @SerializedName(SEConstants.KEY_CONSENT_GIVEN_AT)
    private String consentGivenAt;

    @SerializedName(SEConstants.KEY_CONSENT_EXPIRES_AT)
    private String consentExpiresAt;

    @SerializedName(SEConstants.KEY_STAGES)
    private SEStage[] stages;

    @SerializedName(SEConstants.KEY_LAST_STAGE)
    private SEStage lastStage;

    @SerializedName(SEConstants.KEY_RETURN_TO)
    private String returnToUrl;

// CONSTRUCTOR

    public SEAttempt(String locale, String returnToUrl) {
        this.locale = locale;
        this.returnToUrl = returnToUrl;
    }

// GETTER AND SETTERS

    public String getApiMode() {
        return apiMode;
    }

    public void setApiMode(String apiMode) {
        this.apiMode = apiMode;
    }

    public String getApiVersion() {
        return apiVersion;
    }

    public void setApiVersion(String apiVersion) {
        this.apiVersion = apiVersion;
    }

    public Boolean getAutomaticFetch() {
        return automaticFetch;
    }

    public void setAutomaticFetch(Boolean automaticFetch) {
        this.automaticFetch = automaticFetch;
    }

    public Boolean getDailyRefresh() {
        return dailyRefresh;
    }

    public void setDailyRefresh(Boolean dailyRefresh) {
        this.dailyRefresh = dailyRefresh;
    }

    public String getCategorization() {
        return categorization;
    }

    public void setCategorization(String categorization) {
        this.categorization = categorization;
    }

    public String getDeviceType() {
        return deviceType;
    }

    public void setDeviceType(String deviceType) {
        this.deviceType = deviceType;
    }

    public String getRemoteIp() {
        return remoteIp;
    }

    public void setRemoteIp(String remoteIp) {
        this.remoteIp = remoteIp;
    }

    public String[] getExcludeAccounts() {
        return excludeAccounts;
    }

    public void setExcludeAccounts(String[] excludeAccounts) {
        this.excludeAccounts = excludeAccounts;
    }

    public Boolean getIdentifyMerchant() {
        return identifyMerchant;
    }

    public void setIdentifyMerchant(Boolean identifyMerchant) {
        this.identifyMerchant = identifyMerchant;
    }

    public Boolean getUserPresent() {
        return userPresent;
    }

    public void setUserPresent(Boolean userPresent) {
        this.userPresent = userPresent;
    }

    public String getFailAt() {
        return failAt;
    }

    public void setFailAt(String failAt) {
        this.failAt = failAt;
    }

    public String getFailErrorClass() {
        return failErrorClass;
    }

    public void setFailErrorClass(String failErrorClass) {
        this.failErrorClass = failErrorClass;
    }

    public String getFailMessage() {
        return failMessage;
    }

    public void setFailMessage(String failMessage) {
        this.failMessage = failMessage;
    }

    public String[] getFetchScopes() {
        return fetchScopes;
    }

    public void setFetchScopes(String[] fetchScopes) {
        this.fetchScopes = fetchScopes;
    }

    public Boolean getFinished() {
        return finished;
    }

    public void setFinished(Boolean finished) {
        this.finished = finished;
    }

    public Boolean getFinishedRecent() {
        return finishedRecent;
    }

    public void setFinishedRecent(Boolean finishedRecent) {
        this.finishedRecent = finishedRecent;
    }

    public String getFromDate() {
        return fromDate;
    }

    public void setFromDate(String fromDate) {
        this.fromDate = fromDate;
    }

    public Boolean getInteractive() {
        return interactive;
    }

    public void setInteractive(Boolean interactive) {
        this.interactive = interactive;
    }

    public String getLocale() {
        return locale;
    }

    public void setLocale(String locale) {
        this.locale = locale;
    }

    public Boolean getPartial() {
        return partial;
    }

    public void setPartial(Boolean partial) {
        this.partial = partial;
    }

    public Boolean getStoreCredentials() {
        return storeCredentials;
    }

    public void setStoreCredentials(Boolean storeCredentials) {
        this.storeCredentials = storeCredentials;
    }

    public String getSuccessAt() {
        return successAt;
    }

    public void setSuccessAt(String successAt) {
        this.successAt = successAt;
    }

    public String getToDate() {
        return toDate;
    }

    public void setToDate(String toDate) {
        this.toDate = toDate;
    }

    public Boolean getShowConsentConfirmation() {
        return showConsentConfirmation;
    }

    public void setShowConsentConfirmation(Boolean showConsentConfirmation) {
        this.showConsentConfirmation = showConsentConfirmation;
    }

    public String[] getConsentTypes() {
        return consentTypes;
    }

    public void setConsentTypes(String[] consentTypes) {
        this.consentTypes = consentTypes;
    }

    public Integer getConsentPeriodDays() {
        return consentPeriodDays;
    }

    public void setConsentPeriodDays(Integer consentPeriodDays) {
        this.consentPeriodDays = consentPeriodDays;
    }

    public String getConsentGivenAt() {
        return consentGivenAt;
    }

    public void setConsentGivenAt(String consentGivenAt) {
        this.consentGivenAt = consentGivenAt;
    }

    public String getConsentExpiresAt() {
        return consentExpiresAt;
    }

    public void setConsentExpiresAt(String consentExpiresAt) {
        this.consentExpiresAt = consentExpiresAt;
    }

    public SEStage[] getStages() {
        return stages;
    }

    public void setStages(SEStage[] stages) {
        this.stages = stages;
    }

    public SEStage getLastStage() {
        return lastStage;
    }

    public void setLastStage(SEStage lastStage) {
        this.lastStage = lastStage;
    }

    public String getReturnToUrl() {
        return returnToUrl;
    }

    public void setReturnToUrl(String returnToUrl) {
        this.returnToUrl = returnToUrl;
    }
} public class SEConsent extends BaseModel implements Serializable {

    /**
     * Consent scopes constants
     */
    public final static String SCOPE_HOLDER_INFORMATION = "holder_information";
    public final static String SCOPE_ACCOUNT_DETAILS = "account_details";
    public final static String SCOPE_TRANSACTIONS_DETAILS = "transactions_details";

    /**
     * Consent Status possible values
     */
    public enum STATUS {
        ACTIVE, REVOKED
    }

    @SerializedName(SEConstants.KEY_CONNECTION_ID)
    private String connectionId;

    @SerializedName(SEConstants.KEY_CUSTOMER_ID)
    private String customerId;

    @SerializedName(SEConstants.KEY_STATUS)
    private String status;

    @SerializedName(SEConstants.KEY_SCOPES)
    private String[] scopes;

    @SerializedName(SEConstants.KEY_FROM_DATE)
    private String fromDate;

    @SerializedName(SEConstants.KEY_TO_DATE)
    private String toDate;

    @SerializedName(SEConstants.KEY_PERIOD_DAYS)
    private Integer periodDays;

    @SerializedName(SEConstants.KEY_EXPIRES_AT)
    private String expiresAt;

    @SerializedName(SEConstants.KEY_REVOKED_BY)
    private String revokedBy;

    @SerializedName(SEConstants.KEY_REVOKED_AT)
    private String revokedAt;

    @SerializedName(SEConstants.KEY_COLLECTED_BY)
    private String collectedBy;

// CONSTRUCTOR

    public SEConsent(String[] scopes) {
        this.scopes = scopes;
    }

// GETTER AND SETTERS

    public String[] getScopes() {
        return scopes;
    }

    public void setScopes(String[] scopes) {
        this.scopes = scopes;
    }

    public String getFromDate() {
        return fromDate;
    }

    public void setFromDate(String fromDate) {
        this.fromDate = fromDate;
    }

    public String getToDate() {
        return toDate;
    }

    public void setToDate(String toDate) {
        this.toDate = toDate;
    }

    public Integer getPeriodDays() {
        return periodDays;
    }

    public void setPeriodDays(Integer periodDays) {
        this.periodDays = periodDays;
    }

    public String getExpiresAt() {
        return expiresAt;
    }

    public void setExpiresAt(String expiresAt) {
        this.expiresAt = expiresAt;
    }

    public String getConnectionId() {
        return connectionId;
    }

    public void setConnectionId(String connectionId) {
        this.connectionId = connectionId;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getRevokedAt() {
        return revokedAt;
    }

    public void setRevokedAt(String revokedAt) {
        this.revokedAt = revokedAt;
    }

    public String getCollectedBy() {
        return collectedBy;
    }

    public void setCollectedBy(String collectedBy) {
        this.collectedBy = collectedBy;
    }

    public String getStatus() {
        return status;
    }

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

    public String getRevokedBy() {
        return revokedBy;
    }

    public void setRevokedBy(String revokedBy) {
        this.revokedBy = revokedBy;
    }
} public class ConnectSessionRequest {

    @SerializedName(SEConstants.KEY_DATA)
    private ConnectSessionData data;

    public ConnectSessionRequest(
            SEConsent consent,
            SEAttempt attempt,
            String[] allowedCountries,
            String providerCode,
            String javascriptCallbackType,
            String overrideCredentialsStrategy
    ) {
        data = new ConnectSessionData();
        data.consent = consent;
        data.attempt = attempt;
        data.allowedCountries = allowedCountries;
        data.providerCode = providerCode;
        data.javascriptCallbackType = javascriptCallbackType;
        data.overrideCredentialsStrategy = overrideCredentialsStrategy;
        data.disableProviderSearch = providerCode != null && !providerCode.isEmpty();
    }

    private class ConnectSessionData {

        @SerializedName(SEConstants.KEY_CONSENT)
        SEConsent consent;

        @SerializedName(SEConstants.KEY_ATTEMPT)
        SEAttempt attempt;

        @SerializedName(SEConstants.KEY_ALLOWED_COUNTRIES)
        String[] allowedCountries;

        @SerializedName(SEConstants.KEY_PROVIDER_CODE)
        String providerCode;

        @SerializedName(SEConstants.KEY_JAVASCRIPT_CALLBACK_TYPE)
        String javascriptCallbackType;

        @SerializedName(SEConstants.KEY_OVERRIDE_CREDENTIALS_STRATEGY)
        String overrideCredentialsStrategy;

        @SerializedName(SEConstants.KEY_DISABLE_PROVIDER_SEARCH)
        Boolean disableProviderSearch;
    }
} public class ConnectOAuthSessionResponse {

    @SerializedName(SEConstants.KEY_DATA)
    private SECreateConnectOAuthSessionData data;

    public SECreateConnectOAuthSessionData getData() {
        return data;
    }

    public String getRedirectUrl() {
        return data.redirectUrl;
    }

    public String getExpiresAt() {
        return data.expiresAt;
    }

    public String getConnectionId() {
        return data.connectionId;
    }

    public String getAttemptId() {
        return data.attemptId;
    }

    public String getToken() {
        return data.token;
    }

    class SECreateConnectOAuthSessionData {

        @SerializedName(SEConstants.KEY_REDIRECT_URL)
        public String redirectUrl;

        @SerializedName(SEConstants.KEY_EXPIRES_AT)
        public String expiresAt;

        @SerializedName(SEConstants.KEY_CONNECTION_ID)
        public String connectionId;

        @SerializedName(SEConstants.KEY_ATTEMPT_ID)
        public String attemptId;

        @SerializedName(SEConstants.KEY_TOKEN)
        public String token;
    }
} public class SERestClient {

    private SERestClient() {}

    private static final String TAG = "SERestClient";

    public SEApiInterface service = createRetrofit().create(SEApiInterface.class);
    public static Gson gson = createDefaultGson();
    private static SERestClient instance;

    public static SERestClient getInstance() {
        if (instance == null) {
            instance = new SERestClient();
        }
        return instance;
    }

    public void initService() {
        service = createRetrofit().create(SEApiInterface.class);
    }

    private Retrofit createRetrofit() {
        String baseUrl = SEApiConstants.API_BASE_URL + SEApiConstants.BASE_API_PATH;
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(createOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
    }

    private static Gson createDefaultGson() {
        return new GsonBuilder()
                .registerTypeAdapter(JSONObject.class, new ExtraJsonDataAdapter())
                .create();
    }

    private OkHttpClient createOkHttpClient() {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                .addInterceptor(prepareLoggingInterceptor());
        boolean pinsAdded = addCertificatePinner(clientBuilder);
        addSSLSocketFactory(clientBuilder);
        addHeaderInterceptor(clientBuilder, pinsAdded);
        return clientBuilder.build();
    }

    private void addHeaderInterceptor(OkHttpClient.Builder clientBuilder, boolean acceptJson) {
        clientBuilder.addInterceptor(new HeaderInterceptor(acceptJson ? SEApiConstants.MIME_TYPE_JSON : null));
    }

    private boolean addCertificatePinner(OkHttpClient.Builder clientBuilder) {
        try {
            String[] pins = SEPreferencesRepository.getInstance().getPins();
            if (pins != null && pins.length > 0) {
                CertificatePinner.Builder pinnerBuilder = new CertificatePinner.Builder();
                pinnerBuilder.add(SEApiConstants.API_HOST_NAME, pins);
                clientBuilder.certificatePinner(pinnerBuilder.build());
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private void addSSLSocketFactory(OkHttpClient.Builder builder) {
        try {
            TLSSocketFactory factory = new TLSSocketFactory(SaltEdgeSDK.getInstance().getContext());
            builder.sslSocketFactory(factory, factory.getTrustManager());
        } catch (Exception e) {
            Log.e(TAG, "Can't add SSL Socket factory");
            e.printStackTrace();
        }
    }

    private HttpLoggingInterceptor prepareLoggingInterceptor() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(SaltEdgeSDK.isLoggingEnabled()
                ? HttpLoggingInterceptor.Level.BODY
                : HttpLoggingInterceptor.Level.NONE);
        return interceptor;
    }
} public class SEConstants {

    //Base model keys
    public static final String KEY_ID = "id";
    public static final String KEY_CREATED_AT = "created_at";
    public static final String KEY_UPDATED_AT = "updated_at";

    //WebView/Saltbridge keys
    public static final String KEY_STAGE = "stage";
    public static final String KEY_STAGES = "stages";
    public static final String STATUS_FETCHING = "fetching";
    public static final String STATUS_ERROR = "error";
    public static final String STATUS_SUCCESS = "success";
    public static final String KEY_API_STAGE = "api_stage";
    public static final String KEY_CUSTOM_FIELDS = "custom_fields";

    //JSON keys
    public static final String KEY_DATA = "data";
    public static final String KEY_META = "meta";
    public static final String KEY_NEXT_ID = "next_id";
    public static final String KEY_NEXT_PAGE = "next_page";
    public static final String KEY_MESSAGE = "message";
    public static final String KEY_ERROR = "error";
    public static final String KEY_CLASS = "class";
    public static final String KEY_DOCUMENTATION_URL = "documentation_url";
    public static final String KEY_IDENTIFIER = "identifier";
    public static final String KEY_CONNECT_URL = "connect_url";
    public static final String KEY_REDIRECT_URL = "redirect_url";
    public static final String KEY_EXPIRES_AT = "expires_at";
    public static final String KEY_REVOKED_BY = "revoked_by";
    public static final String KEY_REVOKED_AT = "revoked_at";
    public static final String KEY_REMOVED = "removed";
    public static final String KEY_COLLECTED_BY = "collected_by";
    public static final String KEY_FETCH_SCOPES = "fetch_scopes";
    public static final String KEY_SCOPES = "scopes";
    public static final String KEY_TOKEN = "token";

    //String resources
    public static final String LOADING = "Loading";
    public static final String WARNING = "Warning";

    //Providers keys
    public static final String KEY_NAME = "name";
    public static final String KEY_MODE = "mode";
    public static final String KEY_STATUS = "status";
    public static final String KEY_CODE = "code";
    public static final String KEY_CUSTOMER_NOTIFIED_ON_SIGN_IN = "customer_notified_on_sign_in";
    public static final String KEY_AUTOMATIC_FETCH = "automatic_fetch";
    public static final String KEY_INTERACTIVE = "interactive";
    public static final String KEY_IDENTIFICATION_MODE = "identification_mode";
    public static final String KEY_INSTRUCTION = "instruction";
    public static final String KEY_HOME_URL = "home_url";
    public static final String KEY_LOGIN_URL = "login_url";
    public static final String KEY_LOGO_URL = "logo_url";
    public static final String KEY_ALLOWED_COUNTRIES = "allowed_countries";
    public static final String KEY_REFRESH_TIMEOUT = "refresh_timeout";
    public static final String KEY_HOLDER_INFO = "holder_info";
    public static final String KEY_MAX_CONSENT_DAYS = "max_consent_days";
    public static final String KEY_INCLUDE_FAKE_PROVIDERS = "include_fake_providers";

    //Connections keys
    public static final String KEY_SECRET = "secret";
    public static final String KEY_DAILY_REFRESH = "daily_refresh";
    public static final String KEY_FINISHED = "finished";
    public static final String KEY_FINISHED_RECENT = "finished_recent";
    public static final String KEY_PARTIAL = "partial";
    public static final String KEY_CUSTOMER_EMAIL = "customer_email";
    public static final String KEY_PROVIDER_ID = "provider_id";
    public static final String KEY_PROVIDER_CODE = "provider_code";
    public static final String KEY_PROVIDER_NAME = "provider_name";
    public static final String KEY_LAST_FAIL_AT = "last_fail_at";
    public static final String KEY_FAIL = "fail";
    public static final String KEY_FAIL_AT = "fail_at";
    public static final String KEY_LAST_FAIL_MESSAGE = "last_fail_message";
    public static final String KEY_FAIL_MESSAGE = "fail_message";
    public static final String KEY_FAIL_ERROR_CLASS = "fail_error_class";
    public static final String KEY_LAST_REQUEST_AT = "last_request_at";
    public static final String KEY_LAST_SUCCESS_AT = "last_success_at";
    public static final String KEY_SUCCESS_AT = "success_at";
    public static final String KEY_NEXT_REFRESH_POSSIBLE_AT = "next_refresh_possible_at";
    public static final String KEY_STORE_CREDENTIALS = "store_credentials";
    public static final String KEY_SHOW_CONSENT_CONFIRMATION = "show_consent_confirmation";
    public static final String KEY_CONSENT_TYPES = "consent_types";
    public static final String KEY_CONSENT_PERIOD_DAYS = "consent_period_days";
    public static final String KEY_CONSENT_GIVEN_AT = "consent_given_at";
    public static final String KEY_CONSENT_EXPIRES_AT = "consent_expires_at";
    public static final String KEY_LAST_ATTEMPT = "last_attempt";
    public static final String KEY_LAST_STAGE = "last_stage";
    public static final String KEY_CONSENT = "consent";
    public static final String KEY_ATTEMPT = "attempt";
    public static final String KEY_ATTEMPT_ID = "attempt_id";

    //Account keys
    public static final String KEY_NATURE = "nature";
    public static final String KEY_BALANCE = "balance";
    public static final String KEY_CURRENCY_CODE = "currency_code";
    public static final String KEY_EXTRA = "extra";

    //Transaction keys
    public static final String KEY_DUPLICATED = "duplicated";
    public static final String KEY_UNDUPLICATED = "unduplicated";
    public static final String KEY_MADE_ON = "made_on";
    public static final String KEY_AMOUNT = "amount";
    public static final String KEY_DESCRIPTION = "description";
    public static final String KEY_CATEGORY = "category";
    public static final String KEY_ACCOUNT_ID = "account_id";

    //Other keys
    public static final String KEY_CUSTOMER_ID = "customer_id";
    public static final String KEY_CONNECTION_ID = "connection_id";
    public static final String KEY_API_MODE = "api_mode";
    public static final String KEY_API_VERSION = "api_version";
    public static final String KEY_CATEGORIZATION = "categorization";
    public static final String KEY_DEVICE_TYPE = "device_type";
    public static final String KEY_REMOTE_IP = "remote_ip";
    public static final String KEY_EXCLUDE_ACCOUNTS = "exclude_accounts";
    public static final String KEY_IDENTIFY_MERCHANT = "identify_merchant";
    public static final String KEY_USER_PRESENT = "user_present";
    public static final String KEY_INTERACTIVE_HTML = "interactive_html";
    public static final String KEY_INTERACTIVE_FIELDS_NAMES = "interactive_fields_names";
    public static final String KEY_INTERACTIVE_FIELDS_OPTIONS = "interactive_fields_options";
    public static final String KEY_NAMES = "names";
    public static final String KEY_EMAILS = "emails";
    public static final String KEY_PHONE_NUMBERS = "phone_numbers";
    public static final String KEY_ADDRESSES = "addresses";
    public static final String KEY_CITY = "city";
    public static final String KEY_STATE = "state";
    public static final String KEY_STREET = "street";
    public static final String KEY_COUNTRY_CODE = "country_code";
    public static final String KEY_POST_CODE = "post_code";
    public static final String KEY_ENGLISH_NAME = "english_name";
    public static final String KEY_LOCALIZED_NAME = "localized_name";
    public static final String KEY_OPTION_VALUE = "option_value";
    public static final String KEY_SELECTED = "selected";
    public static final String KEY_CREDENTIALS = "credentials";
    public static final String KEY_OVERRIDE_CREDENTIALS_STRATEGY = "override_credentials_strategy";
    public static final String KEY_DISABLE_PROVIDER_SEARCH = "disable_provider_search";
    public static final String KEY_RETURN_TO = "return_to";
    public static final String KEY_LOCALE = "locale";
    public static final String KEY_PROVIDER_MODES = "provider_modes";
    public static final String KEY_FROM_ID = "from_id";
    public static final String KEY_FROM_DATE = "from_date";
    public static final String KEY_TO_DATE = "to_date";
    public static final String KEY_PERIOD_DAYS = "period_days";
    public static final String KEY_CONSENT_ID = "consent_id";
    public static final String KEY_DATE = "date";
    public static final String KEY_QUERY_STRING = "query_string";

    public static final String KEY_JAVASCRIPT_CALLBACK_TYPE = "javascript_callback_type";
    public static final String IFRAME = "iframe";
    public static final String KEY_RATE = "rate";
    public static final String KEY_TRANSACTION_IDS = "transaction_ids";
    public static final String KEY_KEEP_DAYS = "keep_days";
    public static final String KEY_CLEANUP_STARTED = "cleanup_started";

    //ERRORS
    public static final String REQUEST_ERROR = "Request error";
    public static final String ERROR_CANNOT_BE_NULL = "cannot be null";
    public static final String ERROR_COULD_NOT_CONNECT_ACCOUNT = "Could not connect account";
    public static final String ERROR_CLIENT_APP_ID_IS_NULL = "Client App Id cannot be null or empty";
    public static final String ERROR_CLIENT_APP_SECRET_IS_NULL = "Client App Secret cannot be null or empty";
    public static final String ERROR_CUSTOMER_IDENTIFIER_IS_NULL = "Customer identifier cannot be null or empty";
    public static final String ERROR_INVALID_REFRESH_SECRETS = "Invalid refresh secrets";
    public static final String ERROR_URL_EMPTY = "URL is empty";

    //RESULT CODES
    public static final int FILECHOOSER_RESULT_CODE = 666;


} public class SEErrorTools {

    public final static String ERROR_MSG_INVALID_HPKP_DATA = "Invalid HPKP data";
    public final static String ERROR_MSG_CANT_GET_HPKP_DATA = "Can not get HPKP data";
    public final static String ERROR_MSG_SECURITY_REQUEST_ERROR = "Security request error";
    public final static String ERROR_MSG_SSL_CERT_FAIL = "SSL Handshake Error!";
    public final static String ERROR_MSG_HOST_UNREACHABLE = "No Internet connection.Please try again later.";

    /**
     * Returns localized message from exception or custom message
     *
     * @param t Throwable exception
     * @return string value of error message
     */
    public static String processConnectionError(Throwable t) {
        if (isNoConnectionException(t)) return ERROR_MSG_HOST_UNREACHABLE;
        else if (isSSLException(t)) return ERROR_MSG_SSL_CERT_FAIL;
        else return t.getLocalizedMessage();
    }

    private static Boolean isNoConnectionException(Throwable t) {
        return (t instanceof ConnectException)
                || (t instanceof NoRouteToHostException)
                || (t instanceof SocketTimeoutException)
                || (t instanceof SocketException)
                || (t instanceof InterruptedIOException)
                || (t instanceof UnknownHostException)
                || (t instanceof IOException);
    }

    private static Boolean isSSLException(Throwable t) {
        return (t instanceof SSLPeerUnverifiedException)
                || (t instanceof SSLHandshakeException)
                || (t instanceof SSLException);
    }
} public class SEJsonTools {

    /**
     * Extract string, int, boolean, double, array, object, stringToJSON or errorMessage value from JSONObject by key.
     *
     * @param jsonObject
     * @param key
     * @return string value by key from json Object or empty string
     */
    public static String getString(JSONObject jsonObject, String key) {
        String result = "";
        return (String) parse(jsonObject, key, result);
    }

    public static int getInt(JSONObject jsonObject, String key) {
        int result = 0;
        return (int) parse(jsonObject, key, result);
    }

    public static Boolean getBoolean(JSONObject jsonObject, String key) {
        Boolean result = false;
        return (Boolean) parse(jsonObject, key, result);
    }

    public static Double getDouble(JSONObject jsonObject, String key) {
        double result = 0d;
        return (Double) parse(jsonObject, key, result);
    }

    public static JSONArray getJSONArray(JSONObject jsonObject, String key) {
        JSONArray result = new JSONArray();
        return (JSONArray) parse(jsonObject, key, result);
    }

    public static JSONObject getObject(JSONObject jsonObject, String key) {
        JSONObject result = new JSONObject();
        return (JSONObject) parse(jsonObject, key, result);
    }

    public static JSONObject stringToJSON(String string) {
        JSONObject result = new JSONObject();
        if (string == null || string.isEmpty()) return result;
        try {
            return new JSONObject(string);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return new JSONObject();
    }

    public static String getErrorMessage(ResponseBody error) {
        return getErrorMessage(error, SEConstants.REQUEST_ERROR);
    }

    public static String getErrorMessage(ResponseBody error, String defaultMessage) {
        try {
            SEApiError apiError = new Gson().fromJson(error.string(), SEApiError.class);
            String result = (apiError != null) ? apiError.getErrorMessage() : defaultMessage;
            return (result != null) ? result : defaultMessage;
        } catch (Exception e) {
            e.printStackTrace();
            return defaultMessage;
        }
    }

    private static boolean validation(JSONObject jsonObject, String key) {
        return jsonObject == null || key == null || key.isEmpty() || jsonObject.isNull(key);
    }

    private static Object parse(JSONObject jsonObject, String key, Object result) {
        if (validation(jsonObject, key)) {
            return result;
        }
        try {
            if (result.getClass().equals(JSONObject.class)) {
                return jsonObject.getJSONObject(key);
            } else if (result.getClass().equals(String.class)) {
                return jsonObject.getString(key);
            } else if (result.getClass().equals(JSONArray.class)) {
                return jsonObject.getJSONArray(key);
            } else if (result.getClass().equals(Double.class)) {
                return jsonObject.getDouble(key);
            } else if (result.getClass().equals(Boolean.class)) {
                return jsonObject.getBoolean(key);
            } else if (result.getClass().equals(Integer.class)) {
                return jsonObject.getInt(key);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

}
import com.saltedge.sdk.utils.SEJsonTools;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import com.saltedge.sdk.SaltEdgeSDK;
import com.saltedge.sdk.interfaces.ConnectSessionResult;
import com.saltedge.sdk.model.SEAttempt;
import com.saltedge.sdk.model.SEConsent;
import com.saltedge.sdk.model.request.ConnectSessionRequest;
import com.saltedge.sdk.model.response.ConnectOAuthSessionResponse;
import com.saltedge.sdk.network.SERestClient;
import com.saltedge.sdk.utils.SEConstants;
import com.saltedge.sdk.utils.SEErrorTools;
/*
Copyright © 2019 Salt Edge. https://saltedge.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package com.saltedge.sdk.connector;



public class ConnectOAuthConnector extends BasePinnedConnector implements Callback<ConnectOAuthSessionResponse> {

    private final ConnectSessionResult callback;
    private Call<ConnectOAuthSessionResponse> call;

    public ConnectOAuthConnector(ConnectSessionResult callback) {
        this.callback = callback;
    }

    public void createConnectSession(String customerSecret,
                                     String providerCode,
                                     String[] consentScopes,
                                     String localeCode
    ) {
        ConnectSessionRequest requestData = new ConnectSessionRequest(
                new SEConsent(consentScopes),
                new SEAttempt(localeCode, SaltEdgeSDK.getReturnToUrl()),
                new String[0],
                providerCode,
                SEConstants.IFRAME,
                null
        );
        call = SERestClient.getInstance().service.createOAuthConnectSession(customerSecret, requestData);