Account Linking

Good to know: account linking is helpful if you wish to use AIS (Account Information Service) or skip authentication steps when initiating payments via the in-app payments module.

Firstly, you will need to integrate kevin. API and fetch account linking state.

Instructions on how to do it can be found here:

Authentication

Basic flow

Customise linking flow by tweaking our configuration:

// initialize configuration with state fetched from the API 
let configuration = KevinAccountLinkingSessionConfiguration.Builder(state: "state")
    .setPreselectedCountry(.lithuania)
    .setCountryFilter([.lithuania, .latvia, .estonia])
    .setPreselectedBank("SOME_BANK_ID")
    .setSkipBankSelection(false)
    .build()

Implement KevinAccountLinkingSessionDelegate protocol. Make sure that in onKevinAccountLinkingStarted you present returned UINavigationController:

protocol KevinAccountLinkingSessionDelegate: AnyObject {
    func onKevinAccountLinkingStarted(controller: UINavigationController)
    func onKevinAccountLinkingCanceled(error: Error?)
    func onKevinAccountLinkingSucceeded(authorizationCode: String, bank: ApiBank)
}

Call KevinAccountLinkingSession.initiateAccountLinking and listen to delegate:

KevinAccountLinkingSession.shared.delegate = self
try KevinAccountLinkingSession.shared.initiateAccountLinking(
    configuration: configuration
)

UIKit

class AccountLinkingViewController: UIViewController {
    func initiateAccointLinking() {       
        do {
            let request = AuthStateRequest(
                scopes: ["accounts_basic"],
                redirectUrl: try KevinAccountsPlugin.shared.getCallbackUrl().absoluteString
            )

            let state = try await demoApiService.fetchAuthState(request: request)

            let configuration = try KevinAccountLinkingSessionConfiguration.Builder(state: state.state)
                .setPreselectedCountry(.lithuania)
                .setCountryFilter([.lithuania, .latvia, .estonia])
                .build()
           
            // Set the delegate to obtain linking session result.
            KevinAccountLinkingSession.shared.delegate = self
            KevinAccountLinkingSession.shared.initiateAccountLinking(configuration: configuration)
        } catch {
            // handle session initiation errors
        }        
    }
}

extension AccountLinkingViewController: KevinAccountLinkingSessionDelegate {
    func onKevinAccountLinkingStarted(controller: UINavigationController) {
        present(controller, animated: true)
    }

    func onKevinAccountLinkingCanceled(error: Error?) {
        // handle linking failure and cancellation
    }

    func onKevinAccountLinkingSucceeded(authorizationCode: String, bank: ApiBank?, linkingType: KevinAccountLinkingType) {
        // handle linking success
    }
}

SwiftUI

Library is built on UIKit, however it can be easily used within SwiftUI.

Firstly, create representable for kevin. UIViewController:

import SwiftUI

struct KevinViewControllerRepresentable: UIViewControllerRepresentable {
    
    let controller: UIViewController
    
    func makeUIViewController(context: Context) -> some UIViewController {
        return controller
    }
    
    func updateUIViewController(
        _ uiViewController: UIViewControllerType, 
        context: Context
    ) { }
}

Define your view from which you will be showing representable:

import Kevin
import SwiftUI

struct SampleView: View {

    @ObservedObject var viewModel: SampleViewModel

    var body: some View {
        VStack(spacing: 20) {
            Spacer()
            
            Button {
                viewModel.invokeAccountLinkingSession()
            } label: {
                Text("Link Account")
            }.buttonStyle(MainButtonStyle())
            
            .sheet(isPresented: $viewModel.viewState.openKevin, content: {
                if let controller = viewModel.kevinController {
                    KevinViewControllerRepresentable(controller: controller)
                        .presentation(canDismissSheet: false)
                }
            })

            Spacer()
        }
        .padding(20)
    }
}

Define your ViewModel which will subscribe to KevinAccountLinkingSessionDelegate and invoke the SDK:

import Kevin
import UIKit

class SampleViewModel: ObservableObject, KevinAccountLinkingSessionDelegate {
    
    @Published var viewState = SampleViewState()
    
    var kevinController: UIViewController? = nil
    
    private let apiClient = ApiClient()
    
    func invokeAccountLinkingSession() {
        apiClient.getAuthState().done { state in
            do {
                KevinAccountLinkingSession.shared.delegate = self
                try KevinAccountLinkingSession.shared.initiateAccountLinking(
                    configuration: KevinAccountLinkingSessionConfiguration.Builder(
                        state: state.state
                    )
                    .setPreselectedCountry(.lithuania)
                    .build()
                )
            } catch {
                // do something
            }
        }.catch { error in
            // do something
        }
    }

    
    //MARK: KevinAccountLinkingSessionDelegate
    
    func onKevinAccountLinkingStarted(controller: UINavigationController) {
        self.kevinController = controller
        self.viewState = MainViewState(openKevin: true)
    }
    
    func onKevinAccountLinkingCanceled(error: Error?) {
        // notify user if error has been returned
    }
    
    func onKevinAccountLinkingSucceeded(authorizationCode: String, bank: ApiBank) {
        // notify user
    }
}

Customize session configuration

KevinAccountLinkingSessionConfiguration can be configured with following properties:

  • setPreselectedCountry - allows to set a default country to be selected in the bank selection screen. If this parameter is not specified, the selection will be based on the SIM card information. In case the SIM card information is not available, the SDK will use the device locale. If the SIM card information and device locale are unavailable, the SDK will select the first country from the country filter (or fall back to the first bank on the list).

  • setPreselectedBank - allows to specify a default bank that will be preselected in the bank selection screen.

  • setCountryFilter - allows only certain countries to be supported.

  • setBankFilter - allows to filter banks and show only selected ones to the user. It requires a list of bank IDs that should be shown.

  • setDisableCountrySelection - if set to true, country selection will be disabled, and only preselected country banks will be available to choose from.

  • setSkipBankSelection - if set to true, and bank selection step will be skipped, and the user will be redirected to the bank SCA immediately.

Example:

KevinAccountLinkingSession.shared.initiateAccountLinking(
    configuration: KevinAccountLinkingSessionConfiguration.Builder(
        state: state
    )
    .setPreselectedCountry(.lithuania)
    .setCountryFilter([.lithuania, .latvia, .estonia])
    .setPreselectedBank("SOME_BANK_ID")
    .build()
)

Last updated