diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt index 329ad04a..57a8e259 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt @@ -1,6 +1,5 @@ package net.dankito.banking.fints -import net.dankito.banking.fints.callback.FinTsClientCallback import net.dankito.banking.fints.messages.MessageBuilder import net.dankito.banking.fints.messages.MessageBuilderResult import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens @@ -70,7 +69,7 @@ open class FinTsJobExecutor( protected open fun closeAnonymousDialog(context: JobContext, response: BankResponse) { // bank already closed dialog -> there's no need to send dialog end message - if (context.dialog.closeDialog == false || context.dialog.didBankCloseDialog) { + if (shouldNotCloseDialog(context)) { return } @@ -104,7 +103,7 @@ open class FinTsJobExecutor( bank.resetSelectedTanMethod() // this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN methods - context.startNewDialog(closeDialog, versionOfSecurityMethod = VersionDesSicherheitsverfahrens.Version_1) + context.startNewDialog(closeDialog, versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1) val message = messageBuilder.createInitDialogMessage(context) @@ -612,7 +611,7 @@ open class FinTsJobExecutor( protected open fun closeDialog(context: JobContext) { // bank already closed dialog -> there's no need to send dialog end message - if (context.dialog.closeDialog == false || context.dialog.didBankCloseDialog) { + if (shouldNotCloseDialog(context)) { return } @@ -621,6 +620,10 @@ open class FinTsJobExecutor( fireAndForgetMessage(context, dialogEndRequestBody) } + private fun shouldNotCloseDialog(context: JobContext): Boolean { + return context.dialog.closeDialog == false || context.dialog.didBankCloseDialog + } + protected open fun ensureBasicBankDataRetrieved(context: JobContext, callback: (BankResponse) -> Unit) { val bank = context.bank diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt index 00f29bb9..1988f9d4 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt @@ -60,15 +60,15 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg */ open fun createAnonymousDialogInitMessage(context: JobContext): MessageBuilderResult { - return createUnsignedMessageBuilderResult(context.dialog, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(1), context.dialog), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), context.dialog) + return createUnsignedMessageBuilderResult(context, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(1), context), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), context) )) } open fun createAnonymousDialogEndMessage(context: JobContext): MessageBuilderResult { - return createUnsignedMessageBuilderResult(context.dialog, listOf( + return createUnsignedMessageBuilderResult(context, listOf( Dialogende(generator.resetSegmentNumber(1), context.dialog) )) } @@ -117,41 +117,39 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } protected open fun createInitDialogMessage(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { - val dialog = context.dialog val segments = mutableListOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), dialog), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialog) + IdentifikationsSegment(generator.resetSegmentNumber(2), context), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), context) ) if (segmentIdForTwoStepTanProcess != null) { - segments.add(createTwoStepTanSegment(segmentIdForTwoStepTanProcess, dialog)) + segments.add(createTwoStepTanSegment(context, segmentIdForTwoStepTanProcess)) } else if (context.bank.isTanMethodSelected) { - segments.add(createTwoStepTanSegment(CustomerSegmentId.Identification, dialog)) + segments.add(createTwoStepTanSegment(context, CustomerSegmentId.Identification)) } if (context.bank.customerSystemId == KundensystemID.Anonymous) { segments.add(Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden)) } - return createSignedMessageBuilderResult(dialog, segments) + return createSignedMessageBuilderResult(context, segments) } open fun createSynchronizeCustomerSystemIdMessage(context: JobContext): MessageBuilderResult { - val dialog = context.dialog - return createSignedMessageBuilderResult(dialog, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), dialog), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialog), - createTwoStepTanSegment(CustomerSegmentId.Identification, dialog), + return createSignedMessageBuilderResult(context, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(2), context), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), context), + createTwoStepTanSegment(context, CustomerSegmentId.Identification), Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden) )) } open fun createDialogEndMessage(context: JobContext): MessageBuilderResult { - return createSignedMessageBuilderResult(context.dialog, listOf( + return createSignedMessageBuilderResult(context, listOf( Dialogende(generator.resetSegmentNumber(2), context.dialog) )) } @@ -162,34 +160,34 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val result = supportsGetTransactionsMt940(parameter.account) if (result.isJobVersionSupported) { - return createGetTransactionsMessageMt940(result, parameter, context.dialog) + return createGetTransactionsMessageMt940(context, result, parameter) } val creditCardResult = supportsGetCreditCardTransactions(parameter.account) if (creditCardResult.isJobVersionSupported) { - return createGetCreditCardTransactionsMessage(result, parameter, context.dialog) + return createGetCreditCardTransactionsMessage(context, result, parameter) } return result } - protected open fun createGetTransactionsMessageMt940(result: MessageBuilderResult, parameter: GetTransactionsParameter, - dialogContext: DialogContext): MessageBuilderResult { + protected open fun createGetTransactionsMessageMt940(context: JobContext, result: MessageBuilderResult, + parameter: GetTransactionsParameter): MessageBuilderResult { if (parameter.maxCountEntries != null) { - parameter.isSettingMaxCountEntriesAllowedByBank = determineIsSettingMaxCountEntriesAllowed(dialogContext.bank, InstituteSegmentId.AccountTransactionsMt940Parameters, listOf(5, 6, 7)) + parameter.isSettingMaxCountEntriesAllowedByBank = determineIsSettingMaxCountEntriesAllowed(context.bank, InstituteSegmentId.AccountTransactionsMt940Parameters, listOf(5, 6, 7)) } - val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, dialogContext.bank) + val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, context.bank) else if (result.isAllowed(6)) KontoumsaetzeZeitraumMt940Version6(generator.resetSegmentNumber(2), parameter) else KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter) val segments = mutableListOf(transactionsJob) - addTanSegmentIfRequired(CustomerSegmentId.AccountTransactionsMt940, dialogContext, segments) + addTanSegmentIfRequired(context, CustomerSegmentId.AccountTransactionsMt940, segments) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context, segments) } protected open fun determineIsSettingMaxCountEntriesAllowed(bank: BankData, segmentId: ISegmentId, supportedJobVersions: List): Boolean { @@ -198,14 +196,14 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg .firstOrNull { it.settingCountEntriesAllowed } != null } - protected open fun createGetCreditCardTransactionsMessage(result: MessageBuilderResult, parameter: GetTransactionsParameter, - dialogContext: DialogContext): MessageBuilderResult { + protected open fun createGetCreditCardTransactionsMessage(context: JobContext, result: MessageBuilderResult, + parameter: GetTransactionsParameter): MessageBuilderResult { val segments = mutableListOf(KreditkartenUmsaetze(generator.resetSegmentNumber(2), parameter)) - addTanSegmentIfRequired(CustomerSegmentId.CreditCardTransactions, dialogContext, segments) + addTanSegmentIfRequired(context, CustomerSegmentId.CreditCardTransactions, segments) - return createSignedMessageBuilderResult(dialogContext, segments) + return createSignedMessageBuilderResult(context, segments) } open fun supportsGetTransactions(account: AccountData): Boolean { @@ -232,9 +230,9 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val segments = mutableListOf(balanceJob) - addTanSegmentIfRequired(CustomerSegmentId.Balance, context.dialog, segments) + addTanSegmentIfRequired(context, CustomerSegmentId.Balance, segments) - return createSignedMessageBuilderResult(context.dialog, segments) + return createSignedMessageBuilderResult(context, segments) } return result @@ -261,7 +259,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg generator.resetSegmentNumber(2), tanMediaKind, tanMediumClass) ) - return createSignedMessageBuilderResult(context.dialog, segments) + return createSignedMessageBuilderResult(context, segments) } return result @@ -279,7 +277,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg context.bank, newActiveTanMedium, tan, atc) ) - return createSignedMessageBuilderResult(context.dialog, segments) + return createSignedMessageBuilderResult(context, segments) } return result @@ -294,7 +292,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier) ) - return createSignedMessageBuilderResult(createSignedMessage(context.dialog, enteredTan, segments), context.dialog, segments) + return createSignedMessageBuilderResult(context, createSignedMessage(context, enteredTan, segments), segments) } @@ -308,9 +306,9 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val segments = mutableListOf(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2), urn, context.bank.customerName, account, context.bank.bic, data)) - addTanSegmentIfRequired(segmentId, context.dialog, segments) + addTanSegmentIfRequired(context, segmentId, segments) - return createSignedMessageBuilderResult(context.dialog, segments) + return createSignedMessageBuilderResult(context, segments) } return result @@ -361,64 +359,65 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg open fun rebuildMessage(context: JobContext, message: MessageBuilderResult): MessageBuilderResult { - return createSignedMessageBuilderResult(context.dialog, message.messageBodySegments) + return createSignedMessageBuilderResult(context, message.messageBodySegments) } - protected open fun createSignedMessageBuilderResult(dialogContext: DialogContext, segments: List): MessageBuilderResult { - return createSignedMessageBuilderResult(createSignedMessage(dialogContext, segments), dialogContext, segments) + protected open fun createSignedMessageBuilderResult(context: JobContext, segments: List): MessageBuilderResult { + return createSignedMessageBuilderResult(context, createSignedMessage(context, segments), segments) } - protected open fun createSignedMessageBuilderResult(createdMessage: String, dialogContext: DialogContext, segments: List): MessageBuilderResult { + protected open fun createSignedMessageBuilderResult(context: JobContext, createdMessage: String, segments: List): MessageBuilderResult { + return createMessageBuilderResult(context, createdMessage, segments) + } + + protected open fun createUnsignedMessageBuilderResult(context: JobContext, segments: List): MessageBuilderResult { + return createMessageBuilderResult(context, createMessage(context, segments), segments) + } + + protected open fun createMessageBuilderResult(context: JobContext, createdMessage: String, segments: List): MessageBuilderResult { val message = MessageBuilderResult(createdMessage, segments) - dialogContext.previousMessageInDialog = dialogContext.currentMessage + val dialog = context.dialog - dialogContext.currentMessage = message + dialog.previousMessageInDialog = dialog.currentMessage - return message - } - - protected open fun createUnsignedMessageBuilderResult(dialogContext: DialogContext, segments: List): MessageBuilderResult { - val message = MessageBuilderResult(createMessage(dialogContext, segments), segments) - - dialogContext.previousMessageInDialog = dialogContext.currentMessage - - dialogContext.currentMessage = message + dialog.currentMessage = message return message } - open fun createSignedMessage(dialogContext: DialogContext, payloadSegments: List): String { + open fun createSignedMessage(context: JobContext, payloadSegments: List): String { - return createSignedMessage(dialogContext, null, payloadSegments) + return createSignedMessage(context, null, payloadSegments) } - open fun createSignedMessage(dialogContext: DialogContext, tan: String? = null, + open fun createSignedMessage(context: JobContext, tan: String? = null, payloadSegments: List): String { val date = utils.formatDateTodayAsInt() val time = utils.formatTimeNowAsInt() - val signedPayload = signPayload(2, dialogContext, date, time, tan, payloadSegments) + val signedPayload = signPayload(2, context, date, time, tan, payloadSegments) - val encryptedPayload = encryptPayload(dialogContext, date, time, signedPayload) + val encryptedPayload = encryptPayload(context, date, time, signedPayload) - return createMessage(dialogContext, encryptedPayload) + return createMessage(context, encryptedPayload) } - open fun createMessage(dialogContext: DialogContext, payloadSegments: List): String { + open fun createMessage(context: JobContext, payloadSegments: List): String { - dialogContext.increaseMessageNumber() + val dialog = context.dialog + dialog.increaseMessageNumber() val formattedPayload = formatPayload(payloadSegments) - val ending = Nachrichtenabschluss(generator.getNextSegmentNumber(), dialogContext) + val ending = Nachrichtenabschluss(generator.getNextSegmentNumber(), dialog) val formattedEnding = ending.format() - val messageSize = calculateMessageSize(formattedPayload, formattedEnding, dialogContext) + val messageSize = calculateMessageSize(formattedPayload, formattedEnding, dialog) - val header = Nachrichtenkopf(ISegmentNumberGenerator.FirstSegmentNumber, messageSize, dialogContext) + val header = Nachrichtenkopf(ISegmentNumberGenerator.FirstSegmentNumber, messageSize, dialog) return listOf(header.format(), formattedPayload, formattedEnding) .joinToString(Separators.SegmentSeparator, postfix = Separators.SegmentSeparator) @@ -434,14 +433,14 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - protected open fun signPayload(headerSegmentNumber: Int, dialogContext: DialogContext, date: Int, time: Int, + protected open fun signPayload(headerSegmentNumber: Int, context: JobContext, date: Int, time: Int, tan: String? = null, payloadSegments: List): List { val controlReference = createControlReference() val signatureHeader = PinTanSignaturkopf( headerSegmentNumber, - dialogContext, + context, controlReference, date, time @@ -450,7 +449,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val signatureEnding = Signaturabschluss( generator.getNextSegmentNumber(), controlReference, - dialogContext.bank.pin, + context.bank.pin, tan ) @@ -462,10 +461,10 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - protected open fun encryptPayload(dialogContext: DialogContext, date: Int, time: Int, - payload: List): List { + protected open fun encryptPayload(context: JobContext, date: Int, time: Int, + payload: List): List { - val encryptionHeader = PinTanVerschluesselungskopf(dialogContext, date, time) + val encryptionHeader = PinTanVerschluesselungskopf(context, date, time) val encryptedData = VerschluesselteDaten(formatPayload(payload) + Separators.SegmentSeparator) @@ -513,19 +512,19 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg return MessageBuilderResult(false) } - protected open fun addTanSegmentIfRequired(segmentId: CustomerSegmentId, dialogContext: DialogContext, segments: MutableList) { - if (isTanRequiredForJob(segmentId, dialogContext)) { - segments.add(createTwoStepTanSegment(segmentId, dialogContext)) + protected open fun addTanSegmentIfRequired(context: JobContext, segmentId: CustomerSegmentId, segments: MutableList) { + if (isTanRequiredForJob(context, segmentId)) { + segments.add(createTwoStepTanSegment(context, segmentId)) } } - protected open fun createTwoStepTanSegment(segmentId: CustomerSegmentId, dialogContext: DialogContext): ZweiSchrittTanEinreichung { + protected open fun createTwoStepTanSegment(context: JobContext, segmentId: CustomerSegmentId): ZweiSchrittTanEinreichung { return ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, segmentId, - tanMediaIdentifier = getTanMediaIdentifierIfRequired(dialogContext)) + tanMediaIdentifier = getTanMediaIdentifierIfRequired(context)) } - protected open fun getTanMediaIdentifierIfRequired(dialogContext: DialogContext): String? { - val bank = dialogContext.bank + protected open fun getTanMediaIdentifierIfRequired(context: JobContext): String? { + val bank = context.bank if (bank.isTanMethodSelected && bank.selectedTanMethod.nameOfTanMediumRequired) { return bank.selectedTanMedium?.mediumName @@ -534,8 +533,8 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg return null } - protected open fun isTanRequiredForJob(segmentId: CustomerSegmentId, dialogContext: DialogContext): Boolean { - return dialogContext.bank.pinInfo?.jobTanConfiguration?.first { it.segmentId == segmentId.id }?.tanRequired + protected open fun isTanRequiredForJob(context: JobContext, segmentId: CustomerSegmentId): Boolean { + return context.bank.pinInfo?.jobTanConfiguration?.first { it.segmentId == segmentId.id }?.tanRequired ?: false // TODO: actually in this case it's not allowed to execute job via PIN/TAN at all } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt index 6c661f30..b2c7bce3 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt @@ -6,18 +6,15 @@ import net.dankito.banking.fints.response.BankResponse open class DialogContext( - bank: BankData, - product: ProductData, val closeDialog: Boolean = true, var abortIfTanIsRequired: Boolean = false, var currentMessage: MessageBuilderResult? = null, var dialogId: String = InitialDialogId, var response: BankResponse? = null, var didBankCloseDialog: Boolean = false, - versionOfSecurityMethod: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2, var previousMessageInDialog: MessageBuilderResult? = null, // for PinTan almost always the case except for getting a user's TAN methods var chunkedResponseHandler: ((BankResponse) -> Unit)? = null -) : MessageBaseData(bank, product, versionOfSecurityMethod) { +) { companion object { const val InitialDialogId = "0" diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt index 4b2201b7..f4d1ae59 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/JobContext.kt @@ -8,19 +8,19 @@ import net.dankito.banking.fints.response.BankResponse open class JobContext( open val type: JobContextType, open val callback: FinTsClientCallback, - open val product: ProductData, - open val bank: BankData, + product: ProductData, + bank: BankData, /** * Only set if the current context is for a specific account (like get account's transactions). */ open val account: AccountData? = null -) { +) : MessageBaseData(bank, product) { protected open val _dialogs = mutableListOf() - open var dialog: DialogContext = DialogContext(bank, product) // create null value so that variable is not null + open var dialog: DialogContext = DialogContext() // create null value so that variable is not null protected set open val dialogs: List @@ -28,10 +28,11 @@ open class JobContext( fun startNewDialog(closeDialog: Boolean = true, dialogId: String = DialogContext.InitialDialogId, - versionOfSecurityMethod: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2, + versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2, chunkedResponseHandler: ((BankResponse) -> Unit)? = null) : DialogContext { - val newDialogContext = DialogContext(bank, product, closeDialog, dialogId = dialogId, - versionOfSecurityMethod = versionOfSecurityMethod, chunkedResponseHandler = chunkedResponseHandler) + val newDialogContext = DialogContext(closeDialog, dialogId = dialogId, chunkedResponseHandler = chunkedResponseHandler) + + this.versionOfSecurityProcedure = versionOfSecurityProcedure this.dialog = newDialogContext diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt index c5181f05..4f23b99f 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt @@ -4,7 +4,11 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur. open class MessageBaseData( - val bank: BankData, - val product: ProductData, - val versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PinTanDefaultVersion -) \ No newline at end of file + open val bank: BankData, + open val product: ProductData +) { + + open var versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PinTanDefaultVersion + protected set + +} \ No newline at end of file