Link Search Menu Expand Document

Pure 3DS Interface

Table of contents
  1. Overview
  2. Actors & flow diagram
  3. Initialization
    1. Android
    2. iOS
    3. Android
    4. iOS
  4. The warnings
  5. Create Transaction
    1. Android
      1. Option #1
      2. Option #2 (Enables SCA functions in the transaction, recommended)
    2. iOS
      1. Option #1
      2. Option #2 (Enables SCA functions in the transaction, recommended)
  6. Getting Authentication parameters
    1. The Progress Dialog
      1. Android
      2. iOS
  7. The challenge flow
    1. Android
    2. iOS
  8. Completing Authentication
  9. Closing Transaction
    1. Android
    2. iOS
  10. Cleanup
    1. Android
    2. iOS

Overview

The SDKs implement the pure 3DS v2 protocol as specified by EMVco - https://www.emvco.com/emv-technologies/3d-secure. This section explains the interface in detail as well as provides code snippets to get started promptly.

Actors & flow diagram

The following diagram is a sequence that helps grasping the pure 3DS v2 API interface concepts as well as visualizing the actors and their interactions. The actors are:

  1. The requestor’s mobile application that has the Android or iOS SDK integrated
  2. The SDK itself
  3. The requestor’s backend server side application. That’s where the requestor usually maintains a database of products and users. This is where the authenticate call to a 3DS server call made from.
  4. A 3DS Server

The following sub-sections describe each of the concepts in details.

Initialization

Before use, the SDK has to be instantiated and initialized. This usually is done once during application’s session when application is launching. At this point the SDK captures and caches device information and prepares itself to serve.

The method accepts a couple of arguments, the most important one the configParams which is used to pass the license key as well as any additional directory servers to the SDK at runtime. The code examples below show how the license key is passed.

See Directory Servers section for more details about how to use configParams to pass additional directory servers.

Android

// Create an instance of the 3DS service:
ThreeDS2Service service = new UsdkThreeDS2ServiceImpl();

// Prepare ConfigParameters and put the license key in it
ConfigParameters configParams = new ConfigParameters();
configParams.addParam(UsdkThreeDS2ServiceImpl.SDK_CONFIG_PARAMETER_GROUP,
  UsdkThreeDS2ServiceImpl.LICENSE_PARAMETER_KEY,
  licenseKey);

// Initialize the SDK
service.initialize(context, configParams, locale, uiCustomization);
  • context - An Android context, it can be an Application instance or some other Context instance.
  • configParams - an instance of org.emvco.threeds.core.ConfigParameters class used for passing additional configuration parameters down to the SDK.
  • locale - the locale as a String, a value like en_US would be good. Also it is possible to pass null value which will make it use the default locale on the device.
  • uiCustomization - an instance of org.emvco.threeds.core.ui.UiCustomization class used to specify how the challenge screen should look like. The matter is documented in detail in the Customizing the challenge screen section.

On Android, the initialization is an asynchronous task, so the app has to set up a listener to be notified when the initialization finishes. The complete example would be this:

ThreeDS2Service service = new UsdkThreeDS2ServiceImpl();

ConfigParameters configParams = new ConfigParameters();
configParams.addParam(UsdkThreeDS2ServiceImpl.SDK_CONFIG_PARAMETER_GROUP,
  UsdkThreeDS2ServiceImpl.LICENSE_PARAMETER_KEY,
  licenseKey);

BroadcastReceiver listener = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, intent.toString());
        LocalBroadcastManager.getInstance(context).unregisterReceiver(this);

        if (!intent.getBooleanExtra(UsdkThreeDS2ServiceImpl.INITIALIZATION_ACTION_EXTRA_SUCCESS, false)) {
            throw new RuntimeException("Failed to initialize SDK"
                    + ", code: " + intent.getStringExtra(UsdkThreeDS2ServiceImpl.INITIALIZATION_ACTION_EXTRA_ERROR_CODE)
                    + ", type: " + intent.getStringExtra(UsdkThreeDS2ServiceImpl.INITIALIZATION_ACTION_EXTRA_ERROR_TYPE)
            );
        }

        // it is OK to start use the `service` object as it has been successfully initialized
    }
};

LocalBroadcastManager.getInstance(this).registerReceiver(
    listener, 
    new IntentFilter(UsdkThreeDS2ServiceImpl.INTENT_INITIALIZATION_ACTION)
);

service.initialize(context, configParams, locale, uiCustomization);

iOS

