diff --git a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt index ad835ad7..3aa96d6c 100644 --- a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt +++ b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt @@ -44,6 +44,12 @@ open class fints4javaBankingClient( protected val client = FinTsClientForCustomer(bank, customer, webClient, base64Service, threadPool, object : FinTsClientCallback { + + override fun askUserForTanProcedure(supportedTanProcedures: List, suggestedTanProcedure: TanProcedure?): TanProcedure? { + // we simply return suggestedTanProcedure as even so it's not user's preferred TAN procedure she still can select it in EnterTanDialog + return suggestedTanProcedure + } + override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { mapper.updateTanMediaAndProcedures(account, customer) diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index 8f979ff3..ac3092e6 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -138,6 +138,15 @@ open class FinTsClient @JvmOverloads constructor( } + // do not ask user for tan at this stage + var didOverwriteUserUnselectedTanProcedure = false + if (customer.isTanProcedureSelected == false && customer.supportedTanProcedures.isNotEmpty()) { + + didOverwriteUserUnselectedTanProcedure = true + customer.selectedTanProcedure = customer.supportedTanProcedures.first() + } + + val synchronizeCustomerResponse = synchronizeCustomerSystemId(bank, customer) getTanMediaList(bank, customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) @@ -145,6 +154,10 @@ open class FinTsClient @JvmOverloads constructor( // also check if retrieving account transactions of last 90 days without tan is supported (and thereby may retrieve first account transactions) val transactionsOfLast90DaysResponse = tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, false) + if (didOverwriteUserUnselectedTanProcedure) { + customer.resetSelectedTanProcedure() + } + return AddAccountResponse(synchronizeCustomerResponse.toResponse(), bank, customer, transactionsOfLast90DaysResponse.isSuccessful, transactionsOfLast90DaysResponse.bookedTransactions, @@ -519,11 +532,21 @@ open class FinTsClient @JvmOverloads constructor( if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user return Response(false, noTanProcedureSelected = true) } + + // we know user's supported tan procedures, now ask user which one to select + callback.askUserForTanProcedure(customer.supportedTanProcedures, selectSuggestedTanProcedure(customer))?.let { + customer.selectedTanProcedure = it + } } return Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected) } + protected open fun selectSuggestedTanProcedure(customer: CustomerData): TanProcedure? { + return customer.supportedTanProcedures.firstOrNull { it.displayName.contains("manuell", true) == false } + ?: customer.supportedTanProcedures.firstOrNull() + } + protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: MessageBuilderResult, bank: BankData, customer: CustomerData, dialogData: DialogData): Response { @@ -828,13 +851,6 @@ open class FinTsClient @JvmOverloads constructor( if (response.supportedTanProceduresForUser.isNotEmpty()) { customer.supportedTanProcedures = response.supportedTanProceduresForUser.mapNotNull { findTanProcedure(it, bank) } - - if (customer.isTanProcedureSelected == false) { - (customer.supportedTanProcedures.firstOrNull { it.displayName.contains("manuell", true) == false } - ?: customer.supportedTanProcedures.firstOrNull())?.let { - customer.selectedTanProcedure = it - } - } } } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt index 70baaa5d..6bf19391 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt @@ -1,18 +1,29 @@ package net.dankito.fints import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium -import net.dankito.fints.model.CustomerData -import net.dankito.fints.model.EnterTanGeneratorAtcResult -import net.dankito.fints.model.EnterTanResult -import net.dankito.fints.model.TanChallenge +import net.dankito.fints.model.* interface FinTsClientCallback { + /** + * When user did not select a TAN procedure, this method gets called so that user selects one. + * + * As almost all FinTS messages need the selected TAN procedure, this method gets called quite early. + * + * As a simplification fints4java already suggests which TAN procedure may is the best one for user. + * + * If you do not support an enter tan dialog or if your enter tan dialog supports selecting a TAN procedure, it's + * best returning [suggestedTanProcedure] and to not show an extra select TAN procedure dialog. + */ + fun askUserForTanProcedure(supportedTanProcedures: List, suggestedTanProcedure: TanProcedure?): TanProcedure? + fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult /** * This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator. + * + * If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterTan] */ fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult diff --git a/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java b/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java index d05abb2c..fb6aaf72 100644 --- a/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java +++ b/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java @@ -33,6 +33,13 @@ public class JavaShowcase { customer.setSelectedTanProcedure(new TanProcedure("", Sicherheitsfunktion.PIN_TAN_911, TanProcedureType.ChipTanOptisch)); FinTsClientCallback callback = new FinTsClientCallback() { + + @Nullable + @Override + public TanProcedure askUserForTanProcedure(@NotNull List supportedTanProcedures, @Nullable TanProcedure suggestedTanProcedure) { + return suggestedTanProcedure; // simply return suggestedTanProcedure as in most cases it's the best fitting one + } + @Nullable @Override public EnterTanResult enterTan(@NotNull CustomerData customer, @NotNull TanChallenge tanChallenge) { diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt index ae9d0433..c18da084 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt @@ -30,6 +30,10 @@ class FinTsClientTest { private val callback = object : FinTsClientCallback { + override fun askUserForTanProcedure(supportedTanProcedures: List, suggestedTanProcedure: TanProcedure?): TanProcedure? { + return suggestedTanProcedure // simply return suggestedTanProcedure as in most cases it's the best fitting one + } + override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { didAskUserToEnterTan.set(true)