Android 3DS SDK


Contents

Introduction

This document serves as a guide for the integrator of Ravelin’s Android 3DS SDK, providing details on how the 3DS SDK should be configured and integrated into an Android application.

System requirements and delivery

Ravelin’s Android 3DS SDK supports a minimum Android version Android 7 (API Level 24).

Developed as a standard Android Library, Ravelin provides a single AAR artifact along with the Maven POM file that lists dependencies.

We also include the following.

  • An API token.
  • Environment code.
  • An integration guide.
  • A demo application demonstrating 3DS authentication with and without a challenge. The demonstration app includes showcases of different types of frictionless and challenge flows.

Versioning

The Android 3DS SDK follows semantic versioning - The version can be described as MAJOR.MINOR.PATCH, where:

  • MAJOR - incompatible version changes
  • MINOR - improvements
  • PATCH - bug / security fix

Supporting documentation

The Ravelin 3DS SDK is built and tested according to the following supporting EMV 3DS documentation. Therefore, this guide should be used in conjunction with the following specifications:


Integration

This section provides technical details on how to integrate the Android 3DS SDK into your application. The integration process requires basic knowledge of the gradle build tool.

Importing the SDK

In order to access the Ravelin 3DS SDK, you will first need to add the Ravelin maven repository to the project settings.gradle.kts file or to the project build.gradle.kts file. You may also choose to add the repository to both locations. To add the repository to settings.gradle.kts do the following:


dependencyResolutionManagement {
    repositories {
        /* other repositories */
        maven {
            setUrl("https://maven.ravelin.com/public/repositories/threeds2service/")
        }
    }
}

To add the repository to build.gradle.kts do the following:

allprojects {
    repositories {
        /* other repositories */
        maven {
            setUrl("https://maven.ravelin.com/public/repositories/threeds2service/")
        }
    }
}

Then in the Module level build.gradle.kts add the Ravelin 3DS SDK to the app dependencies:

dependencies {
    /* other dependencies */
    implementation("com.ravelin.threeds2service:threeds2service-sdk:2.0.2")
}

The library will be fetched along with its dependencies as defined in the POM file. These dependencies are listed in Dependencies.


Update the SDK

New versions of the Ravelin 3DS SDK may be released from time to time, and some updates may introduce breaking changes. We recommend reviewing the Release Notes section in this guide for details on the changes included in each release.


Set an environment

This section refers to Configuration parameters.


JDK desugaring

The following configuration is required in order to support Java API desugaring in Android API level 26 or older:

// Add the following to your build.gradle.kts file
compileOptions {
    // Other configurations ...
    isCoreLibraryDesugaringEnabled = true
}

// Add the following dependency to your build.gradle.kts file:
dependencies {
    // Other dependencies
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

Handle application backup rules

The Ravelin 3DS SDK defines backup rules in its Manifest file. In case that backup is enabled, and you have backup rules in your own application manifest file, make sure to merge the 3DS SDK backup rules, otherwise they will be overridden during the Manifest merging process and that may result with an unexpected behaviour.


<application android:dataExtractionRules="@xml/backup_rules_31_and_above"
    android:fullBackupContent="@xml/backup_rules_until_30" tools:targetApi="s" />

Extraction rules for Android API >= 12:


<data-extraction-rules disableIfNoEncryptionCapabilities="false">
    <include domain="sharedpref" path="." />
    <include domain="file" path="." />
    <exclude domain="sharedpref" path="my_secret_shared_prefs.xml" />
    <exclude domain="sharedpref" path="my-3ds-shared-preference.xml" />
    <exclude domain="file" path="datastore/user_pref.pb" />
</data-extraction-rules>

Extraction rules for Android API < 12:

<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
    <cloud-backup disableIfNoEncryptionCapabilities="false">
        <include domain="sharedpref" path="." />
        <include domain="file" path="." />
        <exclude domain="sharedpref" path="my_secret_shared_prefs.xml" />
        <exclude domain="sharedpref" path="my-3ds-shared-preference.xml" />
        <exclude domain="file" path="datastore/user_pref.pb" />
    </cloud-backup>
</data-extraction-rules>

Additional configuration for App bundles

IMPORTANT - The following is applicable only for Ravelin 3DS SDK versions older than v1.5.0. In v1.5.0, in order to keep original ELF segments alignment of the SDK, which is a 16kB alignment, the following configuration needs to be avoided.

The Ravelin 3DS SDK uses a Java native interface (JNI) to communicate with a native library. In case you publish your app as a bundle (aab file), additional configuration is required to avoid receiving the following error: java.lang.UnsatisfiedLinkError. This error happens when users try to side-load the app with an apk file which is not compatible with their device architecture. To avoid such an error, add the following configuration to the android extension in your application module build.gradle.kts file :

packaging {
    jniLibs {
        useLegacyPackaging = true
    }
}

Card schemes configuration

The Ravelin 3DS SDK provides an authentication service for payment transactions which is supported by different Card schemes. Each card scheme provides a public key for device data encryption, a keychain to verify the authenticity of signed certificates that arrive from the ACS (Access Control Server) and the Card scheme logos. In order to comply with the PCI 3DS SDK 1.1v requirements, these values cannot be bundled with the SDK. In order to protect this data, the Ravelin 3DS SDK fetches the data from a remote host over a secured TLS link protected by an API token. The metadata downloaded from the host is stored in an encrypted storage.

The following list consists of card schemes, Ravelin 3DS SDK is certified with:

Card SchemeDirectory Server ID
VisaA000000003
MastercardA000000004
JCB (Japan Credit Bureau)A000000065
CB (Cartes Bancaires)A000000042
Diners Club/DiscoverA000000152
Amex (American Express)A000000025
UnionPayA000000333
EftposA000000384

The integrator needs to set the directory server ID parameter when calling the API for creation of transactions with an argument matching a value from the table above.


Access the SDK

A copy of the SDK object is generated when calling the get() function from the ThreeDS2ServiceInstance class companion object. This helps to ensure that only a single copy of the SDK class is generated during the lifecycle of the application. The following code snippet is an example of how to access this object:

fun provides3dsSdk(): ThreeDS2Service = ThreeDS2ServiceInstance.get()

Initialize the SDK

The SDK needs to be initialized, exactly one time throughout the lifecycle of the application, before it can be used. The SDK initialisation function initialize is a suspending function which means it must be called from a coroutine or another suspending function.
Note: If you’re unfamiliar with coroutines, please refer to the official documentation in Coroutines.
In the following example, the SDK is being initialized from a view model using the view model scope.

protected var coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
    Log.e(
        TAG,
        "coroutine terminated with an error: ${exception.message}"
    )
    // Handle the error...
}
fun initSdk() =
    viewModelScope.launch(coroutineContext + coroutineExceptionHandler) {
        val warnings = threeDS2Service.initialize(
            application,
            configParameters,
            locale,
            uiCustomizationMap,
            this
        )
    }

