Account Linking
Good to know: account linking is useful if you wish to use AIS (Account Information Service) or skip authentication steps when initiating payments via the in-app payments module.
For initialising the account linking flow, you will need to get state and pass it to the SDK - a description on how to get state can be found here.

Customise session configuration

Firstly, you will need to integrate kevin. API and fetch account linking state. Instructions on how to do it can be found here:
AccountSessionConfiguration has multiple settings to tackle:
  • setLinkingType - allows to choose if you want to link bank account or payment card. By default it will link bank account
  • setPreselectedCountry - preselects country in our prebuilt UI
  • setDisableCountrySelection - uses only preselected country and does not allow to change it
  • setCountryFilter - allows only certain countries to be supported
  • setBankFilter - allows to filter banks and show only selected ones to user. It accepts list of bank ids that should be show to user.
  • setPreselectedBank - preselects bank in our prebuilt UI
  • setSkipBankSelection - skips bank and country selection and redirects user to the bank SCA immediately
  • setAccountLinkingType - sets linking type to bank or card. Default is bank.
Example:
1
// initialize configuration with state fetched from the API
2
val config = AccountSessionConfiguration.Builder("state")
3
.setLinkingType(AccountLinkingType.BANK)
4
.setPreselectedCountry(KevinCountry.LITHUANIA)
5
.setCountryFilter(
6
listOf(
7
KevinCountry.LITHUANIA,
8
KevinCountry.ESTONIA,
9
KevinCountry.LATVIA
10
)
11
)
12
.setBankFilter(
13
listOf(
14
"SWEDBANK_LT",
15
"SEB_LT",
16
"LUMINOR_LT",
17
"REVOLUT_LT"
18
)
19
)
20
.setPreselectedBank("SOME_BANK_ID")
21
.build()
22
linkAccount.launch(config)
Copied!

Initialise linking callback

Fragment or Activity

If you intend to call account linking from Activity or Fragment, registerForActivityResult can be used for that:
1
class SampleFragment : Fragment() {
2
3
private val viewModel: SampleViewModel by viewModels()
4
5
private val linkAccount = registerForActivityResult(AccountSessionContract()) {
6
when (it) {
7
is SessionResult.Success -> {
8
// get linkingType by calling it.value.linkingType
9
// get account token by calling it.value.authorizationCode
10
}
11
is SessionResult.Canceled -> {
12
// do something on user cancellation
13
}
14
is SessionResult.Failure -> {
15
// do something on failure
16
}
17
}
18
}
19
20
override fun onCreateView(
21
inflater: LayoutInflater,
22
container: ViewGroup?,
23
savedInstanceState: Bundle?
24
): View {
25
observeChanges()
26
return FragmentSampleBinding.inflate(inflater).apply {
27
linkAccountButton.setDebounceClickListener {
28
// fetch account linking state from kevin. API
29
viewModel.initializeAccountLinking()
30
}
31
}.root
32
}
33
34
private fun observeChanges() {
35
// observe returned state here
36
lifecycleScope.launchWhenStarted {
37
viewModel.state.onEach { state ->
38
openAccountLinkingSession(state)
39
}.launchIn(this)
40
}
41
}
42
43
private fun openAccountLinkingSession(state: String) {
44
val config = AccountSessionConfiguration.Builder(state)
45
.setPreselectedCountry(KevinCountry.LITHUANIA)
46
.build()
47
linkAccount.launch(config)
48
}
49
}
Copied!

Jetpack Compose

The same solution is available on Jetpack Compose:
1
@Composable
2
fun SampleWindow(viewModel: SampleViewModel) {
3
4
val linkAccount = rememberLauncherForActivityResult(AccountSessionContract()) { result ->
5
when (it) {
6
is SessionResult.Success -> {
7
// get linkingType by calling it.value.linkingType
8
// get account token by calling it.value.authorizationCode
9
}
10
is SessionResult.Canceled -> {
11
// do something on user cancellation
12
}
13
is SessionResult.Failure -> {
14
// do something on failure
15
}
16
}
17
}
18
19
LaunchedEffect(viewModel) {
20
viewModel.state.onEach { state ->
21
val config = AccountSessionConfiguration.Builder(state)
22
.setPreselectedCountry(KevinCountry.LITHUANIA)
23
.build()
24
linkAccount.launch(config)
25
}.launchIn(this)
26
}
27
28
Scaffold {
29
Button(
30
text = "Link Account",
31
onClick = {
32
// fetch account linking state from kevin. API
33
viewModel.initializeAccountLinking()
34
}
35
)
36
}
37
}
Copied!

Custom solutions

For non Fragment, Activity or Jetpack Compose windows please use custom ActivityResultRegistry:
1
@Singleton
2
class AccountLinkingObserver @Inject constructor() : DefaultLifecycleObserver {
3
4
private var callback: ((SessionResult<AccountSessionResult>) -> Unit)? = null
5
private var registry : WeakReference<ActivityResultRegistry?> = WeakReference(null)
6
7
private lateinit var accountLauncher: ActivityResultLauncher<AccountSessionConfiguration>
8
9
override fun onCreate(owner: LifecycleOwner) {
10
super.onCreate(owner)
11
accountLauncher = registry.get()!!.register("session", AccountSessionContract()) {
12
callback?.invoke(it)
13
}
14
}
15
16
fun startAccountLinking(
17
configuration: AccountSessionConfiguration,
18
callback: ((SessionResult<AccountSessionResult>) -> Unit)? = null
19
) {
20
this.callback = callback
21
accountLauncher.launch(configuration)
22
}
23
24
fun setRegistry(activityResultRegistry: ActivityResultRegistry) {
25
registry = WeakReference(activityResultRegistry)
26
}
27
28
fun disposeReferences() {
29
callback = null
30
registry = WeakReference(null)
31
}
32
}
Copied!
And in your root Activity listen to the newly created observer:
1
class SampleActivity : AppCompatActivity() {
2
3
@Inject lateinit var accountLinkingObserver: AccountLinkingObserver
4
5
override fun onCreate(savedInstanceState: Bundle?) {
6
super.onCreate(savedInstanceState)
7
with(accountLinkingObserver) {
8
setRegistry(activityResultRegistry)
9
lifecycle.addObserver(this)
10
}
11
}
12
13
override fun onDestroy() {
14
super.onDestroy()
15
with(accountLinkingObserver) {
16
disposeReferences()
17
lifecycle.removeObserver(this)
18
}
19
}
20
}
Copied!