// Prepare ConfigParameters and put the license key in it
private var configParams: UConfigParameters {
    let params = UConfigParameters()
    // kUSDKGroup and kUSDKParamLicenseKey are provided in UConstants.h. See docs there
    params.addParam(kUSDKGroup, name: kUSDKParamLicenseKey, value: mSigniaProvidedicenseKey)
    return params
}

// Initialize the SDK
UThreeDS2ServiceImpl.shared.initialize(configParams, locale: "en_us", uiCustomization: uiCustomization)

Note, the example above also shows how the license key is passed to the SDK.

  • configParams - An instance of UConfigParameters used for passing additional configuration parameters to the SDK.
  • locale - the locale as a String. Non-optional.
  • uiCustomization - An instance of UUiCustomization class used to specify various elements of the challenge screen UI. The matter is documented in detail in the Customizing the challenge screen section.

For more convenience there was added alternative method to initialize SDK. The only difference with the previous one is that there’s an InitSpec argument that encapsulates all the normal parameters.

Android

The complete initialization routine would look like this:

ThreeDS2Service threeDS2Service = new UsdkThreeDS2ServiceImpl();

InitSpec initSpec = new InitSpec();
initSpec.setLicenseKey("<license key");
initSpec.setApplicationContext(context);
initSpec.setUiCustomization(); // if UICustomization is necessary

InitCallback initCallback = new new InitCallback() {
      @Override
      public void onSuccess() {
          // initialization succeeded
      }

      @Override
      public void onError(Exception exception) {
          // initialization failed
      }
}

threeDS2Service.initialize(initSpec, initCallback);

Note, the example above also shows how the license key is passed to the SDK.

The complete structure of InitSpec is this:

public class InitSpec {

    // The license key issued by the MSignia administrator
    private String licenseKey;

    // An instance of Android application context
    private Context applicationContext;

    // Sets a string that represents the locale for the app's user interface.
    // For example, the value of locale can be "en_US" in Java.
    // If this parameter is not provided, then the default device locale is used.
    private String locale;

    // Returns an UI configuration information that is used to specify
    // the UI layout and theme. For example, font style and font size.
    private UiCustomization uiCustomization;

    // A configuration information that shall be used during initialization.
    // For example ConfigParameters can be used for passing custom DirectoryServers.
    private ConfigParameters configParameters;
}

iOS

A complete initialization example is this:

let initSpec = UInitSpec(
    licenseKey: "<license key>",
    uiCustomization: UUiCustomization(),
    configParameters: UConfigParameters()
)