In this example, we use a lifecycle-aware coroutine scope (viewModelScope). However, your application may instead use an unmanaged CoroutineScope. In such cases, it is recommended to attach a SupervisorJob to improve control over exceptions and cancellation of child coroutines. The coroutineExceptionHandler is of importance for catching exceptions from child coroutines, including coroutines which are created inside the 3DS SDK.

Parameters:

ParameterTypeDescription
applicationContextThe application context
configParametersConfigParameters?See Configuration parameters
localeString?Provides information about the default Locality*
uiCustomizationMapHashMap<UiCustomizationType, UiCustomization>?See Challenge UI customization
coroutineScopeCoroutineScope?Scope of the coroutine calling this suspend function

(*) Please provide a String in the following format:

"${language}-${country}"

The API

This section describes the Android 3DS API.

Initialisation warnings

Calling the function initialize on a threeDS2Service object returns a list of initialisation warnings (see Initialise the SDK and Warnings). These warnings can also be retrieved later by calling the getWarnings function on the same object. if getWarnings is called before the SDK has been initialized first, the SDK will throw an SDKNotInitializedException.

Function signature:

@Throws(SDKNotInitializedException::class)
fun getWarnings(): List<Warning?>

SDK version

Call this function to retrieve the version of the SDK. When called before initialising the SDK, it throws an SDKNotInitializedException exception.

Function signature:

@Throws(
    SDKNotInitializedException::class,
    SDKRuntimeException::class
)
fun getSDKVersion(): String

Supported protocols

Call this function to retrieve a list of supported protocol versions by the SDK. The list should be used together with the following sources to determine the right protocol the application should use in the authentication request:

  1. version request API.
  2. The application version.
  3. Some knowledge base about which protocol is supported by which application versions (for older versions of the application).

For example, the version API may return versionRecommendation equal to 2.3.1 while the current version of the application supports only 2.2.0. In that case, the integrator should use 2.2.0 as the protocol version for the authentication request.
Another example: The application supports both 2.2.0 and 2.3.1 protocols while the version API recommendation is to use 2.2.0 because the Card scheme or the issuer don’t yet support 2.3.1.

The following diagram may help in explaining this logic:

Protocol selection

Function signature:

@Throws(SDKNotInitializedException::class)
fun getSupportedVersions(): List<String>?

Configuration parameters

An interface which defines a set of functions for initialising the SDK. To generate an instance of ConfigParameters, one should use the ConfigParametersBuilder. The ConfigParametersBuilder consists of the following function members:

The function setEnvironment

The environment is a string value provided to the integrator during the integration stage. The integrator must pass this value to the setEnvironment function. It ensures that the SDK is configured for the correct environment. Please make sure you have this value before working with the API.

Function signature:

fun setEnvironment(environment: String)

Parameters:

ParameterTypeDescriptionDefault value when not set
environmentStringDesignated environment given to the user during the integration stage.SDKRuntimeException is thrown when the environment is not set

The function setApiToken

This is the token extracted from your CID page at the Ravelin dashboard. You should be setting the publishable key. Without this key, the SDK can’t interact with the server.

Function signature:

fun setApiToken(apiToken: String)

Parameters:

ParameterTypeDescriptionDefault value when not set
apiTokenStringPublishable key used by the SDK to fetch metadataEmpty String

The function setAppVersion

The version of the installed application. This field is essential for us to be able to monitor the app.

Function signature:

fun setAppVersion(appVersion: String)

Parameters:

ParameterTypeDescriptionDefault value when not set
appVersionStringThe version of the installed applicationEmpty String

The function setOobUniversalAppLinkSupport

This parameter indicates whether the requestor application supports universal links for Out-of-Band challenges or not. For more details about how to use app links in Out-of-Band challenges see Requirements for supporting universal app links.

Function signature:

fun setOobUniversalAppLinkSupport(oobUniversalAppLinkSupported: Boolean)

Parameters:

ParameterTypeDescriptionDefault value when not set
oobUniversalAppLinkSupportedBooleanWhether the app supports universal app linksfalse

The following is an example of how to generate the ConfigurationParameters object:

ConfigParametersBuilder.Builder()
.setEnvironment("Environment value provided during the integration stage")
.setApiToken("ApiToken")
.setAppVersion("1.0.0")
.setOobUniversalAppLinkSupport(true)
.build()

Challenge UI customization

According to the EMVCo 3DS SDK specification, the integrator may customise the challenge UI in the native case (The HTML UI is set by the ACS and cannot be customised by the SDK). The table below, lists the customizable parameters for each customizable UI component:

