diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/tan/TanProcedureType.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/tan/TanProcedureType.kt index cff49031..2672afbb 100644 --- a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/tan/TanProcedureType.kt +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/tan/TanProcedureType.kt @@ -7,13 +7,15 @@ enum class TanProcedureType { ChipTanManuell, - ChipTanOptisch, + ChipTanFlickercode, + + ChipTanUsb, ChipTanQrCode, - PhotoTan, + ChipTanPhotoTanMatrixCode, SmsTan, - PushTan + AppTan } \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt index cd71051d..fe027aae 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt @@ -29,7 +29,7 @@ import javax.inject.Inject open class EnterTanDialog : DialogFragment() { companion object { - val OpticalTanProcedures = listOf(TanProcedureType.ChipTanOptisch, TanProcedureType.ChipTanQrCode, TanProcedureType.PhotoTan) + val OpticalTanProcedures = listOf(TanProcedureType.ChipTanFlickercode, TanProcedureType.ChipTanQrCode, TanProcedureType.ChipTanPhotoTanMatrixCode) const val DialogTag = "EnterTanDialog" } diff --git a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt index 2b17b074..990be575 100644 --- a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt +++ b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt @@ -217,11 +217,12 @@ open class fints4javaModelMapper { return when (type) { net.dankito.fints.model.TanProcedureType.EnterTan -> TanProcedureType.EnterTan net.dankito.fints.model.TanProcedureType.ChipTanManuell -> TanProcedureType.ChipTanManuell - net.dankito.fints.model.TanProcedureType.ChipTanOptisch -> TanProcedureType.ChipTanOptisch + net.dankito.fints.model.TanProcedureType.ChipTanFlickercode -> TanProcedureType.ChipTanFlickercode + net.dankito.fints.model.TanProcedureType.ChipTanUsb -> TanProcedureType.ChipTanUsb net.dankito.fints.model.TanProcedureType.ChipTanQrCode -> TanProcedureType.ChipTanQrCode - net.dankito.fints.model.TanProcedureType.PhotoTan -> TanProcedureType.PhotoTan + net.dankito.fints.model.TanProcedureType.ChipTanPhotoTanMatrixCode -> TanProcedureType.ChipTanPhotoTanMatrixCode net.dankito.fints.model.TanProcedureType.SmsTan -> TanProcedureType.SmsTan - net.dankito.fints.model.TanProcedureType.PushTan -> TanProcedureType.PushTan + net.dankito.fints.model.TanProcedureType.AppTan -> TanProcedureType.AppTan } } @@ -300,11 +301,12 @@ open class fints4javaModelMapper { return when (type) { TanProcedureType.EnterTan -> net.dankito.fints.model.TanProcedureType.EnterTan TanProcedureType.ChipTanManuell -> net.dankito.fints.model.TanProcedureType.ChipTanManuell - TanProcedureType.ChipTanOptisch -> net.dankito.fints.model.TanProcedureType.ChipTanOptisch + TanProcedureType.ChipTanFlickercode -> net.dankito.fints.model.TanProcedureType.ChipTanFlickercode + TanProcedureType.ChipTanUsb -> net.dankito.fints.model.TanProcedureType.ChipTanUsb TanProcedureType.ChipTanQrCode -> net.dankito.fints.model.TanProcedureType.ChipTanQrCode - TanProcedureType.PhotoTan -> net.dankito.fints.model.TanProcedureType.PhotoTan + TanProcedureType.ChipTanPhotoTanMatrixCode -> net.dankito.fints.model.TanProcedureType.ChipTanPhotoTanMatrixCode TanProcedureType.SmsTan -> net.dankito.fints.model.TanProcedureType.SmsTan - TanProcedureType.PushTan -> net.dankito.fints.model.TanProcedureType.PushTan + TanProcedureType.AppTan -> net.dankito.fints.model.TanProcedureType.AppTan } } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index ef1fe705..1679f445 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -10,6 +10,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherhe import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium import net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedienArtVersion import net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse +import net.dankito.fints.messages.datenelemente.implementierte.tan.ZkaTanProcedure import net.dankito.fints.messages.segmente.id.CustomerSegmentId import net.dankito.fints.model.* import net.dankito.fints.response.InstituteSegmentId @@ -694,10 +695,10 @@ open class FinTsClient @JvmOverloads constructor( val tanProcedure = customer.selectedTanProcedure return when (tanProcedure.type) { - TanProcedureType.ChipTanOptisch, TanProcedureType.ChipTanManuell -> + TanProcedureType.ChipTanFlickercode, TanProcedureType.ChipTanManuell -> FlickerCodeTanChallenge(FlickerCodeDecoder().decodeChallenge(challenge), messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier) - TanProcedureType.ChipTanQrCode, TanProcedureType.PhotoTan -> + TanProcedureType.ChipTanQrCode, TanProcedureType.ChipTanPhotoTanMatrixCode -> ImageTanChallenge(TanImageDecoder().decodeChallenge(challenge), messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier) else -> TanChallenge(messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier) @@ -913,28 +914,71 @@ open class FinTsClient @JvmOverloads constructor( } protected open fun mapToTanProcedureType(parameters: TanProcedureParameters): TanProcedureType? { - val nameLowerCase = parameters.procedureName.toLowerCase() + val name = parameters.procedureName.toLowerCase() return when { - nameLowerCase.contains("photo") -> TanProcedureType.PhotoTan + // names are like 'chipTAN (comfort) manuell', 'Smart(-)TAN plus (manuell)' and + // technical identification is 'HHD'. Exception: there's one that states itself as 'chipTAN (Manuell)' + // but its ZkaTanProcedure is set to 'HHDOPT1' -> handle ChipTanManuell before ChipTanFlickercode + parameters.zkaTanProcedure == ZkaTanProcedure.HHD || name.contains("manuell") -> + TanProcedureType.ChipTanManuell - nameLowerCase.contains("chiptan") -> { - return when { - nameLowerCase.contains("qr") -> TanProcedureType.ChipTanQrCode - nameLowerCase.contains("manuell") -> TanProcedureType.ChipTanManuell - else -> TanProcedureType.ChipTanOptisch - } + // names are like 'chipTAN optisch/comfort', 'SmartTAN (plus) optic/USB', 'chipTAN (Flicker)' and + // technical identification is 'HHDOPT1' + parameters.zkaTanProcedure == ZkaTanProcedure.HHDOPT1 || + tanProcedureNameContains(name, "optisch", "optic", "comfort", "flicker") -> + TanProcedureType.ChipTanFlickercode + + // 'Smart-TAN plus optisch / USB' seems to be a Flickertan procedure -> test for 'optisch' first + name.contains("usb") -> TanProcedureType.ChipTanUsb + + // QRTAN+ from 1822 direct has nothing to do with chipTAN QR. + name.contains("qr") -> { + if (tanProcedureNameContains(name, "chipTAN", "Smart")) TanProcedureType.ChipTanQrCode + else TanProcedureType.AppTan } - nameLowerCase.contains("push") -> return TanProcedureType.PushTan - nameLowerCase.contains("sms") || nameLowerCase.contains("mobile") -> return TanProcedureType.SmsTan + // photoTAN from Commerzbank (comdirect), Deutsche Bank, norisbank has nothing to do with chipTAN photo + name.contains("photo") -> { + // e.g. 'Smart-TAN photo' / description 'Challenge' + if (tanProcedureNameContains(name, "chipTAN", "Smart")) TanProcedureType.ChipTanPhotoTanMatrixCode + // e.g. 'photoTAN-Verfahren', description 'Freigabe durch photoTAN' + else TanProcedureType.AppTan + } + + tanProcedureNameContains(name, "SMS", "mobile", "mTAN") -> TanProcedureType.SmsTan + + // 'flateXSecure' identifies itself as 'PPTAN' instead of 'AppTAN' + // 'activeTAN-Verfahren' can actually be used either with an app or a reader; it's like chipTAN QR but without a chip card + tanProcedureNameContains(name, "push", "app", "BestSign", "SecureGo", "TAN2go", "activeTAN", "easyTAN", "SecurePlus", "TAN+") + || technicalTanProcedureIdentificationContains(parameters, "SECURESIGN", "PPTAN") -> + TanProcedureType.AppTan - // TODO: what about other tan procedures we're not aware of? // we filter out iTAN and Einschritt-Verfahren as they are not permitted anymore according to PSD2 else -> null } } + protected open fun tanProcedureNameContains(name: String, vararg namesToTest: String): Boolean { + namesToTest.forEach { nameToTest -> + if (name.contains(nameToTest.toLowerCase())) { + return true + } + } + + return false + } + + protected open fun technicalTanProcedureIdentificationContains(parameters: TanProcedureParameters, vararg valuesToTest: String): Boolean { + valuesToTest.forEach { valueToTest -> + if (parameters.technicalTanProcedureIdentification.contains(valueToTest, true)) { + return true + } + } + + return false + } + protected open fun isJobSupported(account: AccountData, supportedJob: JobParameters): Boolean { for (allowedJobName in account.allowedJobNames) { if (allowedJobName == supportedJob.jobName) { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/ZkaTanProcedure.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/ZkaTanProcedure.kt index f3f6182e..806d6551 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/ZkaTanProcedure.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/ZkaTanProcedure.kt @@ -13,8 +13,6 @@ enum class ZkaTanProcedure { // values not specified by standard but found out there in the wild - photoTAN, - appTAN } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/TanProcedureType.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/TanProcedureType.kt index 94f1c73d..bad52a35 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/TanProcedureType.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/TanProcedureType.kt @@ -7,14 +7,16 @@ enum class TanProcedureType { ChipTanManuell, - ChipTanOptisch, + ChipTanFlickercode, + + ChipTanUsb, ChipTanQrCode, - PhotoTan, + ChipTanPhotoTanMatrixCode, SmsTan, - PushTan + AppTan } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt index f0cb70b7..cd65a36f 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -390,7 +390,7 @@ open class ResponseParser @JvmOverloads constructor( return ZkaTanProcedure.mobileTAN } - if (lowerCaseMayZkaTanProcedure == "apptan") { + if (lowerCaseMayZkaTanProcedure == "apptan" || lowerCaseMayZkaTanProcedure == "phototan") { return ZkaTanProcedure.appTAN } diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt index 386b5875..32ca4318 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt @@ -37,7 +37,7 @@ abstract class FinTsTestBase { const val ControlReference = "4477" - val Customer = CustomerData(CustomerId, Pin, selectedTanProcedure = TanProcedure("chipTAN-optisch", SecurityFunction, TanProcedureType.ChipTanOptisch), selectedLanguage = Language) + val Customer = CustomerData(CustomerId, Pin, selectedTanProcedure = TanProcedure("chipTAN-optisch", SecurityFunction, TanProcedureType.ChipTanFlickercode), selectedLanguage = Language) val Currency = "EUR" diff --git a/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt b/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt index 84638f48..c1b48688 100644 --- a/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt +++ b/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt @@ -88,12 +88,12 @@ open class hbci4jModelMapper { net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.ChipTanQrCode, code) } else { - net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.ChipTanOptisch, code) + net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.ChipTanFlickercode, code) } } displayNameLowerCase.contains("sms") -> net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.SmsTan, code) - displayNameLowerCase.contains("push") -> net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.PushTan, code) + displayNameLowerCase.contains("push") -> net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.AppTan, code) // we filter out iTAN and Einschritt-Verfahren as they are not permitted anymore according to PSD2 else -> null