diff --git a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/MobilePhoneTanMedium.kt b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/MobilePhoneTanMedium.kt index 5c3f2d53..b252f30e 100644 --- a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/MobilePhoneTanMedium.kt +++ b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/MobilePhoneTanMedium.kt @@ -1,10 +1,13 @@ package net.codinux.banking.client.model.tan +import net.codinux.banking.client.model.BankAccountIdentifier import net.codinux.banking.client.model.config.NoArgConstructor @NoArgConstructor open class MobilePhoneTanMedium( - val phoneNumber: String? + val phoneNumber: String?, + val concealedPhoneNumber: String? = null, + val smsDebitAccount: BankAccountIdentifier? = null ) { override fun toString() = phoneNumber ?: "No phone number" } \ No newline at end of file diff --git a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanChallenge.kt b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanChallenge.kt index 3b40a885..f296af7e 100644 --- a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanChallenge.kt +++ b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanChallenge.kt @@ -29,7 +29,14 @@ open class TanChallenge( */ val availableTanMethods: List, - // TODO: add available TanMedia - which frontend cannot know when adding an account - and selected TanMedium + /** + * Identifier of selected TanMedium. + * + * As [availableTanMedia] also contains selected TanMedium, we didn't want to duplicate this object. Use + * [selectedTanMedium] to get selected TanMedium or iterate over [availableTanMedia] and filter selected one by this medium name. + */ + val selectedTanMediumName: String? = null, + val availableTanMedia: List = emptyList(), val tanImage: TanImage? = null, val flickerCode: FlickerCode? = null, @@ -41,6 +48,10 @@ open class TanChallenge( val selectedTanMethod: TanMethod get() = availableTanMethods.first { it.identifier == selectedTanMethodId } + @get:JsonIgnore + val selectedTanMedium: TanMedium? + get() = availableTanMedia.firstOrNull { it.mediumName == selectedTanMediumName } + override fun toString(): String { return "$selectedTanMethod $forAction: $messageToShowToUser" + when (type) { diff --git a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanGeneratorTanMedium.kt b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanGeneratorTanMedium.kt index 6aea9f96..6fff2ccf 100644 --- a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanGeneratorTanMedium.kt +++ b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanGeneratorTanMedium.kt @@ -1,10 +1,15 @@ package net.codinux.banking.client.model.tan +import kotlinx.datetime.LocalDate import net.codinux.banking.client.model.config.NoArgConstructor @NoArgConstructor open class TanGeneratorTanMedium( - val cardNumber: String + val cardNumber: String, + val cardSequenceNumber: String? = null, + val cardType: Int? = null, + val validFrom: LocalDate? = null, + val validTo: LocalDate? = null ) { override fun toString() = cardNumber } \ No newline at end of file diff --git a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMedium.kt b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMedium.kt index ce5817cb..9d182ff9 100644 --- a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMedium.kt +++ b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMedium.kt @@ -5,7 +5,7 @@ import net.codinux.banking.client.model.config.NoArgConstructor @NoArgConstructor open class TanMedium( val type: TanMediumType, - val displayName: String, + val mediumName: String?, val status: TanMediumStatus, /** * Only set if [type] is [TanMediumType.TanGenerator]. @@ -16,5 +16,5 @@ open class TanMedium( */ val mobilePhone: MobilePhoneTanMedium? = null ) { - override fun toString() = "$displayName $status" + override fun toString() = "$mediumName $status" } \ No newline at end of file diff --git a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMediumStatus.kt b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMediumStatus.kt index 771ec25e..9d8c4400 100644 --- a/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMediumStatus.kt +++ b/BankingClientModel/src/commonMain/kotlin/net/codinux/banking/client/model/tan/TanMediumStatus.kt @@ -1,7 +1,25 @@ package net.codinux.banking.client.model.tan enum class TanMediumStatus { + + /** + * Die Bank zeigt an, dass es eine TAN-Verifikation gegen dieses Medium vornimmt. + */ Used, - Available + /** + * Das Medium kann genutzt werden, muss aber zuvor mit „TAN-Generator an- bzw. ummelden (HKTAU)“ aktiv gemeldet werden. + */ + Available, + + /** + * Mit der ersten Nutzung der Folgekarte wird die zur Zeit aktive Karte gesperrt. + */ + ActiveFollowUpCard, + + /** + * Das Medium kann mit dem Geschäftsvorfall „TAN-Medium an- bzw. ummelden (HKTAU)“ aktiv gemeldet werden. + * Die aktuelle Karte kann dann nicht mehr genutzt werden. + */ + AvailableFollowUpCard } \ No newline at end of file diff --git a/FinTs4jBankingClient/src/commonMain/kotlin/net/codinux/banking/client/fints4k/FinTs4kMapper.kt b/FinTs4jBankingClient/src/commonMain/kotlin/net/codinux/banking/client/fints4k/FinTs4kMapper.kt index a9728aa3..1f1d2ea0 100644 --- a/FinTs4jBankingClient/src/commonMain/kotlin/net/codinux/banking/client/fints4k/FinTs4kMapper.kt +++ b/FinTs4jBankingClient/src/commonMain/kotlin/net/codinux/banking/client/fints4k/FinTs4kMapper.kt @@ -17,6 +17,10 @@ import net.dankito.banking.client.model.parameter.RetrieveTransactions import net.dankito.banking.client.model.response.ErrorCode import net.codinux.banking.fints.mapper.FinTsModelMapper import net.codinux.banking.fints.model.* +import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium +import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium +import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium +import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi @@ -124,13 +128,16 @@ open class FinTs4kMapper { val tanMethods = challenge.bank.tanMethodsAvailableForUser.map { mapTanMethod(it) } val selectedTanMethodId = challenge.tanMethod.securityFunction.code + val tanMedia = challenge.bank.tanMedia.map { mapTanMedium(it) } + val selectedTanMediumName = challenge.bank.selectedTanMedium?.mediumName + val customer = mapToCustomerAccountViewInfo(challenge.bank) val account = challenge.account?.let { mapToBankAccountViewInfo(it) } val tanImage = if (challenge is ImageTanChallenge) mapTanImage(challenge.image) else null val flickerCode = if (challenge is FlickerCodeTanChallenge) mapFlickerCode(challenge.flickerCode) else null - return TanChallenge(type, action, challenge.messageToShowToUser, selectedTanMethodId, tanMethods, tanImage, flickerCode, customer, account) + return TanChallenge(type, action, challenge.messageToShowToUser, selectedTanMethodId, tanMethods, selectedTanMediumName, tanMedia, tanImage, flickerCode, customer, account) } protected open fun mapTanChallengeType(challenge: net.codinux.banking.fints.model.TanChallenge): TanChallengeType = when { @@ -160,6 +167,36 @@ open class FinTs4kMapper { return Base64.Default.encode(bytes) } + protected open fun mapTanMedium(tanMedium: TanMedium) = net.codinux.banking.client.model.tan.TanMedium( + mapTanMediumType(tanMedium), tanMedium.mediumName, mapTanMediumStatus(tanMedium.status), + (tanMedium as? TanGeneratorTanMedium)?.let { mapTanGeneratorTanMedium(it) }, + (tanMedium as? MobilePhoneTanMedium)?.let { mapMobilePhoneTanMedium(it) } + ) + + protected open fun mapTanMediumStatus(status: TanMediumStatus): net.codinux.banking.client.model.tan.TanMediumStatus = when (status) { + TanMediumStatus.Aktiv -> net.codinux.banking.client.model.tan.TanMediumStatus.Used + TanMediumStatus.Verfuegbar -> net.codinux.banking.client.model.tan.TanMediumStatus.Available + TanMediumStatus.AktivFolgekarte -> net.codinux.banking.client.model.tan.TanMediumStatus.ActiveFollowUpCard + TanMediumStatus.VerfuegbarFolgekarte -> net.codinux.banking.client.model.tan.TanMediumStatus.AvailableFollowUpCard + } + + protected open fun mapMobilePhoneTanMedium(tanMedium: MobilePhoneTanMedium) = net.codinux.banking.client.model.tan.MobilePhoneTanMedium( + tanMedium.phoneNumber, tanMedium.concealedPhoneNumber, + // TODO (but with very low priority): Map smsDebitAccount from segment +// tanMedium.smsDebitAccount?.let { BankAccountIdentifier(it.accountNumber, it.subAccountAttribute, it.iban) } + ) + + protected open fun mapTanGeneratorTanMedium(tanMedium: TanGeneratorTanMedium) = net.codinux.banking.client.model.tan.TanGeneratorTanMedium( + tanMedium.cardNumber, tanMedium.cardSequenceNumber, tanMedium.cardType, + tanMedium.validFrom, tanMedium.validTo + ) + + protected open fun mapTanMediumType(tanMedium: TanMedium): TanMediumType = when { + tanMedium is MobilePhoneTanMedium -> TanMediumType.MobilePhone + tanMedium is TanGeneratorTanMedium -> TanMediumType.TanGenerator + else -> TanMediumType.Generic + } + protected open fun mapFlickerCode(flickerCode: net.codinux.banking.fints.tan.FlickerCode): FlickerCode = FlickerCode(flickerCode.challengeHHD_UC, flickerCode.parsedDataSet, mapException(flickerCode.decodingError))