ComponentPossible customization
Toolbarbackground color, button text, header text, text color, font, font size
Buttons*background color, corner radius, text color, font, font size
TextBoxesborder color, corner radius, border width, text color, font, font size
Labelsheading label text color, heading label font size, heading label font, non-heading label text color, non-heading label font size, non-heading label font

(*) There are several types of configurable buttons: SUBMIT, CONTINUE, NEXT, CANCEL, RESEND, OPEN_OOB_APP and ADD_CHOICE

There are three types of themes supported by the SDK: DEFAULT, DARK and MONOCHROME.

Challenge UI customization is optional and the integrator may implement all of the themes, some or none of them. In case that the user decides to ignore the challenge UI customization, the SDK will use its default themes and styles. The customized themes shall be provided to the SDK during the initialization as a map of UiCustomizationType keys to UiCustomization values. It should be provided to the uiCustomizationMap parameter of the initialize function.

The following code snippet shows how to customise the default (light) mode theme (the dark and monochrome modes customizations should be configured in the same way):

fun getSdkDefaultUiCustomization() = UiCustomization().apply {
    val defaultButtonCustomization = defaultButtonCustomization()
    setToolbarCustomization(defaultToolbarCustomization())
    setButtonCustomization(
        defaultButtonCustomization,
        UiCustomization.ButtonType.NEXT
    )
    setButtonCustomization(
        defaultCancelButtonCustomization(),
        UiCustomization.ButtonType.CANCEL
    )
    setButtonCustomization(
        defaultButtonCustomization,
        UiCustomization.ButtonType.CONTINUE
    )
    setButtonCustomization(
        defaultButtonCustomization,
        UiCustomization.ButtonType.SUBMIT
    )
    setButtonCustomization(
        defaultResendButtonCustomization(),
        UiCustomization.ButtonType.RESEND
    )
    setButtonCustomization(
        defaultOpenOobAppButtonCustomization(),
        UiCustomization.ButtonType.OPEN_OOB_APP
    )
    setButtonCustomization(
        defaultAddChoiceButtonCustomization(),
        UiCustomization.ButtonType.ADD_CHOICE
    )
    setLabelCustomization(defaultLabelCustomization())
    setTextBoxCustomization(defaultTextBoxCustomization())
}

private fun defaultButtonCustomization() = ButtonCustomization().apply {
    setBackgroundColor("#FF80CBC4")
    setCornerRadius(36)
    setTextColor("#FF000000")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
}

private fun defaultToolbarCustomization() = ToolbarCustomization().apply {
    setBackgroundColor("#FFF44336")
    setTextColor("#FF76FF03")
    setTextFontName("Shizuru-Regular.ttf")
    setTextFontSize(14)
    setButtonText("Cancel")
    setHeaderText("Secure Checkout")
}

private fun defaultCancelButtonCustomization() = ButtonCustomization().apply {
    setTextColor("#FF29F7F4")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
    setBackgroundColor("#00000000")
}

private fun defaultResendButtonCustomization() = ButtonCustomization().apply {
    setCornerRadius(36)
    setBackgroundColor("#FFF6DDDC")
    setTextColor("#FFA0B053")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
}

private fun defaultOpenOobAppButtonCustomization() = ButtonCustomization().apply {
    setCornerRadius(36)
    setBackgroundColor("#FFF6DDDC")
    setTextColor("#FFA0B053")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
}

private fun defaultAddChoiceButtonCustomization() = ButtonCustomization().apply {
    setCornerRadius(36)
    setBackgroundColor("#FFF6DDDC")
    setTextColor("#FFA0B053")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
}

private fun defaultLabelCustomization() = LabelCustomization().apply {
    setHeadingTextColor("#FF000000")
    setHeadingTextFontName("sans-serif.ttf")
    setHeadingTextFontSize(22)
    setTextColor("#FF000000")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(15)
}

private fun defaultTextBoxCustomization() = TextBoxCustomization().apply {
    setBorderColor("#FF842361")
    setCornerRadius(36)
    setBorderWidth(8)
    setTextColor("#FF333333")
    setTextFontName("sans-serif.ttf")
    setTextFontSize(16)
}

The values set in the above code snippet are used as an illustration. Where it applies, values are given in pixels. The RESEND, OPEN_OOB_APP and ADD_CHOICE button is a special case, as the setBackgroundColor changes the border color only.


Exceptions

According to the EMVCo SDK Specification, the SDK throws four types of exceptions when errors occur:

ExceptionDescription
InvalidInputExceptionOccurs due to invalid data provided to the SDK.
SDKAlreadyInitializedExceptionOccurs when initialize is called on an already initialised ThreeDS2Service object.
SDKNotInitializedExceptionOccurs due to an attempted usage of an uninitialised ThreeDS2Service object.
SDKRuntimeExceptionInternal SDK Error. Will contain information describing the error cause more in depth.

In the challenge flow, instead of throwing exceptions, the SDK reports errors via ChallengeStatusReceiver.runtimeError(RuntimeErrorEvent). This RuntimeErrorEvent has both the error message and error code.


Warnings

During initialisation (see Initialise the SDK), the SDK performs a number of required security checks. These checks may return different types of warnings back to your Application. It’s up to the Requestor Application how to handle such warnings - The SDK will continue to operate normally, should the application ignores them. Below is a list of the possible warnings the SDK may emit:

CodeDescriptionSeverity Level
SW01The device is jailbrokenHIGH
SW02The integrity of the SDK has been tamperedHIGH
SW03An emulator is being used to run the AppHIGH
SM04A debugger is attached to the AppMEDIUM
SW05The OS or the OS version is not supportedHIGH

The SDK takes additional security measures to protect itself and to protect the app, acc. to PCI 3DS SDK - Security Standard 1.1v.


Transaction creation

In order to make authentication requests, we require a Transaction object. To create a new Transaction object you need to call the createTransaction(...) function on the threeDS2Service object.
The createTransaction(...) may fail due to the following reasons:

