Implemented parsing HITANS version 7

This commit is contained in:
dankito 2020-12-12 00:21:04 +01:00
parent 87662d776a
commit e08076de17
9 changed files with 220 additions and 45 deletions

View File

@ -1141,13 +1141,13 @@ open class FinTsClient(
return when {
// 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 ZkaTanMethod is set to 'HHDOPT1' -> handle ChipTanManuell before ChipTanFlickercode
parameters.zkaTanMethod == ZkaTanMethod.HHD || name.contains("manuell") ->
// but its DkTanMethod is set to 'HHDOPT1' -> handle ChipTanManuell before ChipTanFlickercode
parameters.dkTanMethod == DkTanMethod.HHD || name.contains("manuell") ->
TanMethodType.ChipTanManuell
// names are like 'chipTAN optisch/comfort', 'SmartTAN (plus) optic/USB', 'chipTAN (Flicker)' and
// technical identification is 'HHDOPT1'
parameters.zkaTanMethod == ZkaTanMethod.HHDOPT1 ||
parameters.dkTanMethod == DkTanMethod.HHDOPT1 ||
tanMethodNameContains(name, "optisch", "optic", "comfort", "flicker") ->
TanMethodType.ChipTanFlickercode
@ -1172,7 +1172,8 @@ open class FinTsClient(
// '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
tanMethodNameContains(name, "push", "app", "BestSign", "SecureGo", "TAN2go", "activeTAN", "easyTAN", "SecurePlus", "TAN+")
parameters.dkTanMethod == DkTanMethod.App
|| tanMethodNameContains(name, "push", "app", "BestSign", "SecureGo", "TAN2go", "activeTAN", "easyTAN", "SecurePlus", "TAN+")
|| technicalTanMethodIdentificationContains(parameters, "SECURESIGN", "PPTAN") ->
TanMethodType.AppTan
@ -1185,8 +1186,8 @@ open class FinTsClient(
return when {
technicalTanMethodIdentificationContains(parameters, "HHD1.4") -> HHDVersion.HHD_1_4
technicalTanMethodIdentificationContains(parameters, "HHD1.3") -> HHDVersion.HHD_1_3
parameters.versionZkaTanMethod?.contains("1.4") == true -> HHDVersion.HHD_1_4
parameters.versionZkaTanMethod?.contains("1.3") == true -> HHDVersion.HHD_1_4
parameters.versionDkTanMethod?.contains("1.4") == true -> HHDVersion.HHD_1_4
parameters.versionDkTanMethod?.contains("1.3") == true -> HHDVersion.HHD_1_4
else -> null
}
}

View File

@ -1,7 +1,7 @@
package net.dankito.banking.fints.messages.datenelemente.implementierte.tan
enum class ZkaTanMethod {
enum class DkTanMethod {
HHD,
@ -11,8 +11,10 @@ enum class ZkaTanMethod {
mobileTAN,
// values not specified by standard but found out there in the wild
App,
appTAN
Decoupled,
DecoupledPush
}

View File

@ -10,7 +10,7 @@ open class TanMethod(
open val type: TanMethodType,
open val hhdVersion: HHDVersion? = null,
open val maxTanInputLength: Int? = null,
open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
open val allowedTanFormat: AllowedTanFormat? = null,
open val nameOfTanMediumRequired: Boolean = false
) {

View File

@ -390,33 +390,57 @@ open class ResponseParser(
var remainingDataElements = methodsDataElements
while (remainingDataElements.size >= 20) { // parameters have at least 20 data elements, the last element is optional
val dataElementForNextMethod = if (remainingDataElements.size >= 21) remainingDataElements.subList(0, 21)
else remainingDataElements.subList(0, 20)
val methodParameters = mapToSingleTanMethodParameters(dataElementForNextMethod)
val methodParameters = mapToSingleTanMethodParameters(remainingDataElements)
parsedMethodsParameters.add(methodParameters)
val has21ElementsParsed = methodParameters.countSupportedActiveTanMedia != null ||
(dataElementForNextMethod.size >= 21 && dataElementForNextMethod[20].isBlank())
if (has21ElementsParsed) remainingDataElements = remainingDataElements.subList(21, remainingDataElements.size)
else remainingDataElements = remainingDataElements.subList(20, remainingDataElements.size)
val countParsedDataElements = determineCountParsedTanMethodParametersDataElements(remainingDataElements, methodParameters)
remainingDataElements = remainingDataElements.subList(countParsedDataElements, remainingDataElements.size)
}
return parsedMethodsParameters
}
protected open fun determineCountParsedTanMethodParametersDataElements(remainingDataElements: List<String>, parsedMethodParameters: TanMethodParameters): Int {
// last TanMethodParameters data elements
if (remainingDataElements.size == 20) {
return 20
}
else if (remainingDataElements.size == 21) {
return 21
}
if (parsedMethodParameters.dkTanMethod == DkTanMethod.Decoupled) {
if (parsedMethodParameters.periodicStateRequestsAllowedForDecoupled != null) {
return 26
}
else if (parsedMethodParameters.manualConfirmationAllowedForDecoupled != null) {
return 26
}
else {
return 24
}
}
if (parsedMethodParameters.countSupportedActiveTanMedia != null || remainingDataElements[20].isBlank()) {
return 21
}
return 20
}
protected open fun mapToSingleTanMethodParameters(methodDataElements: List<String>): TanMethodParameters {
val dkTanMethod = tryToParseDkTanMethod(methodDataElements[3])
val isDecoupledTanMethod = dkTanMethod == DkTanMethod.Decoupled || dkTanMethod == DkTanMethod.DecoupledPush
return TanMethodParameters(
parseCodeEnum(methodDataElements[0], Sicherheitsfunktion.values()),
parseCodeEnum(methodDataElements[1], TanProcess.values()),
parseString(methodDataElements[2]),
tryToParseZkaTanMethod(methodDataElements[3]),
dkTanMethod,
parseStringToNullIfEmpty(methodDataElements[4]),
parseString(methodDataElements[5]),
parseInt(methodDataElements[6]),
parseCodeEnum(methodDataElements[7], AllowedTanFormat.values()),
if (isDecoupledTanMethod) null else parseNullableInt(methodDataElements[6]),
if (isDecoupledTanMethod) null else parseCodeEnum(methodDataElements[7], AllowedTanFormat.values()),
parseString(methodDataElements[8]),
parseInt(methodDataElements[9]),
// for HITANS 4 and 5 here is another "Anzahl unterstützter aktiver TAN-Listen" Integer element
@ -431,30 +455,35 @@ open class ResponseParser(
parseCodeEnum(methodDataElements[17], Initialisierungsmodus.values()),
parseCodeEnum(methodDataElements[18], BezeichnungDesTanMediumsErforderlich.values()),
parseBoolean(methodDataElements[19]),
if (methodDataElements.size > 20) parseNullableInt(methodDataElements[20]) else null
if (methodDataElements.size > 20) parseNullableInt(methodDataElements[20]) else null,
if (isDecoupledTanMethod && methodDataElements.size > 21) parseNullableInt(methodDataElements[21]) else null,
if (isDecoupledTanMethod && methodDataElements.size > 22) parseNullableInt(methodDataElements[22]) else null,
if (isDecoupledTanMethod && methodDataElements.size > 23) parseNullableInt(methodDataElements[23]) else null,
if (isDecoupledTanMethod && methodDataElements.size > 24) parseNullableBoolean(methodDataElements[24]) else null,
if (isDecoupledTanMethod && methodDataElements.size > 25) parseNullableBoolean(methodDataElements[25]) else null
)
}
protected open fun tryToParseZkaTanMethod(mayZkaTanMethod: String): ZkaTanMethod? {
if (mayZkaTanMethod.isBlank()) {
protected open fun tryToParseDkTanMethod(mayDkTanMethod: String): DkTanMethod? {
if (mayDkTanMethod.isBlank()) {
return null
}
try {
val lowerCaseMayZkaTanMethod = mayZkaTanMethod.toLowerCase()
val lowerCaseMayDkTanMethod = mayDkTanMethod.toLowerCase()
if (lowerCaseMayZkaTanMethod == "mobiletan" || lowerCaseMayZkaTanMethod == "mtan") {
return ZkaTanMethod.mobileTAN
if (lowerCaseMayDkTanMethod == "mobiletan" || lowerCaseMayDkTanMethod == "mtan") {
return DkTanMethod.mobileTAN
}
if (lowerCaseMayZkaTanMethod == "apptan" || lowerCaseMayZkaTanMethod == "phototan") {
return ZkaTanMethod.appTAN
if (lowerCaseMayDkTanMethod == "apptan" || lowerCaseMayDkTanMethod == "phototan") {
return DkTanMethod.App
}
// TODO: what about these values, all returned by banks in anonymous dialog initialization:
// BestSign, HHDUSB1, Secoder_UC, ZkaTANMode, photoTAN, QRTAN, 1822TAN+
return ZkaTanMethod.valueOf(mayZkaTanMethod)
return DkTanMethod.valueOf(mayDkTanMethod)
} catch (ignored: Exception) { }
return null
@ -892,6 +921,14 @@ open class ResponseParser(
.replace(Separators.MaskingCharacter + Separators.MaskingCharacter, Separators.MaskingCharacter)
}
protected open fun parseNullableBoolean(mayBoolean: String): Boolean? {
try {
return parseBoolean(mayBoolean)
} catch (ignored: Exception) { }
return null
}
protected open fun parseBoolean(dataElement: String): Boolean {
if ("J" == dataElement) {
return true

View File

@ -8,11 +8,11 @@ open class TanMethodParameters(
val securityFunction: Sicherheitsfunktion,
val tanProcess: TanProcess,
val technicalTanMethodIdentification: String,
val zkaTanMethod: ZkaTanMethod?,
val versionZkaTanMethod: String?,
val dkTanMethod: DkTanMethod?,
val versionDkTanMethod: String?,
val methodName: String,
val maxTanInputLength: Int,
val allowedTanFormat: AllowedTanFormat,
val maxTanInputLength: Int?,
val allowedTanFormat: AllowedTanFormat?,
val descriptionToShowToUser: String,
val maxReturnValueLength: Int,
val multipleTansAllowed: Boolean,
@ -25,7 +25,12 @@ open class TanMethodParameters(
val initializingMode: Initialisierungsmodus,
val nameOfTanMediumRequired: BezeichnungDesTanMediumsErforderlich,
val hhdUcResponseRequired: Boolean, // TODO: wird hierueber gesteuert ob eine TAN eingegeben werden muss (z. B. beim EasyTAN Verfahren muss ja keine eingegeben werden)
val countSupportedActiveTanMedia: Int?
val countSupportedActiveTanMedia: Int?,
val maxNumberOfStateRequestsForDecoupled: Int? = null,
val initialDelayInSecondsForStateRequestsForDecoupled: Int? = null,
val delayInSecondsForNextStateRequestsForDecoupled: Int? = null,
val manualConfirmationAllowedForDecoupled: Boolean? = null,
val periodicStateRequestsAllowedForDecoupled: Boolean? = null
) {

View File

@ -710,7 +710,7 @@ class ResponseParserTest : FinTsTestBase() {
@Test
fun parseTanInfo() {
fun parseTanInfo_1() {
// when
val result = underTest.parse("HITANS:171:6:4+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0'")
@ -734,6 +734,84 @@ class ResponseParserTest : FinTsTestBase() {
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
@Test
fun parseTanInfo6_2() {
// when
val result = underTest.parse("HITANS:77:6:3+1+1+1+J:N:0:942:2:MTAN2:mobileTAN::mobile TAN:6:1:SMS:2048:J:1:N:0:2:N:J:00:0:N:1:944:2:SECUREGO:mobileTAN::SecureGo:6:1:TAN:2048:J:1:N:0:2:N:J:00:0:N:1:962:2:HHD1.4:HHD:1.4:Smart-TAN plus manuell:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1:972:2:HHD1.4OPT:HHDOPT1:1.4:Smart-TAN plus optisch / USB:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1:982:2:MS1.0.0:::Smart-TAN photo:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 77, 6, 3)
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
expect(tanMethodParameters).hasSize(5)
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_942, DkTanMethod.mobileTAN, "mobile TAN")
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_944, DkTanMethod.mobileTAN, "SecureGo")
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_962, DkTanMethod.HHD, "Smart-TAN plus manuell")
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_972, DkTanMethod.HHDOPT1, "Smart-TAN plus optisch / USB")
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_982, null, "Smart-TAN photo")
}
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
@Test
fun parseTanInfo6_3() {
// when
val result = underTest.parse("HITANS:169:6:3+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 169, 6, 3)
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
expect(tanMethodParameters).hasSize(7)
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_910, null, "chipTAN manuell")
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_911, DkTanMethod.HHDOPT1, "chipTAN optisch")
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_912, null, "chipTAN-USB") // TODO: parse not specified 'HHDUSB1' to DkTanMethod?
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_913, null, "chipTAN-QR") // TODO: parse not specified 'Secoder_UC' to DkTanMethod?
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_920, null, "smsTAN")
assertTanMethodParameter(tanMethodParameters, 5, Sicherheitsfunktion.PIN_TAN_921, null, "pushTAN")
assertTanMethodParameter(tanMethodParameters, 6, Sicherheitsfunktion.PIN_TAN_900, null, "iTAN")
}
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
@Test
fun parseTanInfo6_4() {
// when
val result = underTest.parse("HITANS:54:6:3+1+1+1+N:N:0:901:2:CR#1:::SMS-TAN:6:1:SMS-TAN:256:J:2:J:0:0:N:N:01:0:N:1:904:2:CR#5 - 1.4:HHDOPT1:1.4:chipTAN comfort:6:1:chipTAN comfort:2048:N:1:N:0:0:N:N:01:0:N:1:905:2:CR#6 - 1.4:HHD:1.4:chipTAN comfort manuell:6:1:chipTAN comfort manuell:2048:N:1:N:0:0:N:N:01:0:N:0:906:2:CR#8 - MS1.0:::BV AppTAN:6:1:BV AppTAN:2048:N:1:N:0:0:N:N:01:0:N:0:907:2:MS1.0.0:::PhotoTAN:7:1:PhotoTAN:2048:N:1:N:0:0:N:J:01:0:N:0'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 54, 6, 3)
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
expect(tanMethodParameters).hasSize(5)
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_901, null, "SMS-TAN")
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_904, DkTanMethod.HHDOPT1, "chipTAN comfort")
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_905, DkTanMethod.HHD, "chipTAN comfort manuell")
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_906, null, "BV AppTAN")
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_907, null, "PhotoTAN")
}
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
private fun assertTanMethodParameter(parsedTanMethodParameters: List<TanMethodParameters>, index: Int, securityFunction: Sicherheitsfunktion,
tanMethod: DkTanMethod?, methodName: String) {
val tanMethodParameters = parsedTanMethodParameters[index]
expect(tanMethodParameters.securityFunction).toBe(securityFunction)
expect(tanMethodParameters.dkTanMethod).toBe(tanMethod)
expect(tanMethodParameters.methodName).toBe(methodName)
}
@Test
fun parseTanInfo_CountSupportedActiveTanMediaIsBlank() {
@ -807,6 +885,54 @@ class ResponseParserTest : FinTsTestBase() {
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
@Test
fun parseTanInfo7_DecoupledpushTan() {
// when
val result = underTest.parse("HITANS:177:7:4+1+1+1+N:N:0:922:2:pushTAN-dec:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:2:180:1:1:J:J'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 177, 7, 4)
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
expect(segment.maxCountJobs).toBe(1)
expect(segment.minimumCountSignatures).toBe(1)
expect(segment.securityClass).toBe(1)
expect(segment.tanProcedureParameters.oneStepProcedureAllowed).isFalse()
expect(segment.tanProcedureParameters.moreThanOneTanDependentJobPerMessageAllowed).isFalse()
expect(segment.tanProcedureParameters.jobHashValue).toBe("0")
expect(segment.tanProcedureParameters.methodParameters).hasSize(1)
val decoupledPushTanMethod = segment.tanProcedureParameters.methodParameters.first()
expect(decoupledPushTanMethod.securityFunction).toBe(Sicherheitsfunktion.PIN_TAN_922)
expect(decoupledPushTanMethod.tanProcess).toBe(TanProcess.TanProcess2)
expect(decoupledPushTanMethod.technicalTanMethodIdentification).toBe("pushTAN-dec")
expect(decoupledPushTanMethod.dkTanMethod).toBe(DkTanMethod.Decoupled)
expect(decoupledPushTanMethod.versionDkTanMethod).toBe(null)
expect(decoupledPushTanMethod.methodName).toBe("pushTAN 2.0")
expect(decoupledPushTanMethod.maxTanInputLength).toBe(null)
expect(decoupledPushTanMethod.allowedTanFormat).toBe(null)
expect(decoupledPushTanMethod.descriptionToShowToUser).toBe("Aufforderung")
expect(decoupledPushTanMethod.maxReturnValueLength).toBe(2048)
expect(decoupledPushTanMethod.multipleTansAllowed).toBe(true)
expect(decoupledPushTanMethod.timeAndDialogRelation).toBe(TanZeitUndDialogbezug.TanZeitversetztDialoguebergreifendErlaubt)
expect(decoupledPushTanMethod.cancellationAllowed).toBe(false)
expect(decoupledPushTanMethod.nameOfTanMediumRequired).toBe(BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden)
expect(decoupledPushTanMethod.countSupportedActiveTanMedia).toBe(2)
expect(decoupledPushTanMethod.maxNumberOfStateRequestsForDecoupled).toBe(180)
expect(decoupledPushTanMethod.initialDelayInSecondsForStateRequestsForDecoupled).toBe(1)
expect(decoupledPushTanMethod.delayInSecondsForNextStateRequestsForDecoupled).toBe(1)
expect(decoupledPushTanMethod.manualConfirmationAllowedForDecoupled).toBe(true)
expect(decoupledPushTanMethod.periodicStateRequestsAllowedForDecoupled).toBe(true)
}
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
}
@Test
fun parseTanResponse_NoStrongAuthenticationRequired() {

View File

@ -71,7 +71,7 @@ class BanksFinTsDetailsRetriever {
private val tanMethodTypes = mutableMapOf<TanMethodType?, MutableSet<TanMethodParameters>>()
private val tanMethodParameterTechnicalIdentification = mutableSetOf<String>()
private val tanMethodParameterVersionZkaTanMethod = mutableSetOf<String?>()
private val tanMethodParameterVersionDkTanMethod = mutableSetOf<String?>()
private val requiresSmsAbbuchungskonto = mutableListOf<BankInfo>()
private val requiresAuftraggeberkonto = mutableListOf<BankInfo>()
@ -220,7 +220,7 @@ class BanksFinTsDetailsRetriever {
}
tanMethodParameterTechnicalIdentification.add(methodParameter.technicalTanMethodIdentification)
tanMethodParameterVersionZkaTanMethod.add(methodParameter.versionZkaTanMethod)
tanMethodParameterVersionDkTanMethod.add(methodParameter.versionDkTanMethod)
if (methodParameter.smsDebitAccountRequired == SmsAbbuchungskontoErforderlich.SmsAbbuchungskontoMussAngegebenWerden) {
requiresSmsAbbuchungskonto.add(bankInfo)
@ -283,11 +283,11 @@ class BanksFinTsDetailsRetriever {
private fun printStatistics() {
log.info("Did not receive response from Banks ${printBanks(requestNotSuccessful)}")
log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.zkaTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n")
log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.zkaTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n")
log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n")
log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n")
log.info("TanMethodParameters TechnicalIdentification:${tanMethodParameterTechnicalIdentification.joinToString(", ") } \n\n")
log.info("TanMethodParameters VersionZkaTanMethod:${tanMethodParameterVersionZkaTanMethod.joinToString(", ") } \n\n")
log.info("TanMethodParameters VersionDkTanMethod:${tanMethodParameterVersionDkTanMethod.joinToString(", ") } \n\n")
log.info("Requires SmsAbbuchungskonto ${printBanks(requiresSmsAbbuchungskonto)}") // no (only 2)
log.info("Requires Auftraggeberkonto ${printBanks(requiresAuftraggeberkonto)}") // yes, a lot of (12631)

View File

@ -5,6 +5,8 @@ enum class AllowedTanFormat {
Numeric,
Alphanumeric
Alphanumeric,
TanIsEnteredOnOtherDevice
}

View File

@ -283,10 +283,11 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
open fun mapAllowedTanFormat(allowedTanFormat: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat): AllowedTanFormat {
open fun mapAllowedTanFormat(allowedTanFormat: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat?): AllowedTanFormat {
return when (allowedTanFormat) {
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric -> AllowedTanFormat.Alphanumeric
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric -> AllowedTanFormat.Numeric
null -> AllowedTanFormat.TanIsEnteredOnOtherDevice
}
}
@ -420,10 +421,11 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
open fun mapAllowedTanFormat(allowedTanFormat: AllowedTanFormat): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat {
open fun mapAllowedTanFormat(allowedTanFormat: AllowedTanFormat): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat? {
return when (allowedTanFormat) {
AllowedTanFormat.Alphanumeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric
AllowedTanFormat.Numeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric
AllowedTanFormat.TanIsEnteredOnOtherDevice -> null
}
}