Payment Initiation
Good to know: Payment Initiation is responsible for handling bank payments.
To start Payment Initiation flow, you will need to get the Payment ID and pass it to the SDK. The description of how to get the Payment ID can be found here.
Firstly, you will need to integrate kevin. API and fetch payment ID. Instructions on how to do it can be found here:
PaymentSessionConfiguration
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 totrue
, country selection will be disabled, and only preselected country banks will be available to choose from.setSkipBankSelection
- if set totrue
country, and bank selection step will be skipped, and the user will be redirected to the bank SCA immediately.setSkipAuthentication
- allows users to skip the SCA bank step and proceed directly to the payment confirmation. Bank payment needs to be initialised with an account access token. See reference here.
Example:
// initialize configuration with paymentId fetched from the API
val config = PaymentSessionConfiguration.Builder(paymentId)
.setPreselectedCountry(KevinCountry.LITHUANIA)
.setCountryFilter(
listOf(
KevinCountry.LITHUANIA,
KevinCountry.ESTONIA,
KevinCountry.LATVIA
)
)
.setBankFilter(
listOf(
"SWEDBANK_LT",
"SEB_LT",
"LUMINOR_LT",
"REVOLUT_LT"
)
)
.setPreselectedBank("SOME_BANK_ID")
.build()
linkAccount.launch(config)
If you use intend to call account linking from Activity or Fragment, registerForActivityResult can be used for that:
class SampleFragment : Fragment() {
private val viewModel: SampleViewModel by viewModels()
private val makePayment = registerForActivityResult(PaymentSessionContract()) {
when (result) {
is SessionResult.Success -> {
// get payment id by calling it.value.paymentId
}
is SessionResult.Canceled -> {
// do something on user cancellation
}
is SessionResult.Failure -> {
// do something on failure
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
observeChanges()
return FragmentMainBinding.inflate(inflater).apply {
makeBankPaymentButton.setDebounceClickListener {
// fetch payment id from kevin. API
viewModel.initializePayment()
}
}.root
}
private fun observeChanges() {
// observe returned payment here
lifecycleScope.launchWhenStarted {
viewModel.payment.onEach { payment ->
openPaymentSession(payment)
}.launchIn(this)
}
}
private fun openPaymentSession(payment: ApiPayment) {
val config = PaymentSessionConfiguration.Builder(payment.id)
.setPreselectedCountry(KevinCountry.LITHUANIA)
.build()
makePayment.launch(config)
}
}
The same solution is available on Jetpack Compose:
@Composable
fun SampleWindow(viewModel: SampleViewModel) {
val paymentLauncher = rememberLauncherForActivityResult(PaymentSessionContract()) { result ->
when (result) {
is SessionResult.Success -> {
// get payment id by calling it.value.paymentId
}
is SessionResult.Canceled -> {
// do something on user cancellation
}
is SessionResult.Failure -> {
// do something on failure
}
}
}
LaunchedEffect(viewModel) {
viewModel.payment.onEach { payment ->
val config = PaymentSessionConfiguration.Builder(payment.id)
.setPreselectedCountry(KevinCountry.LITHUANIA)
.build()
paymentLauncher.launch(config)
}.launchIn(this)
}
Scaffold {
Button(
text = "Make Payment",
onClick = {
// fetch payment id from kevin. API
viewModel.initializePayment()
}
)
}
}
For non Fragment, Activity or Jetpack Compose windows please use custom ActivityResultRegistry:
@Singleton
class PaymentObserver @Inject constructor() : DefaultLifecycleObserver {
private var callback: ((SessionResult<PaymentSessionResult>) -> Unit)? = null
private var registry : WeakReference<ActivityResultRegistry?> = WeakReference(null)
private lateinit var paymentLauncher: ActivityResultLauncher<PaymentSessionConfiguration>
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
paymentLauncher = registry.get()!!.register("session", PaymentSessionContract()) {
callback?.invoke(it)
}
}
fun startPaymentInitiation(
configuration: PaymentSessionConfiguration,
callback: ((SessionResult<PaymentSessionResult>) -> Unit)? = null
) {
this.callback = callback
paymentLauncher.launch(configuration)
}
fun setRegistry(activityResultRegistry: ActivityResultRegistry) {
registry = WeakReference(activityResultRegistry)
}
fun disposeReferences() {
callback = null
registry = WeakReference(null)
}
}
And in your root Activity listen to the newly created observer:
class SampleActivity : AppCompatActivity() {
@Inject lateinit var paymentObserver: PaymentObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
with(paymentObserver) {
setRegistry(activityResultRegistry)
lifecycle.addObserver(this)
}
}
override fun onDestroy() {
super.onDestroy()
with(paymentObserver) {
disposeReferences()
lifecycle.removeObserver(this)
}
}
}
Last modified 13d ago