SDKNotInitializedException when called on a non initialized SDK.
SDKRuntimeException when unable to find or open the directory server keys.
InvalidInputException when it fails to identify the message version or the directory server ID given to it as arguments.

Function signature:

@Throws(
    InvalidInputException::class,
    SDKNotInitializedException::class,
    SDKRuntimeException::class
)
override fun createTransaction(
    directoryServerId: String,
    messageVersion: String
): Transaction

Parameters:

ParameterTypeDescription
directoryServerIdStringThe Directory Server ID is a Registered Application Provider Identifier (Directory server Id) that is unique to the Payment System
messageVersionStringProtocol version that should be used by all 3DS components along the transaction

The following code snippet demonstrates a possible way to call the function and catch its exceptions :

fun generateAuthenticationRequest(
    directoryServerId: String,
    messageVersion: String
) =
    try {
        transaction = threeDS2Service.createTransaction(
            directoryServerId,
            messageVersion
        )
        val authenticationParameters =
            transaction?.getAuthenticationRequestParameters()
    } catch (ex: InvalidInputException) {
        // Handle exception
    } catch (ex: SDKNotInitializedException) {
        // Handle exception
    } catch (ex: SDKRuntimeException) {
        // Handle exception
    }

Transaction operations

The function getAuthenticationRequestParameters

In order to make an authentication request, the Ravelin 3DS SDK must prepare a set of parameters. This process includes the following operations:

1. Encrypting device data, with the directory server public key. This data is collected during the SDK initialization.
2. Generating an ephemeral public key for a possible challenge.
3. Generating an SDK transaction ID.
4. Sharing the SDK application ID, which is generated during the first initialisation of the SDK for every app installation.
5. Sharing the SDK reference number.
6. Sharing the message version used by the SDK.

Function signature:

@Throws(SDKRuntimeException::class)
fun getAuthenticationRequestParameters(): AuthenticationRequestParameters?

The function doChallenge

When the ACS(Access Control Server) is asking for a challenge to take place, the requestor application starts the challenge by calling the doChallenge function with proper challenge parameters.
The function receives the current Activity as a reference. It enables the SDK to start the challenge Activity. The challengeParameters parameter is an instance of the ChallengeParameters class and it implements an interface with the same name. Please see Challenge parameters . The challengeStatusReceiver parameter is a callback which defines the required behavior of the SDK upon completion. See Challenge flow results.
Once started, the SDK is in charge and communicates directly with the ACS. It also generates its own Activity to display the challenge UI.
The user shall also set a time limitation in the timeOut parameter. This parameter defines how long a challenge may last. The challenge will be automatically completed when this time limitation is reached. The minimal value for this parameter is 5 minutes. Setting a lower value will make the SDK default to 5 minutes.

Function signature:

@Throws(InvalidInputException::class, SDKRuntimeException::class)
fun doChallenge(
    currentActivity: Activity?,
    challengeParameters: ChallengeParameters?,
    challengeStatusReceiver: ChallengeStatusReceiver?,
    timeOut: Int
)

Parameters:

ParameterTypeDescription
currentActivityActivity?The application activity that calls doChallenge(...)
challengeParametersChallengeParameters?Set of parameters required to perform a challenge
challengeStatusReceiverChallengeStatusReceiver?Optional callback defines how to handle the challenge result
timeOutIntTime limitation in minutes for finishing a challenge. Its minimum value is 5 minutes

The function getProgressView

When called, this function generates a processing screen which displays the Android default progress bar and the directory server logo. The processing screen is an indication to the user that there is an activity taking place in the background either by the application or by the 3DS SDK. The processing screen should be displayed only in 2 cases:
– The requestor application is making an authentication request. In that case the processing screen is displayed during the AReq/ARes messaging.
– The first CReq/CRes cycle.

Please note that ProgressDialog was deprecated in Android API 26 but it is still used by the EMVCo specification in the Transaction contract.

Function signature:

@Throws(InvalidInputException::class, SDKRuntimeException::class)
fun getProgressView(currentActivity: Activity?): ProgressDialog?

Parameters:

ParameterTypeDescription
currentActivityActivity?The activity in which getProgressView(...) is called.

The function close

Call this function when the transaction is completed. This function cleans resources related to the transaction. Calling any of the other functions on a closed transaction will result with an SDKRuntimeException thrown by the 3DS SDK. Make sure to call this function whether the transaction was successful or not.

Function signature:

fun close()

Authentication request parameters

As described earlier (see Transaction operations), calling getAuthenticationRequestParameters on a transaction instance, returns an object that implements the AuthenticationRequestParameters interface. The properties of this object are used to generate the AReq message and when required also a challenge request (CReq) message.

/**
 * The AuthenticationRequestParameters class shall hold transaction data that the App
 * passes to the 3DS Server for creating the AReq.
 */
interface AuthenticationRequestParameters {
    /**
     * The getDeviceData method shall return the encrypted device data as a string.
     *
     * @return This method returns the encrypted device data as a JWE string.
     */
    fun getDeviceData(): String?

    /**
     * The getSDKTransactionID method shall return the SDK Transaction ID. When this method
     * is called, the 3DS SDK uses a secure random function to generate a Transaction ID in UUID
     * format.
     *
     * @return The getSDKTransactionID method returns this Transaction ID as a string.
     */
    fun getSDKTransactionID(): String?

    /**
     * The getSDKAppID method shall return the SDK App ID. The 3DS SDK uses a secure random
     * function to generate the App ID in UUID format. This ID is unique and is generated during
     * installation and update of the 3DS Requestor App on the Cardholder’s device.
     *
     * @return This method returns the SDK App ID as a string.
     */
    fun getSDKAppID(): String?

