diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt index 5d106ff5..b82e5131 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt @@ -12,7 +12,9 @@ import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog import net.dankito.fints.FinTsClientCallback +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.TanChallenge import net.dankito.fints.model.TanProcedure import java.util.concurrent.CountDownLatch @@ -34,6 +36,10 @@ class MainActivity : AppCompatActivity() { return getTanFromUserOffUiThread(customer, tanChallenge) } + override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult? { + return null + } + }) diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index 01d1e995..1e3a7d99 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -6,6 +6,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion +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.model.* @@ -345,6 +346,61 @@ open class FinTsClient @JvmOverloads constructor( } + open fun changeTanMediumAsync(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, + callback: (FinTsClientResponse) -> Unit) { + + threadPool.runAsync { + callback(changeTanMedium(newActiveTanMedium, bank, customer)) + } + } + + open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData): FinTsClientResponse { + + val lastCreatedMessage = messageBuilder.lastCreatedMessage + +// lastCreatedMessage?.let { closeDialog(bank, customer, ) } // TODO: close previous dialog + + + var enteredAtc: EnterTanGeneratorAtcResult? = null + + if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) { + enteredAtc = callback.enterTanGeneratorAtc(customer, newActiveTanMedium) + + if (enteredAtc == null) { + return FinTsClientResponse(Response(false, exception = + Exception("Bank requires to enter ATC and TAN in order to change TAN medium."))) // TODO: translate + } + } + + + val dialogData = DialogData() + + val initDialogResponse = initDialog(bank, customer, dialogData) + + if (initDialogResponse.successful == false) { + return FinTsClientResponse(initDialogResponse) + } + + + dialogData.increaseMessageNumber() + + val message = messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, bank, customer, dialogData, + enteredAtc?.tan, enteredAtc?.atc) + + val response = getAndHandleResponseForMessage(message, bank) + + closeDialog(bank, customer, dialogData) + + + lastCreatedMessage?.let { + resendMessageInNewDialogAsync(lastCreatedMessage, bank, customer) + } + + + return FinTsClientResponse(response) + } + + open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) { @@ -377,6 +433,37 @@ open class FinTsClient @JvmOverloads constructor( } + protected open fun resendMessageInNewDialogAsync(message: MessageBuilderResult, bank: BankData, + customer: CustomerData) { + + threadPool.runAsync { + resendMessageInNewDialog(message, bank, customer) + } + } + + protected open fun resendMessageInNewDialog(message: MessageBuilderResult, bank: BankData, + customer: CustomerData): FinTsClientResponse { + + log.info("Resending message ${message.messageBodySegments.map { it.dataElementsAndGroups.firstOrNull()?.format() }} in a new dialog") // TODO: remove again + + val dialogData = DialogData() + + val initDialogResponse = initDialog(bank, customer, dialogData) + if (initDialogResponse.successful == false) { + return FinTsClientResponse(initDialogResponse) + } + + + val newMessage = messageBuilder.rebuildMessage(message, bank, customer, dialogData) + + val response = getAndHandleResponseForMessageThatMayRequiresTan(newMessage, bank, customer, dialogData) + + closeDialog(bank, customer, dialogData) + + return FinTsClientResponse(response) + } + + protected open fun initDialog(bank: BankData, customer: CustomerData, dialogData: DialogData): Response { // we first need to retrieve supported tan procedures and jobs before we can do anything @@ -596,6 +683,13 @@ open class FinTsClient @JvmOverloads constructor( } } + // TODO: check if response contains '3931 TAN-Generator gesperrt, Synchronisierung erforderlich' or + // '3933 TAN-Generator gesperrt, Synchronisierung erforderlich Kartennummer ##########' message, + // call callback.enterAtc() and implement and call HKTSY job (p. 77) + + // TODO: also check '9931 Sperrung des Kontos nach %1 Fehlversuchen' -> if %1 == 3 synchronize TAN generator + // as it's quite unrealistic that user entered TAN wrong three times, in most cases TAN generator is not synchronized + return response } @@ -639,6 +733,10 @@ open class FinTsClient @JvmOverloads constructor( } } + response.getFirstSegmentById(InstituteSegmentId.ChangeTanMediaParameters)?.let { parameters -> + bank.changeTanMediumParameters = parameters + } + if (response.supportedJobs.isNotEmpty()) { bank.supportedJobs = response.supportedJobs } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt index d0e4d960..1b805a33 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientCallback.kt @@ -1,6 +1,8 @@ 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.TanChallenge import net.dankito.fints.model.TanProcedure @@ -11,4 +13,9 @@ interface FinTsClientCallback { fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): String? + /** + * This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator. + */ + fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult? + } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt index 018e4f74..8abfb97f 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt @@ -1,6 +1,7 @@ package net.dankito.fints import net.dankito.fints.messages.MessageBuilder +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium import net.dankito.fints.model.* import net.dankito.fints.response.ResponseParser import net.dankito.fints.response.client.AddAccountResponse @@ -40,4 +41,8 @@ open class FinTsClientForCustomer( client.doBankTransferAsync(bankTransferData, bank, customer, callback) } + open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, callback: (FinTsClientResponse) -> Unit) { + client.changeTanMediumAsync(newActiveTanMedium, bank, customer, callback) + } + } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt index b95951d0..264440ce 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt @@ -3,6 +3,7 @@ package net.dankito.fints.messages import net.dankito.fints.extensions.containsAny import net.dankito.fints.messages.datenelemente.implementierte.Aufsetzpunkt import net.dankito.fints.messages.datenelemente.implementierte.Synchronisierungsmodus +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.TanProcess @@ -14,6 +15,7 @@ import net.dankito.fints.messages.segmente.id.CustomerSegmentId import net.dankito.fints.messages.segmente.implementierte.* import net.dankito.fints.messages.segmente.implementierte.sepa.SepaEinzelueberweisung import net.dankito.fints.messages.segmente.implementierte.tan.TanGeneratorListeAnzeigen +import net.dankito.fints.messages.segmente.implementierte.tan.TanGeneratorTanMediumAnOderUmmelden import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version5 import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version6 import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version7 @@ -40,6 +42,10 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } + var lastCreatedMessage: MessageBuilderResult? = null + protected set + + /** * Um Kunden die Möglichkeit zu geben, sich anonym anzumelden, um sich bspw. über die * angebotenen Geschäftsvorfälle fremder Kreditinstitute (von denen sie keine BPD besitzen) @@ -157,6 +163,23 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg return result } + open fun createChangeTanMediumMessage(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, + dialogData: DialogData, tan: String? = null, atc: Int? = null): MessageBuilderResult { + + val result = getSupportedVersionsOfJob(CustomerSegmentId.ChangeTanMedium, customer, listOf(1, 2, 3)) + + if (result.isJobVersionSupported) { + val segments = listOf( + TanGeneratorTanMediumAnOderUmmelden(result.getHighestAllowedVersion!!, generator.resetSegmentNumber(2), + bank, customer, newActiveTanMedium, tan, atc) + ) + + return createMessageBuilderResult(bank, customer, dialogData, segments) + } + + return result + } + open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, bank: BankData, customer: CustomerData, dialogData: DialogData): String { val tanProcess = if (tanResponse.tanProcess == TanProcess.TanProcess1) TanProcess.TanProcess1 else TanProcess.TanProcess2 @@ -203,13 +226,23 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg aufsetzpunkte.forEach { it.resetContinuationId(continuationId) } + return rebuildMessage(message, bank, customer, dialogData) + } + + open fun rebuildMessage(message: MessageBuilderResult, bank: BankData, customer: CustomerData, + dialogData: DialogData): MessageBuilderResult { + dialogData.increaseMessageNumber() return createMessageBuilderResult(bank, customer, dialogData, message.messageBodySegments) } protected open fun createMessageBuilderResult(bank: BankData, customer: CustomerData, dialogData: DialogData, segments: List): MessageBuilderResult { - return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, segments), segments) + val message = MessageBuilderResult(createSignedMessage(bank, customer, dialogData, segments), segments) + + lastCreatedMessage = message + + return message } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/abgeleiteteformate/Code.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/abgeleiteteformate/Code.kt index d8caca94..d54464bf 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/abgeleiteteformate/Code.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/abgeleiteteformate/Code.kt @@ -2,14 +2,20 @@ package net.dankito.fints.messages.datenelemente.abgeleiteteformate import net.dankito.fints.messages.Existenzstatus import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum /** * Es sind nur die jeweils aufgeführten Werte zulässig. */ -abstract class Code(code: String?, val allowedValues: List, existenzstatus: Existenzstatus) +open class Code(code: String?, val allowedValues: List, existenzstatus: Existenzstatus) : AlphanumerischesDatenelement(code, existenzstatus) { + + constructor(code: ICodeEnum?, allowedValues: List, existenzstatus: Existenzstatus) + : this(code?.code, allowedValues, existenzstatus) + + override fun validate() { super.validate() diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt index fdd49198..0af98e56 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt @@ -6,7 +6,7 @@ import net.dankito.fints.messages.Existenzstatus /** * Es gilt der FinTS-Basiszeichensatz ohne die Zeichen CR und LF. */ -abstract class AlphanumerischesDatenelement @JvmOverloads constructor( +open class AlphanumerischesDatenelement @JvmOverloads constructor( alphanumericValue: String?, existenzstatus: Existenzstatus, val maxLength: Int? = null ) : TextDatenelement(alphanumericValue, existenzstatus) { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/NumerischesDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/NumerischesDatenelement.kt index 6f9225d0..d6edebba 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/NumerischesDatenelement.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/NumerischesDatenelement.kt @@ -6,7 +6,7 @@ import net.dankito.fints.messages.Existenzstatus /** * Zulässig sind lediglich die Ziffern ‘0’ bis ‘9’. Führende Nullen sind nicht zugelassen. */ -abstract class NumerischesDatenelement(val number: Int?, val numberOfDigits: Int, existenzstatus: Existenzstatus) +open class NumerischesDatenelement(val number: Int?, val numberOfDigits: Int, existenzstatus: Existenzstatus) : TextDatenelement(number?.toString(), existenzstatus) { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/DoNotPrintDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/DoNotPrintDatenelement.kt new file mode 100644 index 00000000..a0eccc1a --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/DoNotPrintDatenelement.kt @@ -0,0 +1,10 @@ +package net.dankito.fints.messages.datenelemente.implementierte + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.basisformate.TextDatenelement + + +/** + * A dummy data element for conditional data elements building to tell formatter not to print this data element + */ +open class DoNotPrintDatenelement : TextDatenelement("", Existenzstatus.NotAllowed) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt index e1e69fce..1bdff968 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt @@ -8,7 +8,7 @@ class TanGeneratorTanMedium( status: TanMediumStatus, val cardNumber: String, val followUpCardNumber: String?, - val cardType: String?, + val cardType: Int?, val validFrom: Date?, val validTo: Date?, val mediaName: String? diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/Kontoverbindung.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/Kontoverbindung.kt index 021edb29..ba6d27ff 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/Kontoverbindung.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/Kontoverbindung.kt @@ -5,6 +5,9 @@ import net.dankito.fints.messages.datenelemente.implementierte.account.KontoDepo import net.dankito.fints.messages.datenelemente.implementierte.account.Unterkontomerkmal import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung +import net.dankito.fints.model.AccountData +import net.dankito.fints.model.BankData +import net.dankito.fints.model.CustomerData /** @@ -26,4 +29,12 @@ open class Kontoverbindung( KontoDepotnummer(accountNumber, Existenzstatus.Mandatory), Unterkontomerkmal(subAccountAttribute ?: "", Existenzstatus.Optional), Kreditinstitutskennung(bankCountryCode, bankCode) -), Existenzstatus.Mandatory) \ No newline at end of file +), Existenzstatus.Mandatory) { + + constructor(bank: BankData, customer: CustomerData, account: AccountData?) + : this(bank, customer, account?.subAccountAttribute) + + constructor(bank: BankData, customer: CustomerData, subAccountAttribute: String?) + : this(bank.countryCode, bank.bankCode, customer.customerId, subAccountAttribute) + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/KontoverbindungInternational.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/KontoverbindungInternational.kt index 710c1ea2..7a80b54e 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/KontoverbindungInternational.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/account/KontoverbindungInternational.kt @@ -7,6 +7,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.account.KontoDepo import net.dankito.fints.messages.datenelemente.implementierte.account.Unterkontomerkmal import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung +import net.dankito.fints.model.AccountData import net.dankito.fints.model.BankData import net.dankito.fints.model.CustomerData @@ -32,6 +33,9 @@ open class KontoverbindungInternational( Kreditinstitutskennung(bankCountryCode ?: 0, bankCode ?: "", if (bankCountryCode != null && bankCode != null) Existenzstatus.Optional else Existenzstatus.NotAllowed) ), Existenzstatus.Mandatory) { + constructor(bank: BankData, customer: CustomerData, account: AccountData?) + : this(bank, customer, account?.subAccountAttribute) + constructor(bank: BankData, customer: CustomerData, subAccountAttribute: String?) : this(customer.iban, bank.bic, bank.countryCode, bank.bankCode, customer.customerId, subAccountAttribute) } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/Segment.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/Segment.kt index b86c7087..d5e8e2fb 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/Segment.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/Segment.kt @@ -3,6 +3,7 @@ package net.dankito.fints.messages.segmente import net.dankito.fints.messages.Nachrichtenteil import net.dankito.fints.messages.Separators import net.dankito.fints.messages.datenelemente.DatenelementBase +import net.dankito.fints.messages.datenelemente.implementierte.DoNotPrintDatenelement import java.util.regex.Pattern @@ -15,7 +16,7 @@ abstract class Segment(val dataElementsAndGroups: List) : Nach override fun format(): String { - val formattedSegment = dataElementsAndGroups.joinToString(Separators.DataElementGroupsSeparator) { it.format() } + val formattedSegment = dataElementsAndGroups.filter { it is DoNotPrintDatenelement == false }.joinToString(Separators.DataElementGroupsSeparator) { it.format() } return cutEmptyDataElementGroupsAtEndOfSegment(formattedSegment) } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/id/CustomerSegmentId.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/id/CustomerSegmentId.kt index 90c4e819..474d0f79 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/id/CustomerSegmentId.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/id/CustomerSegmentId.kt @@ -15,6 +15,8 @@ enum class CustomerSegmentId(override val id: String) : ISegmentId { TanMediaList("HKTAB"), + ChangeTanMedium("HKTAU"), + Balance("HKSAL"), AccountTransactionsMt940("HKKAZ"), diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorListeAnzeigen.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorListeAnzeigen.kt index d6493eec..5c8427cc 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorListeAnzeigen.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorListeAnzeigen.kt @@ -27,7 +27,7 @@ open class TanGeneratorListeAnzeigen( if (supportedMediaClasses.contains(tanMediumClass) == false) { throw UnsupportedOperationException("Value $tanMediumClass for TAN medium class is not valid for HKTAB version $segmentVersion. " + - "Supported values are: " + TanMediumKlasse.values().filter { it.supportedHkTabVersions.contains(segmentVersion) }.map { it.code }) + "Supported values are: " + supportedMediaClasses) } } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmelden.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmelden.kt new file mode 100644 index 00000000..991d3671 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmelden.kt @@ -0,0 +1,86 @@ +package net.dankito.fints.messages.segmente.implementierte.tan + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Datum +import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement +import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.DoNotPrintDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.NotAllowedDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.allCodes +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse +import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf +import net.dankito.fints.messages.datenelementgruppen.implementierte.account.Kontoverbindung +import net.dankito.fints.messages.datenelementgruppen.implementierte.account.KontoverbindungInternational +import net.dankito.fints.messages.segmente.Segment +import net.dankito.fints.messages.segmente.id.CustomerSegmentId +import net.dankito.fints.model.BankData +import net.dankito.fints.model.CustomerData +import net.dankito.fints.response.segments.ChangeTanMediaParameters + + +/** + * The actual job is called "TAN-Medium an- bzw. ummelden", but as TAN lists aren't supported anymore I've implemented + * it only for TAN Generators. + * + * Mit Hilfe dieses Geschäftsvorfalls kann der Kunde seinem Institut mitteilen, welches Medium (Chipkarte, + * TAN-Generator oder bilateral vereinbart) er für die Autorisierung der Aufträge per TAN verwenden wird. + * + * Welches Medium gerade aktiv ist, kann mit Hilfe des Geschäftsvorfalls „TAN-Medium anzeigen Bestand (HKTAB)“ bzw. + * für Detailinformationen zur Karte auch „Kartenanzeige anfordern (HKAZK)“ durch den Kunden erfragt werden. + * + * Der Kunde entscheidet selbst, welches seiner verfügbaren TAN-Medien er verwenden möchte. + * + * chipTAN-Verfahren: + * Steht beim chipTAN-Verfahren ein Kartenwechsel an, so kann der Kunde mit diesem Geschäftsvorfall seine Karte bzw. + * Folgekarte aktivieren. Kann der Kunde mehrere Karten verwenden, dann kann mit diesem GV die Ummeldung auf eine + * andere Karte erfolgen. Das Kreditinstitut entscheidet selbst, ob dieser GV TAN-pflichtig istoder nicht. + */ +open class TanGeneratorTanMediumAnOderUmmelden( + segmentVersion: Int, + segmentNumber: Int, + bank: BankData, + customer: CustomerData, + newActiveTanMedium: TanGeneratorTanMedium, + /** + * Has to be set if „Eingabe von ATC und TAN erforderlich“ (BPD)=“J“ + */ + tan: String? = null, + /** + * Has to be set if „Eingabe von ATC und TAN erforderlich“ (BPD)=“J“ + */ + atc: Int? = null, + /** + * An optional field and only used in version 3 + */ + iccsn: String? = null, + + parameters: ChangeTanMediaParameters = bank.changeTanMediumParameters!! +) + : Segment(listOf( + Segmentkopf(CustomerSegmentId.ChangeTanMedium, segmentVersion, segmentNumber), + Code(TanMediumKlasse.TanGenerator, allCodes(), Existenzstatus.Mandatory), + AlphanumerischesDatenelement(newActiveTanMedium.cardNumber, Existenzstatus.Mandatory), + AlphanumerischesDatenelement(newActiveTanMedium.followUpCardNumber, if (parameters.enteringFollowUpCardNumberRequired) Existenzstatus.Mandatory else Existenzstatus.NotAllowed), + if (segmentVersion > 1) NumerischesDatenelement(newActiveTanMedium.cardType, 2, if (parameters.enteringCardTypeAllowed) Existenzstatus.Optional else Existenzstatus.NotAllowed) else DoNotPrintDatenelement(), + if (segmentVersion == 2) Kontoverbindung(bank, customer, customer.accounts.firstOrNull()) else DoNotPrintDatenelement(), + if (segmentVersion >= 3 && parameters.accountInfoRequired) KontoverbindungInternational(bank, customer, customer.accounts.firstOrNull()) else DoNotPrintDatenelement(), + if (segmentVersion >= 2) Datum(newActiveTanMedium.validFrom, Existenzstatus.Optional) else DoNotPrintDatenelement(), + if (segmentVersion >= 2) Datum(newActiveTanMedium.validTo, Existenzstatus.Optional) else DoNotPrintDatenelement(), + if (segmentVersion >= 3) AlphanumerischesDatenelement(iccsn, Existenzstatus.Optional, 19) else DoNotPrintDatenelement(), + NotAllowedDatenelement(), // TAN-Listennummer not supported anymore + NumerischesDatenelement(atc, 5, if (parameters.enteringAtcAndTanRequired) Existenzstatus.Mandatory else Existenzstatus.NotAllowed), + AlphanumerischesDatenelement(tan, if (parameters.enteringAtcAndTanRequired) Existenzstatus.Mandatory else Existenzstatus.NotAllowed, 99) +)) { + + init { + if (parameters.enteringAtcAndTanRequired) { + if (atc == null || tan == null) { + throw UnsupportedOperationException("As „Eingabe von ATC und TAN erforderlich“ is set to \"J\" " + + "(ChangeTanMediaParameters.enteringAtcAndTanRequired is set to true) parameters atc and tan have to be set.") + } + } + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/BankData.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/BankData.kt index df61ed5b..0610acf1 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/BankData.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/BankData.kt @@ -3,6 +3,7 @@ package net.dankito.fints.model import net.dankito.fints.messages.datenelemente.implementierte.BPDVersion import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.HbciVersion +import net.dankito.fints.response.segments.ChangeTanMediaParameters import net.dankito.fints.response.segments.JobParameters @@ -22,6 +23,7 @@ open class BankData( var supportedHbciVersions: List = listOf(), var supportedTanProcedures: List = listOf(), + var changeTanMediumParameters: ChangeTanMediaParameters? = null, var supportedLanguages: List = listOf(), var supportedJobs: List = listOf() ) { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/EnterTanGeneratorAtcResult.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/EnterTanGeneratorAtcResult.kt new file mode 100644 index 00000000..88510962 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/EnterTanGeneratorAtcResult.kt @@ -0,0 +1,13 @@ +package net.dankito.fints.model + + +open class EnterTanGeneratorAtcResult( + val tan: String, + val atc: Int +) { + + override fun toString(): String { + return "TAN: $tan, ATC: $atc" + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt index 7cb66b27..45ca7972 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt @@ -31,6 +31,8 @@ enum class InstituteSegmentId(override val id: String) : ISegmentId { TanMediaList("HITAB"), + ChangeTanMediaParameters("HITAUS"), // there's no response data segment for HKTAU -> HITAU does not exist + Balance("HISAL"), AccountTransactionsMt940("HIKAZ") diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt index 55a38570..9c82c32b 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt @@ -8,7 +8,7 @@ import net.dankito.fints.messages.segmente.id.MessageSegmentId import net.dankito.fints.response.segments.* -open class Response constructor( +open class Response( val didReceiveResponse: Boolean, val receivedResponse: String? = null, val receivedSegments: List = listOf(), 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 98cda9e1..07a1ac67 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -94,6 +94,7 @@ open class ResponseParser @JvmOverloads constructor( InstituteSegmentId.TanInfo.id -> parseTanInfo(segment, segmentId, dataElementGroups) InstituteSegmentId.Tan.id -> parseTanResponse(segment, dataElementGroups) InstituteSegmentId.TanMediaList.id -> parseTanMediaList(segment, dataElementGroups) + InstituteSegmentId.ChangeTanMediaParameters.id -> parseChangeTanMediaParameters(segment, segmentId, dataElementGroups) InstituteSegmentId.Balance.id -> parseBalanceSegment(segment, dataElementGroups) InstituteSegmentId.AccountTransactionsMt940.id -> parseMt940AccountTransactions(segment, dataElementGroups) @@ -432,7 +433,7 @@ open class ResponseParser @JvmOverloads constructor( protected open fun parseTanGeneratorTanMedium(mediumClass: TanMediumKlasse, status: TanMediumStatus, hitabVersion: Int, dataElements: List): TanGeneratorTanMedium { - val cardType = if (hitabVersion < 2) null else parseStringToNullIfEmpty(dataElements[2]) // TODO: may parse to number + val cardType = if (hitabVersion < 2) null else parseNullableInt(dataElements[2]) // TODO: may also parse account info val validFrom = if (hitabVersion < 2) null else parseNullableDate(dataElements[8]) val validTo = if (hitabVersion < 2) null else parseNullableDate(dataElements[9]) @@ -443,6 +444,28 @@ open class ResponseParser @JvmOverloads constructor( } + protected open fun parseChangeTanMediaParameters(segment: String, segmentId: String, dataElementGroups: List): ChangeTanMediaParameters { + val jobParameters = parseJobParameters(segment, segmentId, dataElementGroups) + + val hiTausSegmentVersion = parseInt(getDataElements(dataElementGroups[0])[2]) + val changeTanGeneratorParameters = getDataElements(dataElementGroups[4]) + + val enteringCardTypeRequired = if (hiTausSegmentVersion < 2) false else parseBoolean(changeTanGeneratorParameters[3]) + val accountInfoRequired = if (hiTausSegmentVersion < 3) false else parseBoolean(changeTanGeneratorParameters[4]) + + val remainingParameters = when (hiTausSegmentVersion) { + 1 -> listOf() + 2 -> changeTanGeneratorParameters.subList(4, changeTanGeneratorParameters.size) + else -> changeTanGeneratorParameters.subList(5, changeTanGeneratorParameters.size) + } + val allowedCardTypes = remainingParameters.map { parseInt(it) } + + return ChangeTanMediaParameters(jobParameters, parseBoolean(changeTanGeneratorParameters[0]), + parseBoolean(changeTanGeneratorParameters[1]), parseBoolean(changeTanGeneratorParameters[2]), + enteringCardTypeRequired, accountInfoRequired, allowedCardTypes) + } + + protected open fun parseBalanceSegment(segment: String, dataElementGroups: List): BalanceSegment { // dataElementGroups[1] is account details diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ChangeTanMediaParameters.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ChangeTanMediaParameters.kt new file mode 100644 index 00000000..f7a16ba3 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/ChangeTanMediaParameters.kt @@ -0,0 +1,13 @@ +package net.dankito.fints.response.segments + + +open class ChangeTanMediaParameters( + parameters: JobParameters, + val enteringTanListNumberRequired: Boolean, + val enteringFollowUpCardNumberRequired: Boolean, + val enteringAtcAndTanRequired: Boolean, + val enteringCardTypeAllowed: Boolean, + val accountInfoRequired: Boolean, + val allowedCardTypes: List +) + : JobParameters(parameters) \ No newline at end of file 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 3c7aac97..4569818d 100644 --- a/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java +++ b/fints4javaLib/src/test/java/net/dankito/fints/java/JavaShowcase.java @@ -4,6 +4,7 @@ import net.dankito.fints.FinTsClient; import net.dankito.fints.FinTsClientCallback; import net.dankito.fints.banks.BankFinder; import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion; +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium; import net.dankito.fints.model.*; import net.dankito.fints.model.mapper.BankDataMapper; import net.dankito.fints.response.client.GetTransactionsResponse; @@ -45,6 +46,11 @@ public class JavaShowcase { return null; } + @Nullable + @Override + public EnterTanGeneratorAtcResult enterTanGeneratorAtc(@NotNull CustomerData customer, @NotNull TanGeneratorTanMedium tanMedium) { + return null; + } }; FinTsClient finTsClient = new FinTsClient(callback, new Java8Base64Service()); diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt index 04a904b3..afd690ca 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsClientTest.kt @@ -6,6 +6,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatus import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.fints.messages.datenelemente.implementierte.tan.TanEinsatzOption +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.model.* @@ -13,6 +14,7 @@ import net.dankito.fints.model.mapper.BankDataMapper import net.dankito.fints.response.client.FinTsClientResponse import net.dankito.fints.util.Java8Base64Service import org.assertj.core.api.Assertions.assertThat +import org.junit.Assert import org.junit.Ignore import org.junit.Test import java.util.concurrent.atomic.AtomicBoolean @@ -41,6 +43,12 @@ class FinTsClientTest { return null } + override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult? { + Assert.fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " + + "(follow-up number ${tanMedium.followUpCardNumber}). Please do this via your online banking portal or Banking UI.") + return null // should actually never be called + } + } diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt index 12b3d110..3ac55c9f 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt @@ -5,6 +5,8 @@ import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Laenderkennze import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion import net.dankito.fints.model.* +import net.dankito.fints.response.segments.ChangeTanMediaParameters +import net.dankito.fints.response.segments.JobParameters import java.math.BigDecimal import java.util.* @@ -45,6 +47,11 @@ abstract class FinTsTestBase { const val Date = 19880327 const val Time = 182752 + + + init { + Bank.changeTanMediumParameters = ChangeTanMediaParameters(JobParameters("", 1, 1, 1, ":0:0"), false, false, false, false, false, listOf()) + } } @@ -68,4 +75,9 @@ abstract class FinTsTestBase { return message.replace(0.toChar(), ' ') } + + protected open fun createEmptyJobParameters(): JobParameters { + return JobParameters("", 1, 1, 1, ":0:0") + } + } \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmeldenTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmeldenTest.kt new file mode 100644 index 00000000..8988adc7 --- /dev/null +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/tan/TanGeneratorTanMediumAnOderUmmeldenTest.kt @@ -0,0 +1,191 @@ +package net.dankito.fints.messages.segmente.implementierte.tan + +import net.dankito.fints.FinTsTestBase +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse +import net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumStatus +import net.dankito.fints.response.segments.ChangeTanMediaParameters +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() { + + companion object { + + private const val TAN = "123456" + + private const val ATC = 12345 + + private const val CardNumber = "9876543210" + + private const val FollowUpCardNumber = "02" + + private const val CardType = 11 + + private const val SegmentNumber = 3 + + private val NewActiveTanMedium = TanGeneratorTanMedium(TanMediumKlasse.TanGenerator, TanMediumStatus.Verfuegbar, CardNumber, FollowUpCardNumber, CardType, null, null, "EC-Card") + + } + + + @Test + fun format_Version1_AtcNotRequired_FollowUpCardNumberNotRequired() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:1+G+$CardNumber") + } + + @Test + fun format_Version1_AtcRequired_FollowUpCardNumberNotRequired() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:1+G+$CardNumber+++$ATC+$TAN") + } + + @Test + fun format_Version1_AtcNotRequired_FollowUpCardNumberRequired() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:1+G+$CardNumber+$FollowUpCardNumber") + } + + @Test + fun format_Version1_AtcRequired_FollowUpCardNumberRequired() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:1+G+$CardNumber+$FollowUpCardNumber++$ATC+$TAN") + } + + + @Test + fun format_Version2_AtcNotRequired_FollowUpCardNumberNotRequired_CardTypeNotAllowed() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:2+G+$CardNumber+++$CustomerId::$BankCountryCode:$BankCode") + } + + @Test + fun format_Version2_AtcRequired_FollowUpCardNumberNotRequired_CardTypeNotAllowed() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:2+G+$CardNumber+++$CustomerId::$BankCountryCode:$BankCode++++$ATC+$TAN") + } + + @Test + fun format_Version2_AtcNotRequired_FollowUpCardNumberRequired_CardTypeNotAllowed() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:2+G+$CardNumber+$FollowUpCardNumber++$CustomerId::$BankCountryCode:$BankCode") + } + + @Test + fun format_Version2_AtcNotRequired_FollowUpCardNumberNotRequired_CardTypeAllowed() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, true, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:2+G+$CardNumber++$CardType+$CustomerId::$BankCountryCode:$BankCode") + } + + @Test + fun format_Version2_AtcRequired_FollowUpCardNumberRequired_CardTypeAllowed() { + + // given + val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, true, false, listOf()) + + val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters) + + + // when + val result = underTest.format() + + + // then + assertThat(result).isEqualTo("HKTAU:$SegmentNumber:2+G+$CardNumber+$FollowUpCardNumber+$CardType+$CustomerId::$BankCountryCode:$BankCode++++$ATC+$TAN") + } + + // TODO: may also test 'gueltig ab' and 'gueltig bis' + + + // TODO: also test Version3 + +} \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt index fd0b051d..2541c094 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt @@ -710,6 +710,80 @@ class ResponseParserTest : FinTsTestBase() { } + + @Test + fun parseChangeTanMediaParametersVersion1() { + + // when + val result = underTest.parse("HITAUS:64:1:3+1+1+1+J:N:J:'") + + // then + assertSuccessfullyParsedSegment(result, InstituteSegmentId.ChangeTanMediaParameters, 64, 1, 3) + + result.getFirstSegmentById(InstituteSegmentId.ChangeTanMediaParameters)?.let { segment -> + assertThat(segment.maxCountJobs).isEqualTo(1) + assertThat(segment.minimumCountSignatures).isEqualTo(1) + assertThat(segment.securityClass).isEqualTo(1) + + assertThat(segment.enteringTanListNumberRequired).isTrue() + assertThat(segment.enteringFollowUpCardNumberRequired).isFalse() + assertThat(segment.enteringAtcAndTanRequired).isTrue() + assertThat(segment.enteringCardTypeAllowed).isFalse() + assertThat(segment.accountInfoRequired).isFalse() + assertThat(segment.allowedCardTypes).isEmpty() + } + ?: run { Assert.fail("No segment of type ChangeTanMediaParameters found in ${result.receivedSegments}") } + } + + @Test + fun parseChangeTanMediaParametersVersion2() { + + // when + val result = underTest.parse("HITAUS:64:2:3+1+1+1+N:J:N:J:11:13:15:17'") + + // then + assertSuccessfullyParsedSegment(result, InstituteSegmentId.ChangeTanMediaParameters, 64, 2, 3) + + result.getFirstSegmentById(InstituteSegmentId.ChangeTanMediaParameters)?.let { segment -> + assertThat(segment.maxCountJobs).isEqualTo(1) + assertThat(segment.minimumCountSignatures).isEqualTo(1) + assertThat(segment.securityClass).isEqualTo(1) + + assertThat(segment.enteringTanListNumberRequired).isFalse() + assertThat(segment.enteringFollowUpCardNumberRequired).isTrue() + assertThat(segment.enteringAtcAndTanRequired).isFalse() + assertThat(segment.enteringCardTypeAllowed).isTrue() + assertThat(segment.accountInfoRequired).isFalse() + assertThat(segment.allowedCardTypes).containsExactlyInAnyOrder(11, 13, 15, 17) + } + ?: run { Assert.fail("No segment of type ChangeTanMediaParameters found in ${result.receivedSegments}") } + } + + @Test + fun parseChangeTanMediaParametersVersion3() { + + // when + val result = underTest.parse("HITAUS:64:3:3+1+1+1+N:J:N:J:J:11:13:15:17'") + + // then + assertSuccessfullyParsedSegment(result, InstituteSegmentId.ChangeTanMediaParameters, 64, 3, 3) + + result.getFirstSegmentById(InstituteSegmentId.ChangeTanMediaParameters)?.let { segment -> + assertThat(segment.maxCountJobs).isEqualTo(1) + assertThat(segment.minimumCountSignatures).isEqualTo(1) + assertThat(segment.securityClass).isEqualTo(1) + + assertThat(segment.enteringTanListNumberRequired).isFalse() + assertThat(segment.enteringFollowUpCardNumberRequired).isTrue() + assertThat(segment.enteringAtcAndTanRequired).isFalse() + assertThat(segment.enteringCardTypeAllowed).isTrue() + assertThat(segment.accountInfoRequired).isTrue() + assertThat(segment.allowedCardTypes).containsExactlyInAnyOrder(11, 13, 15, 17) + } + ?: run { Assert.fail("No segment of type ChangeTanMediaParameters found in ${result.receivedSegments}") } + } + + @Test fun parseBalance() {