# Android SDK

# Getting started

Latest release: Version 1.13.5 (Changelog)

# Requirements

# Resources

# Installation

It's always a good idea to usе the latest version

Add the SumSub maven repository to the repositories section in your build.gradle file:

repositories {
  maven { url "https://maven.sumsub.com/repository/maven-public/" }
}

Add the following dependencies to your build.gradle file:

dependencies {
    // SumSub core
    implementation "com.sumsub.sns:idensic-mobile-sdk:$latestVersion"

    // optional
    // if you want to use Liveness3D module
    implementation "com.sumsub.sns:idensic-mobile-sdk-liveness3d:$latestVersion"
}

# Basic Usage

Make sure you complete the Backend routines before initializing the SDK

# Initialization

Kotlin example

// NOTE: make sure to change to 'https://api.sumsub.com' when going live
val apiUrl = "https://test-api.sumsub.com"

// use the 'accessToken' that you generated on your backend
val accessToken = "..."
val flowName = "msdk-basic-kyc" // the name of the applicant flow (must be set up via the dashboard)
val modules = listOf(SNSLiveness3d(), SNSProoface())

val tokenExpirationHandler = object : TokenExpirationHandler {
    override fun onTokenExpired(): String? {
        // Access token expired
        // get a new one and pass it to the callback to re-initiate the SDK
        val newToken = "..." // get a new token from your backend
        return newToken
    }
}

val snsSdk = SNSMobileSDK.Builder(this, apiUrl, flowName)
    .withAccessToken(accessToken, onTokenExpiration = tokenExpirationHandler)
    .withModules(modules)
    .withLocale(Locale("en"))
    .build()

snsSdk.launch()

Java example


// NOTE: make sure to change to 'https://api.sumsub.com' when going live
String apiUrl = "https://test-api.sumsub.com";

// use the 'accessToken' that you generated on your backend
String accessToken = "...";

String flowName = "msdk-basic-kyc"; // the name of the applicant flow (must be set up via the dashboard)
List<SNSModule> modules = Arrays.asList(new SNSLiveness3d(), new SNSProoface());

TokenExpirationHandler tokenUpdater = () -> {
        // Access token expired
        // get a new one and pass it to the callback to re-initiate the SDK
        String newAccessToken = "..."; // get a new token from your backend
        return newAccessToken;
    };

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction)
        .withAccessToken(accessToken, tokenUpdater)
        .withModules(modules)
        .withLocale(new Locale("en"))
        .build();

If you don't intend to use the liveness module just make sure that the modules are set as an empty list

# Handlers

# Token Expiration Handler

Because of the limited lifespan of the accessToken, it's important to handle the situation correctly when the token expires and needs to be refreshed. In order to do this provide tokenHandler for the SDK Builder. The handler should make a call to your backend, obtain a new access token and then pass it back to the SDK by returning its value.

val tokenHandler = object : TokenExpirationHandler {
    override fun onTokenExpired(): String? {
        val newToken = ....
        return newToken
    }
}
TokenExpirationHandler tokenUpdater = () -> {
        String newAccessToken = "...";
        return newAccessToken;
    };

onTokenExpired is called on a non-UI thread

# Callbacks

# On SDK State changes

Use withHandlers(onStateChanged=onSDKStateChangedHandler) in the SDK builder to get notified about changed in the flow of the verification process. The callback takes two parameters:

  • newState is the current SDK State
  • prevState is the previous state value

The following example lists all the possible states:

val onSDKStateChangedHandler: (SNSSDKState, SNSSDKState) -> Unit = { newState, prevState ->
    Timber.d("onSDKStateChangedHandler: $prevState -> $newState")

    when (newState) {
        is SNSSDKState.Ready -> Timber.d("SDK is ready")
        is SNSSDKState.Failed -> {
            when (newState) {
                is SNSSDKState.Failed.ApplicantNotFound -> Timber.e(newState.message)
                is SNSSDKState.Failed.ApplicantMisconfigured -> Timber.e(newState.message)
                is SNSSDKState.Failed.InitialLoadingFailed -> Timber.e(newState.exception,"Initial loading error")
                is SNSSDKState.Failed.InvalidParameters -> Timber.e(newState.message)
                is SNSSDKState.Failed.NetworkError -> Timber.e(newState.exception,newState.message)
                is SNSSDKState.Failed.Unauthorized -> Timber.e(newState.exception,"Invalid token or a token can't be refreshed by the SDK. Please, check your token expiration handler")
                is SNSSDKState.Failed.Unknown -> Timber.e(newState.exception, "Unknown error")
            }
        }
        is SNSSDKState.Initial -> Timber.d("No verification steps are passed yet")
        is SNSSDKState.Incomplete -> Timber.d("Some but not all verification steps are passed over")
        is SNSSDKState.Pending -> Timber.d("Verification is in pending state")
        is SNSSDKState.FinallyRejected -> Timber.d("Applicant has been finally rejected")
        is SNSSDKState.TemporarilyDeclined -> Timber.d("Applicant has been declined temporarily")
        is SNSSDKState.Approved -> Timber.d("Applicant has been approved")
    }
}