    /**
     * The getSDKReferenceNumber method shall return the SDK Reference Number.
     *
     * @return This method returns the SDK Reference Number as a string.
     */
    fun getSDKReferenceNumber(): String?

    /**
     * The getSDKEphemeralPublicKey method shall return the SDK Ephemeral Public Key. An
     * ephemeral key pair is used to establish a secure session between the 3DS SDK and the ACS.
     * During each transaction, the createTransaction method generates a fresh ephemeral key
     * pair and the getSDKEphemeralPublicKey method returns the public key component of the
     * same as a String representation of a JWK object.
     *
     * @return The getSDKEphemeralPublicKey method returns the public key component of the
     * ephemeral key pair as a String representation of a JWK object.
     */
    fun getSDKEphemeralPublicKey(): String?

    /**
     * The getMessageVersion method shall return the protocol version that is used for the
     * transaction.
     * The SDK receives the protocol version as a parameter in the createTransaction method
     * and determines whether it supports the version.
     * If the SDK does not receive the protocol version as a parameter in the createTransaction
     * method, then it returns the latest version that it supports.
     *
     * @return This method returns the protocol version as a string.
     */
    fun getMessageVersion(): String?
}

The function getDeviceData

Returns the encrypted device data which is collected during the initialisation of the SDK. This data is important for the ACS to conduct its TRA.

Function signature:

fun getDeviceData(): String?

The function getSDKTransactionID

A unique identifier generated by the SDK during a transaction creation. This ID is used to identify the transaction.

Function signature:

fun getSDKTransactionID(): String?

The function getSDKAppID

A unique identifier generated by the SDK during the first installation of the SDK. It is used to identify the requestor application on the user device.

Function signature:

fun getSDKAppID(): String?

The function getSDKReferenceNumber

A unique identifier of the SDK generated by EMVCo. during the certification of the SDK. It is used to identify the SDK during authentication requests.

Function signature:

fun getSDKReferenceNumber(): String?

The function getSDKEphemeralPublicKey

Returns the SDK Ephemeral Public Key. An ephemeral key pair is used to establish a secure session between the 3DS SDK and the ACS during the challenge flow.

Function signature:

fun getSDKEphemeralPublicKey(): String?

The function getMessageVersion

Returns the protocol version used during the transaction.

Function signature:

fun getMessageVersion(): String?

Challenge flow

The ACS performs a TRA(Transaction Risk Analysis) to decide whether to successfully authenticate the user or not. If the ACS have doubts about the TRA verdict it may ask for a challenge to take place. The requestor application may initiate a challenge by calling the doChallenge function with appropriate parameters (see Transaction operations for more details).

Challenge parameters

One of the parameters of the doChallenge function is an instance of the ChallengeParameters class. This class implements the ChallengeParameters contract. The class can be found in the 3DS SDK. Its constructor function looks as follows:

/**
 * The ChallengeParameters class shall hold the parameters that are required to conduct the challenge
 * process.
 * Note: It is mandatory to set values for these parameters.
 *
 * @param threeDSServerTransactionID Transaction identifier assigned by the 3DS Server to uniquely
 *                                   identify a single transaction.
 * @param acsTransactionID Transaction ID assigned by the ACS to uniquely identify a single
 *                          transaction.
 * @param acsRefNumber EMVCo assigns the ACS this identifier after running the EMV 3-D Secure
 *                     Testing and Approvals process on the ACS.
 * @param acsSignedContent ACS signed content. This data includes the ACS URL, ACS ephemeral public
 *                         key, and SDK ephemeral public key.
 * @param threeDSRequestorAppURL is the 3DS Requestor App URL. If the app sets the URL, then the SDK
 *                               shall pass the URL in the CReq.
 */
@Parcelize
@Serializable
data class ChallengeParameters(
    @SerialName("threeDSServerTransactionID")
    private var threeDSServerTransactionID: String? = null,

    @SerialName("acsTransactionID")
    private var acsTransactionID: String? = null,

    @SerialName("acsRefNumber")
    private var acsRefNumber: String? = null,

    @SerialName("acsSignedContent")
    private var acsSignedContent: String? = null,

    @SerialName("threeDSRequestorAppURL")
    private var threeDSRequestorAppURL: String? = null
) : Parcelable, ChallengeParameters

Please note that the first four parameters may be found in the authentication response message while the last parameter is an optional one. The threeDSRequestorAppURL consists of the merchant application URL. It allows the banking app to automatically return to the merchant app when the authentication completes using an app link in an Out-Of-Band (OOB) authentication. More information about how to set this parameter can be found in the spec EMV 3-D Secure - Protocol and Core Functions Specification version 2.3.1.1 and in Requirments for supporting universal app links.


Challenge flow behaviour

When a challenge is initiated, the 3DS SDK starts a new Activity which displays the challenge screen. The amount of challenges required to complete the authentication process is decided by the ACS based during the TRA. At any point the user may cancel the challenge flow by pressing the cancel button in the top bar of the challenge screen.

Challenge flow results

After invoking the doChallenge function, the Challenge Flow starts and the 3DS SDK takes control over the user interface. This means that the Activity provided via the doChallenge function, should not finish or get replaced by another Activity. The doChallenge function receives a callback object of type ChallengeStatusReceiver. This contract defines all the possible outcomes of a challenge flow. The challenge may terminate with one of the following outcomes:

Completed - The challenge flow ended and a transaction status is available.
Cancellation - The user decides to cancel the challenge (by pressing the cancel button in the Challenge screen top-bar).
Time out - The challenge may end with a timeout if it has not finished during the predefined period that is set by the application when calling the doChallenge(...) function.
Runtime error - The 3DS SDK has tackled a runtime error during the challenge process.
Protocol error - The 3DS SDK received an error message from the ACS and had to terminate the challenge.