UThreeDS2ServiceImpl.shared().initialize(spec: initSpec) { (error) in
  if let error = error { // can flip the logic and use guard let if preferred
    // handle error
    return
  }
  // no error

Note, the example above also shows how the license key is passed to the SDK

The structure of InitSpec is this:

@interface UInitSpec : NSObject

/**
 Init method for UInitSpec that takes both required params
 @param licenseKey your license key
 @param configParameters config parameters for the 3DS SDK as defined in the EMVCo 3DS Spec
 */
- (nonnull UInitSpec *)initWithLicenseKey:(nonnull NSString *)licenseKey
                         configParameters:(nonnull UConfigParameters *)configParameters;

/**
Init method for UInitSpec that takes both required params and optional UICustomization
@param licenseKey your license key
@param uiCustomization the UI customization for the 3DS SDK as defined in the EMVCo 3DS Spec
@param configParameters config parameters for the 3DS SDK as defined in the EMVCo 3DS Spec
*/
- (nonnull UInitSpec *)initWithLicenseKey:(nonnull NSString *)licenseKey
                          uiCustomization:(nullable UUiCustomization *)uiCustomization
                         configParameters:(nonnull UConfigParameters *)configParameters;

/**
Init method for UInitSpec that takes both required params and optional locale
@param licenseKey your license key
@param locale the locale of your app
@param configParameters config parameters for the 3DS SDK as defined in the EMVCo 3DS Spec
*/
- (nonnull UInitSpec *)initWithLicenseKey:(nonnull NSString *)licenseKey
                                   locale:(nullable NSString *)locale
                         configParameters:(nonnull UConfigParameters *)configParameters;

/**
Init method for UInitSpec that takes all params
@param licenseKey your license key
 @param locale the locale of your app
 @param uiCustomization the UI customization for the 3DS SDK as defined in the EMVCo 3DS Spec
@param configParameters config parameters for the 3DS SDK as defined in the EMVCo 3DS Spec
*/
- (nonnull UInitSpec *)initWithLicenseKey:(nonnull NSString *)licenseKey
                                   locale:(nullable NSString *)locale
                          uiCustomization:(nullable UUiCustomization *)uiCustomization
                         configParameters:(nonnull UConfigParameters *)configParameters;

#pragma mark Setters
- (void)setLicenseKey:(nonnull NSString *)licenseKey;
- (void)setLocale:(nullable NSString *)locale;
- (void)setUICustomization:(nullable UUiCustomization *)uiCustomization;
- (void)setConfigParameters:(nonnull UConfigParameters *)configParameters;

#pragma mark Getters
- (nonnull NSString *)getLicenseKey;
- (nullable NSString *)getLocale;
- (nullable UUiCustomization *)getUICustomization;
- (nonnull UConfigParameters *)getConfigParameters;

@end

The warnings

After a successful initialization of the uSDK, the app may call an optional getWarnings() method to gather any warnings uSDK generated during the initialization. The complete Warnings concept is described in detail in its own section later in the documentation.

Create Transaction

At some point the app decided to start a 3DS transaction. This can be done at the moment the user taps the “Purchase” button in the app.

Android

There are two methods there to create a transaction. The first one is the one required per the 3DS v2 specification while the second one is more generic and is recommended to use when SCA functionality is required.

Option #1

String directoryServerId = "A000000065"; // JCB DS RID
String messageVersion = "2.2";

Transaction transaction = service.createTransaction(directoryServerId, messageVersion);
String directoryServerId = "A000000065"; // JCB DS RID
String messageVersion = "2.2";
String cardId = "bc9af71d7f22110c6b5dd64d249839fe"; // Card ID from the wallet

CreateTransactionSpec spec = new CreateTransactionSpec(directoryServerId, messageVersion);
spec.setCardId(paymentMethod.getCard().getCardId());

Transaction transaction = threeDS2Service.createTransaction(spec);

iOS

Same two options are applicable for iOS:

Option #1

do {
    transaction = try UThreeDS2ServiceImpl.shared()
      .u_createTransaction("A000000065", messageVersion: "2.2.0")
}
catch {
    // handle error
}
do {
  let createSpec = UCreateTransactionSpec(
    directoryServerID: "A000000065", 
    messageVersion: "2.2.0", 
    cardID: "bc9af71d7f22110c6b5dd64d249839fe")

  transaction = try UThreeDS2ServiceImpl.shared().createTransaction(with: createSpec)
}
catch {
      // handle error
}

Getting Authentication parameters

Once there’s a transaction instance created, it is the time to call its getAuthenticationRequestParameters method. The result of this method is a plain object with a bunch of fields that need to be literally conveyed to 3DS server. See the “Deliver purchase information & authentication params” label in the diagram above for how this is conveyed to the backend and later to a 3DS Server.

The response received from 3DS Server is an ARes. Either the backend or the mobile application has to inspect it to see the transStatus value the 3DS server returned. The following matrix could be used as a starting point but it is strongly recommended to familiarize with the 3DS specification here.

ARes.transStatus Description
Y Order completed successfully / Authentication successful
A Order completed successfully / Attempts processing performed
D Decoupled authentication is being performed
C Continue to Challenge Flow
N Order denied / Not authenticated (denied)
R Order denied / Authentication rejected
U Order failed due to technical reasons / Authentication could not be performed

So, for instance, if ARes.transStatus was Y then the authentication has succeeded.

The Progress Dialog

While the application conveys the parameters to the backend and then to a 3DS Server, it is recommended to display a progress dialog, given that is normally a long and time consuming operation. The SDK provides a component for that displaying the DS logotype and a spinning image to entertain the user.

Android

// To Show the Progress Dialog
ProgressDialog progressDialog = transaction.getProgressView(activity);
((UsdkProgressDialog)progressDialog).setImage(imageBitmap); // It is optional step. By default will be shown DS logo passed with DirectoryServer.
progressDialog.show();

...

// To Hide the Progress Dialog
progressDialog.dismiss();

iOS

// To Show the Progress Dialog
do {
  var hud = try? transaction.getProgressView()
  
  if customImageToLoadWithHud {
    hud.setProgressViewImage(UIImage(named: "customImage"))
  }
  
  view.addSubview(hud)
  hud.center = CGPoint(x: screenWidth / 2, y: screenHeight / 2)
  view.bringSubviewToFront(hud)
  hud.start()
}
catch {
  // handle error
}

...

// To Hide the Progress Dialog
hud.stop()
hud.removeFromSuperview()

The challenge flow

If ARes.transStatus is C then that means ACS opts to challenge the user. For that the mobile application calls the doChallenge method on the transaction instance.

The result of challenge flow is then called back to the application as part of ChallengeStatusReceiver interface/delegate.

Android

ChallengeParameters challengeParameters = new ChallengeParameters();
challengeParameters.setAcsRefNumber(acsReferenceNumber);
challengeParameters.set3DSServerTransactionID(threeDSServerTransID);
challengeParameters.setAcsSignedContent(acsSignedContent);
challengeParameters.setAcsTransactionID(acsTransID);
challengeParameters.setThreeDSRequestorAppURL("https://requestor-url.com/app-destination");

try {
    transaction.doChallenge(activity, challengeParameters, challengeStatusReceiver, timeOut);
} catch(InvalidInputException e) {
    // catch block here
}
  • activity - activity instance that invoked doChallenge method
  • challengeParameters - the data ACS puts in the ARes for the SDK to conduct a challenges flow. This is an instance of ChallengeParameters class that the mobile application has to construct literally using the corresponding data from ARes.
  • challengeStatusReceiver - an implementation of ChallengeStatusReceiverinterface which acts as a callback object for notifying the application about the result of challenging.
  • timeOut - an interval, in minutes, within which the challenge process must be completed. The minimum possible value is 5 minutes. The recommended value for this parameter is 10 minutes.

iOS

Simply call doChallenge and the SDK’s UI will handle the challenge process and notify the app upon completion of the challenge. There’s also a convenience method that supports do-try-catch for Swift:

guard let navController = navigationController else {
  showAlert(message: "You must present challenge flows within a UINavigationController 
    per EMVco 3DS specification (see 'Toolbar' in the EMVCo 3DS Spec).")
  return
}

let challengeParams = try UChallengeParameters(
    threeDSTransID: threeDSServerTransID, 
    acsTransactionID: acsTransID, 
    acsRefNumber: acsReferenceNumber, 
    acsSignedContent: acsSignedContent)
challengeParams.setThreeDSRequestorAppURL("https://requestor-url.com/app-destination")
let timeout: Int = 20

do {
  try transaction.u_doChallenge(
      navController, 
      challengeParameters: challengeParameters, 
      challengeStatusReceiver: self, 
      timeout: timeout)
}
catch {
  // handle error
}
  • currentNavController - the UINavigationController inside which the SDK UI will be presented
  • challengeParameters - ACS details required by the 3DS SDK to conduct the challenge process during the transaction.
  • challengeStatusReceiver - a callback object for notifying the 3DS Requestor App about the challenge status.
  • timeOut - an interval, in minutes, within which the challenge process must be completed. The minimum possible value is 5 minutes. The recommended value for this parameter is 10 minutes.

Completing Authentication

There’re a couple of considerations for completing authentication. First, the authentication can be deemed successful if there was a corresponding transStatus received in ARes, the “Getting Authentication parameters” section above explains that.

Another possibility is to receive a call via ChallengeStatusReceiverinterface/delegate the app passed to doChallenge method. The interface methods are self-explanatory - completed etc.

It is also worth to make sure the code handles exceptional situations as there might be networking issues, device resources limited and so on and so forth.

Closing Transaction

When authentication completes, it is necessary for the application to close the transaction. There’s a close method in SDK for that.

Note the CloseTransactionSpec instance is passed into the method. By default a new default instance would be just fine or just use close method without arguments(the one required per the 3DS v2 specification). If however there were SCA methods leveraged in the transaction then the CloseTransactionSpec instance needs to be specifically prepared. The SCA methods are documented in another section of this document.

Android

The method is called like this:

CloseTransactionSpec spec = new CloseTransactionSpec();
transaction.close(spec);

iOS

The corresponding iOS method signature is this:

try? transaction.close()

Cleanup

The cleanup method frees up resources that are used by the SDK. It is called only once during a single 3DS Requestor App session.

Android

For Android, the cleanup method does de-initialize for the SDK. The method is called like this:

service.cleanup(context);
  • context - An Android context, it can be an Application instance or some other Context instance.

iOS

For iOS, the u_cleanup and cleanup methods do not de-initialize the SDK. These methods only clear out some local variables used by the SDK.

Example Usage

do {
    try UThreeDS2ServiceImpl.shared().u_cleanup()
}
catch {
    // handle error
}