val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl).withHandlers(onStateChanged = onSDKStateChangedHandler)
Function2<SNSSDKState, SNSSDKState, Unit> onStateChanged = (newState, prevState) -> {
    Timber.d("The SDK state was changed: " + prevState + " -> " + newState);

    if (newState instanceof SNSSDKState.Ready) {
        Timber.d("SDK is ready");
    } else if (newState instanceof SNSSDKState.Failed.Unauthorized) {
        Timber.e(((SNSSDKState.Failed.Unauthorized) newState).getException(), "Invalid token or a token can't be refreshed by the SDK. Please, check your token expiration handler");
    } else if (newState instanceof SNSSDKState.Failed.Unknown) {
        Timber.e(((SNSSDKState.Failed.Unknown) newState).getException(), "Unknown error");
    } else if (newState instanceof SNSSDKState.Initial) {
        Timber.d("No verification steps are passed yet");
    } else if (newState instanceof SNSSDKState.Incomplete) {
        Timber.d("Some but not all verification steps are passed over");
    } else if (newState instanceof SNSSDKState.Pending) {
        Timber.d("Verification is in pending state");
    } else if (newState instanceof SNSSDKState.FinallyRejected) {
        Timber.d("Applicant has been finally rejected");
    } else if (newState instanceof SNSSDKState.TemporarilyDeclined) {
        Timber.d("Applicant has been declined temporarily");
    } else if (newState instanceof SNSSDKState.Approved) {
        Timber.d("Applicant has been approved");
    }

    return Unit.INSTANCE;
};

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction).withHandlers(null, onStateChanged, null, null).build();

# Applicant Actions

If you are using Applicant Actions, use additional state for handling action's result

val onSDKStateChangedHandler: (SNSSDKState, SNSSDKState) -> Unit = { newState, prevState ->
    Timber.d("onSDKStateChangedHandler: $prevState -> $newState")

    // Flow action has been completed
    if (newState is SNSSDKState.ActionCompleted) {
        val actionId = newState.actionId
        val type = newState.type
        val answer = newState.answer
        val payload = newState.payload
    }
}
Function2<SNSSDKState, SNSSDKState, Unit> onStateChanged = (newState, prevState) -> {
    Timber.d("The SDK state was changed: " + prevState + " -> " + newState);

    if (newState instanceof SNSSDKState.ActionCompleted) {
        SNSSDKState.ActionCompleted actionState = (SNSSDKState.ActionCompleted) newState;
        String actionId = actionState.getActionId();
        FlowActionType type = actionState.getType();
        AnswerType answer = actionState.getAnswer();
        Map<String, Object> payload = actionState.getPayload();
    }

    return Unit.INSTANCE;
};

# On SDK Errors

Use withHandlers(onError=onSDKErrorHandler) in the SDK builder to know about errors that occur within the SDK. Refer to the example below to see how:

val onSDKErrorHandler: (SNSException) -> Unit = { exception ->
    Timber.d("The SDK throws an exception. Exception: $exception")

    when (exception) {
        is SNSException.Api -> Timber.d("Api exception. ${exception.description}")
        is SNSException.Network -> Timber.d(exception, "Network exception.")
        is SNSException.Unknown -> Timber.d(exception, "Unknown exception.")
    }
}