When any of these are invoked, the Challenge is considered to be finished. As a result, the 3DS SDK dismisses the Challenge screen, closes the transaction, cleans up resources and returns control back to the application.

The ChallengeStatusReceiver interface needs to be implemented by the requestor app. You may find an example of how to implement this contract in the sample app.

 val challengeStatusReceiver = object : ChallengeStatusReceiver {
    override fun completed(completionEvent: CompletionEvent?) {
        // Implement your logic here
    }

    override fun cancelled() {
        // Implement your logic here
    }

    override fun timedout() {
        // Implement your logic here
    }

    override fun protocolError(protocolErrorEvent: ProtocolErrorEvent?) {
        // Implement your logic here
    }

    override fun runtimeError(runtimeErrorEvent: RuntimeErrorEvent?) {
        // Implement your logic here
    }
}

Cleaning the SDK

The requestor application should call this function to free resources that were designated for the Ravelin 3DS SDK. Like the initialize function, it should be called only once throughout the lifecycle of the application.
Calling this function, without initialising the SDK first, will cause an SDKNotInitializedException exception to be thrown.
Call this function before terminating the application or when interaction with the SDK is not needed anymore while the application is still alive.

Function signature

@Throws(SDKNotInitializedException::class)
fun cleanup(applicationContext: Context)

Parameters:

ParameterTypeDescription
applicationContextContextThe application context

UI mode support

The Ravelin SDK supports three themes: light, dark and monochrome. The theme is selected based on the system configuration. A monochrome theme is used only when the device itself is a monochromatic device.

If the application supports selecting its own theme independently from the system, you may inform the SDK about the current theme by providing a value in the Activity extras bundle. This is the same Activity used when calling the doChallenge(...) function to start a challenge. The following is an example of how to set the theme:

private fun Activity.setAppUiTheme(useDarkTheme: Boolean) =
    when (useDarkTheme) {
        true -> this.intent.putExtra(APP_UI_MODE, DARK_MODE)
        false -> this.intent.putExtra(APP_UI_MODE, LIGHT_MODE)
    }

where:

const val APP_UI_MODE = "uiMode"
const val LIGHT_MODE = "light"
const val DARK_MODE = "dark"

The SDK will check if it has received a value for the key APP_UI_MODE in the extras bundle and change the theme according to its value.

You may also provide an optional customized theme for each one of the mentioned themes, as explained in the Challenge UI customization section.

Required permissions

As part of complying with The EMVCo. standard, the Ravelin 3DS SDK collects information about the user device, which helps the ACS to conduct its TRA. To do so, the integrator needs to request the user to grant several runtime permissions and set some installation time permissions as well. Nonetheless, except for few of them, requesting these permissions is not considered mandatory, and the user may still be able to authenticate without granting them. On the other hand, The more device information collected by the SDK, the lower the risk for a user to fail the authentication as the ACS has less uncertainty when making a TRA. In case your app is targeting Android SDK 13 or higher, you may decide to revoke those permissions your app has no longer need for, after finishing the 3DS SDK initialization. To revoke permissions you can use the following API revokeSelfPermissionsOnKill(Collection). The following tables lists the permissions required by the SDK and may be added to the manifest file:

Internet


<uses-permission android:name="android.permission.INTERNET"></uses-permission>
PermissionINTERNET
Why is it needed?Connection to the network.
When to ask for the permission?Installation.
CommentsThe SDK requires connectivity to the Internet in order to be able to authenticate and fetch resources.

Access Network State


<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
PermissionACCESS_NETWORK_STATE
Why is it needed?Validates user authenticity - Access to device ip address.
When to ask for the permission?Installation.
Comments-

Change Network State


<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
PermissionCHANGE_NETWORK_STATE
Why is it needed?Validates user authenticity - Access Wifi manger connection information.
When to ask for the permission?Installation.
Comments-

Access fine location


<uses-permission
    android:name="com.google.android.gms.permission.ACCESS_FINE_LOCATION"></uses-permission>
PermissionACCESS_FINE_LOCATION
Why is it needed?Validates user authenticity - Used to Access device location and Wifi connection features.
When to ask for the permission?Installation for API levels 21 & 22 and below and installation & runtime for API levels 23 and above.
CommentsRequest together with the ACCESS_COARSE_LOCATION.

Access coarse location


<uses-permission
    android:name="com.google.android.gms.permission.ACCESS_COARSE_LOCATION"></uses-permission>
PermissionACCESS_COARSE_LOCATION
Why is it needed?Validates user authenticity - Used to Access device location.
When to ask for the permission?Installation for API levels 21 & 22 and below and installation & runtime for API levels 23 and above.
Comments-

Read phone state


<uses-permission
    android:name="com.google.android.gms.permission.READ_PHONE_STATE"></uses-permission>
PermissionREAD_PHONE_STATE
Why is it needed?Validates user authenticity - Used to retrieve telephony identifiers.
When to ask for the permission?Installation time for API levels 21 & 22 and runtime for API levels 23 and above.
Comments-

Read basic phone state


<uses-permission
    android:name="com.google.android.gms.permission.READ_BASIC_PHONE_STATE"></uses-permission>
PermissionREAD_BASIC_PHONE_STATE
Why is it needed?Validates user authenticity - Used to retrieve telephony identifiers.
When to ask for the permission?Runtime for API levels 33 and above.
Comments-

Send SMS


<uses-permission android:name="com.google.android.gms.permission.SEND_SMS"></uses-permission>
PermissionSEND_SMS
Why is it needed?Validates user authenticity - Used to retrieve phone line number.
When to ask for the permission?Install time for API levels 21 & 22 and runtime for API level 23 and above.
CommentsRequest only if your app is the default handler of SMS on the device.

Read phone numbers


<uses-permission
    android:name="com.google.android.gms.permission.READ_PHONE_NUMBERS"></uses-permission>
