Netcetera iOS 3DS SDK Demo Application

This section describes the Netcetera iOS 3DS SDK Demo Application.

Netcetera iOS 3DS SDK demo application

This document provides details about the Netcetera iOS 3DS SDK demo application and how the Netcetera iOS 3DS SDK is integrated in it.

Requestor Application

The Netcetera iOS 3DS SDK demo application is an example application that integrates the Netcetera iOS 3DS SDK. Together with this document, they serve as a guide for an easier integration of the Netcetera iOS 3DS SDK in an application or library.

The demo application simulates a real life payment action. It handles both frictionless and challenge flows.

When you open the application, the SDK is initialed and warnings are retrieved from the SDK. In case of any warning, a warning info screen is displayed. If no warnings are returned the application shows a payment details screen. The payment screen mimics a checkout process with all required payment details. Depending on the provided input (or selected scenario), the application will present the appropriate flow. Users can enter data manually, or select the predefined scenarios which will fill the payment screen values. These are configured for easier testing purposes with our backend. The scenarios can be selected when taping on the “Choose pre-filled data” cell. Once all values are filled a transaction is triggered with taping on the “Submit” button.

The demo application is connected to a Netcetera requestor backend server. The server decides whether a frictionless or a challenge flow will be initiated. Note that it works in Netcetera test environment only. Appropriate adjustments would be needed in order to connect to another Requestor backend.

App architecture

The NCA demo application implements the MVP pattern. It uses a ViewController for the UI, a presenter that contains the presentation logic, and use cases which contain the business logic. The application features are divided in groups. Each group contains the corresponding files.

For example, the source/main/initalization folder contains the following files: * InitialViewController - contains the methods for presenting the correct screens * InitialViewPresenter - contains the logic for the screen presentation * InitializationUseCase - contains the logic for the initialization of the SDK and verifies its warnings.

The same type of structure is implemented in the source/main/paymentDetails group.

With the intent to be used for different use cases, the iOS SDK demo application uses a singleton object of the ThreeDS2Service class. For easier dependency injection, the application implements the Container class from the GirdersSwift framework.

The pre-defined scenarios screen is implemented with PrefilledDataController. The scenarios model objects are created in the PaymentDetailsScenarios class. These can be easily adjusted with new preferred values.

The handling od user actions on the payment checkout screen is done in the PaymentDetailsPresenterImplementation class. The creation of the transaction is triggered by the submitButtonTapped action. The transaction is created based on the screen input.

The authentication request is created in AuthenticationRequestJsonGenerator class. Depending on the response received from the server, the application decides whether or not to opt for a challenge. An example implementation of the authentication request can be found in AppApiGateway, while the method which calls this request is located in PaymentDetailsPresenterImplementation.

For integration of external libraries, the 3DS demo application uses CocoaPods integration.

SDK Usage

At the start of the Netcetera 3DS iOS demo application, the 3DS SDK is initialized in InitializationUseCaseImplementation. For the initialization to be successful, a valid licence key needs to be configured. An example of such case is displayed below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func initializeSDK(succesHandler: @escaping InitializationCompleteHandler,
                   errorHandler: @escaping ErrorHandler) {
     
    let configParameters = ConfigParameters()
    do {
        try configParameters.addParam(group: nil,
                                      paramName: "license-key",
                                      paramValue:"ey...")
        // Change uicustomization to nil for default design.
        let uicustomization = try createUICustomization()
        try threeDS2Service.initialize(configParameters,
                                       locale: nil,
                                       uiCustomization: uicustomization)
        succesHandler()
    } catch let error as NSError {
        errorHandler(error.localizedDescription)
    }
}

Once the SDK has been initialized, you can access the warnings generated during the initialization. This is done in the getWarnings() method of ThreeDS2ServiceSDK object. The method is called by the verifyWarnings method in the InitializationUseCase class. Both the SDK and PCI warnings are returned in the list. It is up to the application or library to decide what to do with these warnings. An example is displayed bellow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func verifyWarnings(errorHandler: @escaping ErrorHandler) {
    var sdkWarnings: [Warning] = []
    do {
        sdkWarnings = try threeDS2Service.getWarnings()
    } catch let error as NSError {
        errorHandler(error.localizedDescription)
    } catch {
        errorHandler("ThreeDS SDK couldn't calculate warnings")
    }
    if sdkWarnings.count > 0 {
        var message = ""
        for warning in sdkWarnings {
            message = message + warning.getMessage()
            message = message + "\n"
        }
        errorHandler(message)
    }
}

With the help of the SDK, the logic for creating the transaction can be found in PaymentDetailsPresenterImplementation. The creation of the transaction starts in the submitButtonTapped function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func submitButtonTapped(paymentDetails: PaymentDetails) {
     
    var progressView: ProgressDialog?
    var requestSentTime: Date?
    var directoryServerId: String!
 
    requestSentTime = Date()
    do {
        directoryServerId = try directoryServerIdFor(accountNumber: paymentDetails.accountNumber)
        self.transaction = try self.paymentDetailsUseCase.createTransaction(directoryServerId: directoryServerId)
        progressView = try self.transaction!.getProgressView()
        progressView!.start()
    } catch let error {
        print(error)
        self.view?.showErrorScreen(with: "\(error)")
        return
    }
    let authenticatingPromise = paymentDetailsUseCase.sendAuthenticationRequest(transaction: self.transaction!,
                                                                                paymentDetails: paymentDetails)
    authenticatingPromise.done { authenticationResponse in
        self.handleResponseReceived(requestSentTime: requestSentTime!, {
            progressView!.stop()
            self.handleAuthenticationResponse(authenticationResponse, for: self.transaction!)
        })
        }.catch { (error) in
            if let requestSentTime = requestSentTime {
                self.handleResponseReceived(requestSentTime: requestSentTime, {
                    if let progressView = progressView {
                        progressView.stop()
                    }
                    self.view?.showErrorScreen(with: "Transaction failed")
                })
            } else {
                self.view?.showErrorScreen(with: "Transaction failed")
            }
    }
}

If the authentication request requires a challenge, then the doChallenge method is called. This is done in the handleChallengeFlow function of the PaymentDetailsPresenterImplementation file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let challengeParameters = ChallengeParameters.init(threeDSServerTransactionID: transactionResult.dsTransID,
                                                            acsTransactionID: transactionResult.acsTransID,
                                                            acsRefNumber: transactionResult.acsReferenceNumber,
                                                            acsSignedContent: acsSignedContent)
        let challengeStatusReceiver = AppChallengeStatusReceiver(view: self.view!)
        do {
             
            try self.transaction!.doChallenge(challengeParameters: challengeParameters,
                                              challengeStatusReceiver: challengeStatusReceiver,
                                              timeOut:5,
                                              inViewController: self.view as! UIViewController)
        } catch {
            self.showTransactionError()
        }

The configuration of DS public keys and certificates is done in DSsConfiguration.plist. You can use this file as an example, while for implementation guidelines, please refer to the Netcetera iOS 3DS SDK documentation.

The images used in the NCA 3DS demo application can be found in Assets.xcassets.