val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl).withHandlers(onError = onSDKErrorHandler)
Function1<SNSException, Unit> onError = e -> {
    Timber.d("The SDK throws an exception. Exception: %s", e);

    if (e instanceof SNSException.Api) {
        Timber.d("Api exception. %s", ((SNSException.Api) e).getDescription());
    } else if (e instanceof SNSException.Network) {
        Timber.d(e, "Network exception.");
    } else if (e instanceof SNSException.Unknown) {
        Timber.d(e, "Unknown exception.");
    }

    return Unit.INSTANCE;
};

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction).withHandlers(onError, null, null, null).build();

# On SDK completion

An optional callback to get notified when the SDK is closed:

val onSDKCompletedHandler: (SNSCompletionResult, SNSSDKState) -> Unit = { (result, state) ->
    when (result) {
        is SNSCompletionResult.SuccessTermination -> Timber.d("The SDK finished successfully")
        is SNSCompletionResult.AbnormalTermination -> Timber.e(result.exception, "The SDK got closed because of errors")
    }
}

val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl).withHandlers(onCompleted = onSDKCompletedHandler)
Function2<SNSCompletionResult, SNSSDKState, Unit> onSDKCompletedHandler = (result, state) -> {
    Timber.d("The SDK is finished. Result: " + result + " , State: " + state);
    Toast.makeText(requireContext(), "The SDK is finished. Result: $result, State: $state", Toast.LENGTH_SHORT).show();

    if (result instanceof SNSCompletionResult.SuccessTermination) {
        Timber.d(result.toString());
    } else if (result instanceof SNSCompletionResult.AbnormalTermination) {
        Timber.d(((SNSCompletionResult.AbnormalTermination) result).getException());
    }
    return Unit.INSTANCE;
};

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction).withHandlers(null, null, onSDKCompletedHandler, null).build();

The callback takes two parameters:

  • result:
    • SNSCompletionResult.SuccessTermination - A user clicks on the cancel button.
    • SNSCompletionResult.AbnormalTermination - an error occurred. Look at the exception object if you want to get more information
  • state: The state at which the SDK was closed. For possible states refer to the following Section

# On Action Result

An optional handler for getting liveness result and controlling action scenario.

The handler takes two parameters: -actionId : String - Action ID -answer : String - Liveness module answer. Possible values: "GREEN", "YELLOW", "RED", "ERROR" or null

The handler must return SNSActionResult. Currently the following values supported: -SNSActionResult.Continue - continue default action scenario (show result screen etc.) -SNSActionResult.Cancel - cancel default action scenario (close sdk without result screen)

val onActionResultHandler: (String, String?) -> SNSActionResult = { actionId, answer ->
    Timber.d("Action Result: actionId: $actionId answer: $answer")
    // use default scenario
    SNSActionResult.Continue
}

val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl).withHandlers(onActionResult = onActionResultHandler)    
Function2<String, String, SNSActionResult> onActionResult = (actionId, answer) -> {
    Timber.d("Action Result: actionId: " + actionId + ", answer: " + answer);
    return SNSActionResult.Continue;
};

SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction).withHandlers(null, null, null, onActionResult).build();

# Debugging

# Logs

If you want to see the logs within the SDK, please, use the following code:

// default is 'false'
val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl, flowName).withDebug(true)
SNSMobileSDK.SDK snsSdk = new SNSMobileSDK.Builder(requireActivity(), apiUrl, flowOrAction).withDebug(true);

Don't forget to disable the flag for the release build.

# Custom Logger

By default the SDK uses Timber for logging purposes. If, for some reason, it doesn't work or you want to implement your own tree for Timber, you should extend from SNSLogTree and implement the log function, for example:

class CustomTree: SNSLogTree() {
    override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
        Log.println(priority, tag, message)
    }
}

Right now you can use your custom log tree:

val snsSdkBuilder = SNSMobileSDK.Builder(this, apiUrl).withLogTree(CustomTree())

# Customization

The SDK can be customized: you can change styles, colors, typefaces, etc.

# Localization

.withLocale(Locale("en")) gives you ability to switch between locales on initialization You can customize or localize the texts used within the SDK through the MSDK Translations tool in the dashboard.

# Action Flow

In order to run the SDK in applicant action mode, you need to create an applicant flow of Applicant actions type in the dashboard and specify its name as the flowName initialization parameter. Also, it'll be required to make an Access Token not only with the userId parameter, but with the externalActionId one as well.

Aside from the notes above, you manage the sdk the same way that you do with regular flows, the only difference is in how you get the action's result.

Last Updated: 11/19/2020, 9:17:24 AM