PermissionREAD_PHONE_NUMBERS
Why is it needed?Validates user authenticity - Used to retrieve phone line number.
When to ask for the permission?runtime for API levels 26 and above.
Comments-

Access Wifi state


<uses-permission
    android:name="com.google.android.gms.permission.ACCESS_WIFI_STATE"></uses-permission>
PermissionACCESS_WIFI_STATE
Why is it needed?Validates user authenticity - Used for accessing Wifi network attributes.
When to ask for the permission?Installation time permission from API 21 and above.
Comments-

Bluetooth


<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
PermissionBLUETOOTH - Bluetooth device identifiers
Why is it needed?Validate user authenticity - Used for accessing Bluetooth adapter and paired devices attributes.
When to ask for the permission?Installation time permission from API 18 and above.
Comments-

Bluetooth connect


<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"></uses-permission>
PermissionBluetooth connection to paired devices
Why is it needed?Validates user authenticity - Bluetooth parameters related to paired devices.
When to ask for the permission?Installation time permission from API 31 and above.
Comments-

Request install packages


<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"></uses-permission>
PermissionREQUEST_INSTALL_PACKAGES
Why is it needed?Validates user authenticity - Checks whether the application can install non market packages.
When to ask for the permission?Installation time permission.
CommentsRequest only if your app core functionality includes sending, receiving or enabling app packages.

The Out-of-Band(OOB) challenge allows the user to authenticate through a dedicated banking application installed on their device.
To ensure a smoother experience when opening the banking authentication app via universal app links, follow the steps below:

1. Enable app-links support in the SDK by calling setOobUniversalAppLinkSupport with the value true.

To allow the banking application to automatically return to the merchant application after the authentication, use the following app link setup:

1. Follow the instructions in the Android App links documentation to configure app links in your application.

2. When generating challenge parameters, set the threeDSRequestorAppURL. For example:

https://appname.com/openRefApp?transID=<sdkTransID>

This value enables the banking application to return to the merchant application automatically once the authentication is complete.
If the threeDSRequestorAppURL is not provided but setOobUniversalAppLinkSupport(true) is enabled, an OOB authentication will still proceed; however, the banking application will not automatically return to the merchant application. A query parameter named transID needs to be set. This is the SDK transaction ID that can be retrieved from the authentication request parameters by calling the function: getSDKTransactionID() see Authentication request parameters.


Security

PCI requirements

The Ravelin 3DS SDK is designed to meet the requirements set in PCI 3DS SDK - Security Standard 1.1v. Nevertheless, these requirements entail constant updates and improvements as security threats become more and more sophisticated.


ProGuard rules

This section is relevant to application providers that use the R8 compiler to optimise and obfuscate their source code and app resources. In case you’re using the R8 compiler, there are no additional requirements. The compiler will take care of merging the SDK ProGuard keep rules. In case you’re using a different indexing tool, please add the following ProGuard rules to your ProGuard configuration file manually. Ignoring these keep rules may lead to potential errors.


Recommendations for the integrator

Apart from complying with the requirements indicated by the EMVCo and the PCI specifications (please refer to Supporting documentation), some additional measures can be taken by the integrator, in order to mitigate some potential security threats. A full list of risks and vulnerabilities and their potential mitigations is outside the scope of this guide. Yet few important measures may be taken to make it harder for attackers to exploit your app. The following list includes only suggestions and/or recommendations:

ActionCategoryDescriptionAdditional requirements / comments
Keep your secrets safeConfidentiality / IntegritySecrets within the app may be discovered through reverse engineering or even due to the nature of the package file anatomy.Opt to encrypt such valuable data by using the KeyStore or an Encrypted shared preference.
Protection against reverse engineeringConfidentiality / IntegrityThe Ravelin 3DS SDK is taking different measures to maintain the integrity of both the app and the SDK. Nevertheless the integrator can increase the integrity of the app by obfuscating its source code.Opt to obfuscate your source code. This will make reverse engineering your code harder.
Avoid setting android:debuggable=“true” in the Manifest.xml file under the application tagConfidentiality / IntegrityThis makes it easier for attackers to exploit your app-
Avoid setting android:allowBackup=“true” in the Manifest.xm file the application tagConfidentiality / IntegrityIt gives bad actors the ability to manually backup the app into a readable file with simple ADB shell commandsThese files may contain sensitive data about your users and expose your application secrets. This recommendation is harder to meet with. As an alternative you add a backup configuration file (Mandatory from Android 12).
It is recommended to avoid logging sensitive dataConfidentialityA potential attacker may exploit sensitive information about the device, the user or the 3DS messages.-

Gradle and Kotlin versions

The latest version of the Ravelin 3DS SDK is built and tested with AGP 8.10.0, uses Gradle Wrapper 8.13, and is compiled with Kotlin plugin version 2.1.20.


Dependencies

Please see below a list of dependencies used by the Ravelin 3DS SDK:

Dependency NameVersion
Kotlinx-Serialization-Json (Apache2)1.8.1
Kotlinx-Coroutines-Core (Apache2)1.10.2
AppCompat (Apache2)1.7.1
Google-Android-Material (Apache2)1.13.0
Core-Ktx (Apache2)1.16.0
AndroidX-Activity-Ktx (Apache2)1.10.1
AndroidxX-Fragment-Ktx (Apache2)1.8.9
AndroidX-ConstraintLayout (Apache2)2.2.1
AndroidX-RecyclerView (Apache2)1.4.0
AndroidX-CardView (Apache2)1.0.0
AndroidX-WorkManager (Apache2)2.10.5
AndroidX-Datastore (Apache2)1.0.0
AndroidX-Lifecycle-ViewModel-Ktx (Apache2)2.9.4
Retrofit2 (Apache2)3.0.0
Retrofit2-kotlinx-serialization-converter (Apache2)3.0.0
Retrofit2-Converter-Scalars (Apache2)3.0.0
Okhttp3 (Apache2)5.3.0
Timber (Apache2)5.0.1
Dagger2 (Apache2)2.57
Dagger2-Android (Apache2)2.57
Dagger-Compiler (Apache2)2.57
Dagger-Android-Processor (Apache2)2.57
Bouncycastle (Apache2)1.82
Coil (Apache2)3.3.0
Coil-network (Apache2)3.3.0
Nimbus (Apache2)10.6
Play-services-location (Apache2)21.3.0
Play-services-basement (Apache2)18.9.0

Release Notes

v2.0.2- January 30, 2026

  • Extend the Challenge Processing screen on-time to prevent a dead interval before the challenge UI is dismissed.

v2.0.1- January 19, 2026

Bug fixes

  • For protocol version v2.3.1, device binding information was incorrectly populated when the toggle position indicator was enabled.

v2.0.0 - December 09, 2025

Breaking changes:

  • The SDK was certified by EMVCo for protocol version v2.3.1 which introduces some breaking changes.
  • Drop support for protocol version v2.1.0.
  • The SDK initialization function has been modified to support the new protocol version. It has a new optional parameter uiCustomizationMap of type HashMap<UiCustomizationType, UiCustomization>?. This parameter can take three different types of UI customization instances for: dark mode, light mode and for monochromatic devices. It replaces the previous function were two possible UI customization instances were provides for light and dark modes.
  • R8 ProGuard keep rules were updated.
  • DeviceInfoContract was renamed to DeviceInfoProvider.
  • The minimum Android version has been updated from Android 5 (API Level 21) to Android 7 (API Level 24).

Minor changes

  • A new configuration parameter has been added to indicate whether the requestor application supports universal app links for Out-of-Band(OOB) challenges. This parameter, oobUniversalAppLinkSupported, can be set using the setOobUniversalAppLinkSupport function. When not set, this value defaults to false.
  • A new API function was added getSupportedVersions(). This function should help the application decide which protocol version to use when creating new transaction. It is useful for old versions of the application that may not support a new version of the protocol.
  • Auto links removed to avoid a potential disruption for OOB challenges.

v1.6.0 - May 13, 2025

Minor changes

  • Added support for Amex PKI due to Amex internal infrastructure changes.

v1.5.0 - February 21, 2025

Minor changes

  • The SDK supports Android API level 35 (Android 15).
  • NDK updated to r28.
  • Compatibility with 16kB page size Android devices.
  • The challenge information text indicator icon has been updated.

Bug fixes

  • Reading the pass point network information, as part of the device information collection ignores the internal API timeout configuration. This resulted with potential timeouts for the device information collection; hence, a timeout wrapper was added to the pass point network information collection.

v1.4.2 - September 23, 2024

Minor changes

  • Some third party dependencies were updated.
  • Migration to Kotlin 2.0.20.
  • New logging events were added. These information helps us to monitor the SDK in production.

Bug fixes

  • Some Samsung devices throw an error when trying to read telephony data even with granted permissions. This data is used to build the device fingerprint for an ACS TRA. We now bypass reading such information on these type of devices.
  • Send a CreateTransaction event only after SDK initialization check to avoid a scenario where the requestor app calls createTransaction before the SDK is fully initialized.

v1.4.1 - August 16, 2024

Minor changes

  • A retry mechanism, based on AndroidX WorkManager was added to the logging unit. If the device connectivity to the network is lost, it tries to retransmit the events, once the device connectivity is renewed. It will do so even if the application is moved to the background or the application process is killed by the OS.
  • Runtime errors verbosity is revised where possible to provide additional information to the requestor application.
  • A new config parameter named appVersion was added. This parameter is used to set the version of the requestor application.

Bug fixes

  • We have retired the use of the AndroidX security-crypto library. The library suffered from occasional synchronization problems on write/read to/from the device KeyStore. This issue was causing initialization problems on some devices. Instead, the same functionality is now integrated into the SDK itself.

v1.4.0 - February 20, 2024

Minor changes

  • The SDK been re-certified by EMVCo.
  • Granting the AD_ID permission in the requestor app is not required anymore and was removed from the device information spec. v1.6.
  • Migration to Kotlin 1.9.22.
  • Migration of annotation/symbol processing from kapt to ksp.
  • SDK device information collection migration from v1.4 to v1.6.
  • Update dependencies and build tools (see list of dependencies above).
  • Processing screen half transparent overlay background color changed to 0x60FFFFFF in light mode and 0x60000000 in dark mode, as required by the EMVCo. specification bulletins(SB204 v7 and SB214 v3).

v1.3.0 - December 1, 2023

Minor changes

  • Design adjustments in Native UI challenges.

v1.2.0 - November 13, 2023

Minor changes

  • Compatibility with Android 14.
  • Extend the usage of AndroidX datastore to fragments by replacing usage of fragments arguments bundles with datastore to bypass bundles size limitation.
  • Upgrade third party dependencies and the build tools.
  • Migrate to Kotlin 1.9.10.
  • Source code improvements such as making it more functional and use of context receivers where applicable.
  • Replace usage of the deprecated coroutines function whenResumed with the improved repeatOnLifecycle.
  • Update the SDK ProGuard rules (see ProGuard rules).

v1.1.0 - September 13, 2023

Minor changes

  • Update the Kotlin coroutines library version to 1.7.3.
  • When the soft key is visible, for instance when the user types an OTP code, the challenge layout resizes to fit the remaining space. This results with the user ability to scroll and interact with views that are not visible when the soft key is visible.
  • UI customization improvements: the UI now uses ScaleDrawable to scale the customized view. For more information see the Challenge UI customization section.

Feedback