From 333747a5e4e6d31fc608d58e24b41ecfb1bc56f5 Mon Sep 17 00:00:00 2001 From: dankito Date: Tue, 12 May 2020 16:05:23 +0200 Subject: [PATCH] Implemented DialogContext to keep track of dialog's current state and to not have to pass BankData, CustomerData and ProductData to almost all methods --- .../kotlin/net/dankito/fints/FinTsClient.kt | 271 +++++++++--------- .../dankito/fints/messages/MessageBuilder.kt | 148 +++++----- .../segmente/implementierte/Dialogende.kt | 6 +- .../implementierte/IdentifikationsSegment.kt | 14 +- .../implementierte/Nachrichtenabschluss.kt | 6 +- .../implementierte/Nachrichtenkopf.kt | 8 +- .../implementierte/PinTanSignaturkopf.kt | 10 +- .../PinTanVerschluesselungskopf.kt | 10 +- .../Verarbeitungsvorbereitung.kt | 18 +- .../net/dankito/fints/model/DialogContext.kt | 29 ++ .../net/dankito/fints/model/DialogData.kt | 18 -- .../dankito/fints/model/MessageBaseData.kt | 8 + .../bankdetails/BanksFinTsDetailsRetriever.kt | 10 +- .../fints/messages/MessageBuilderTest.kt | 36 ++- .../IdentifikationsSegmentTest.kt | 3 +- .../implementierte/SignaturkopfTest.kt | 3 +- .../VerschluesselungskopfTest.kt | 3 +- 17 files changed, 312 insertions(+), 289 deletions(-) create mode 100644 fints4k/src/main/kotlin/net/dankito/fints/model/DialogContext.kt delete mode 100644 fints4k/src/main/kotlin/net/dankito/fints/model/DialogData.kt create mode 100644 fints4k/src/main/kotlin/net/dankito/fints/model/MessageBaseData.kt diff --git a/fints4k/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4k/src/main/kotlin/net/dankito/fints/FinTsClient.kt index b6d63df8..af9a2c5d 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -4,7 +4,6 @@ import net.dankito.fints.callback.FinTsClientCallback import net.dankito.fints.messages.MessageBuilder import net.dankito.fints.messages.MessageBuilderResult 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 @@ -76,35 +75,33 @@ open class FinTsClient @JvmOverloads constructor( * On success [bank] parameter is updated afterwards. */ open fun getAnonymousBankInfo(bank: BankData): FinTsClientResponse { - val dialogData = DialogData() + val dialogContext = DialogContext(bank, CustomerData.Anonymous, product) - val requestBody = messageBuilder.createAnonymousDialogInitMessage(bank, product, dialogData) + val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext) - val response = getAndHandleResponseForMessage(requestBody, bank) + val response = getAndHandleResponseForMessage(message, dialogContext) if (response.successful) { updateBankData(bank, response) - closeAnonymousDialog(dialogData, response, bank) + closeAnonymousDialog(dialogContext, response) } return FinTsClientResponse(response) } - protected open fun closeAnonymousDialog(dialogData: DialogData, response: Response, bank: BankData) { - dialogData.increaseMessageNumber() + protected open fun closeAnonymousDialog(dialogContext: DialogContext, response: Response) { + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder - response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId } + response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId } // TODO: senseful here? // TODO: move to MessageBuilder - val dialogEndRequestBody = messageBuilder.createAnonymousDialogEndMessage(bank, dialogData) + val dialogEndRequestBody = messageBuilder.createAnonymousDialogEndMessage(dialogContext) - getAndHandleResponseForMessage(dialogEndRequestBody, bank) + getAndHandleResponseForMessage(dialogEndRequestBody, dialogContext) } open fun getBankAndCustomerInfoForNewUser(bank: BankData, customer: CustomerData): AddAccountResponse { - val dialogData = DialogData() - // just to ensure settings are in its initial state and that bank sends use bank parameter (BPD), // user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure()) bank.resetBpdVersion() @@ -117,24 +114,15 @@ open class FinTsClient @JvmOverloads constructor( */ customer.resetSelectedTanProcedure() - val initDialogResponse = initDialogWithoutChecks(bank, customer, dialogData, false) + val dialogContext = DialogContext(bank, customer, product) - closeDialog(bank, customer, dialogData) + val initDialogResponse = initDialogAfterSuccessfulChecks(dialogContext, false) + + closeDialog(dialogContext) // TODO: only close dialog if a) bank didn't close it already and b) if a global flag is set to close dialog as actually it's not necessary return AddAccountResponse(initDialogResponse, bank, customer) } - - protected open fun synchronizeCustomerSystemIdIfNotDoneYet(bank: BankData, - customer: CustomerData): FinTsClientResponse { - - if (customer.customerSystemId == KundensystemID.Anonymous) { // customer system id not synchronized yet - return synchronizeCustomerSystemId(bank, customer) - } - - return FinTsClientResponse(true, true, false) - } - /** * According to specification synchronizing customer system id is required: * "Die Kundensystem-ID ist beim HBCI RAH- / RDH- sowie dem PIN/TAN-Verfahren erforderlich." @@ -148,18 +136,18 @@ open class FinTsClient @JvmOverloads constructor( */ protected open fun synchronizeCustomerSystemId(bank: BankData, customer: CustomerData): FinTsClientResponse { - val dialogData = DialogData() - val requestBody = messageBuilder.createSynchronizeCustomerSystemIdMessage(bank, customer, product, dialogData) + val dialogContext = DialogContext(bank, customer, product) + val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext) - val response = getAndHandleResponseForMessage(requestBody, bank) + val response = getAndHandleResponseForMessage(message, dialogContext) // TODO: HKSYN also contains HKTAN -> get..ThatMayRequiresTan() if (response.successful) { updateBankData(bank, response) updateCustomerData(customer, bank, response) - response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId } + response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId } - closeDialog(bank, customer, dialogData) + closeDialog(dialogContext) } return FinTsClientResponse(response) @@ -176,6 +164,8 @@ open class FinTsClient @JvmOverloads constructor( open fun addAccount(bank: BankData, customer: CustomerData): AddAccountResponse { + /* First dialog: Get user's basic data like her TAN procedures */ + val newUserInfoResponse = getBankAndCustomerInfoForNewUser(bank, customer) if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong @@ -188,14 +178,22 @@ open class FinTsClient @JvmOverloads constructor( if (customer.isTanProcedureSelected == false && customer.supportedTanProcedures.isNotEmpty()) { didOverwriteUserUnselectedTanProcedure = true - customer.selectedTanProcedure = customer.supportedTanProcedures.first() + customer.selectedTanProcedure = customer.supportedTanProcedures.first() // TODO: check if user has only one TAN procedure -> set it and we're done } + /* Second dialog: Get customer system ID */ // TODO: needed? + val synchronizeCustomerResponse = synchronizeCustomerSystemId(bank, customer) + + /* Third dialog: Get customer TAN media list */ + getTanMediaList(bank, customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) + + /* Fourth dialog: Try to retrieve account transactions of last 90 days without TAN */ + // also check if retrieving account transactions of last 90 days without tan is supported (and thereby may retrieve first account transactions) val transactionsOfLast90DaysResponses = mutableListOf() val balances = mutableMapOf() @@ -257,9 +255,9 @@ open class FinTsClient @JvmOverloads constructor( open fun getTransactions(parameter: GetTransactionsParameter, bank: BankData, customer: CustomerData, account: AccountData): GetTransactionsResponse { - val dialogData = DialogData() + val dialogContext = DialogContext(bank, customer, product) - val initDialogResponse = initDialog(bank, customer, dialogData) + val initDialogResponse = initDialog(dialogContext) if (initDialogResponse.successful == false) { return GetTransactionsResponse(initDialogResponse) @@ -269,10 +267,10 @@ open class FinTsClient @JvmOverloads constructor( var balance: BigDecimal? = null if (parameter.alsoRetrieveBalance && account.supportsRetrievingBalance) { - val balanceResponse = getBalanceAfterDialogInit(bank, customer, account, dialogData) + val balanceResponse = getBalanceAfterDialogInit(account, dialogContext) if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented - closeDialog(bank, customer, dialogData) + closeDialog(dialogContext) return GetTransactionsResponse(balanceResponse) } @@ -281,16 +279,16 @@ open class FinTsClient @JvmOverloads constructor( } if (balanceResponse.didReceiveResponse) { - dialogData.increaseMessageNumber() + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder } } - val message = messageBuilder.createGetTransactionsMessage(parameter, bank, customer, account, product, dialogData) + val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext) - val response = getAndHandleResponseForMessageThatMayRequiresTan(message, bank, customer, dialogData) + val response = getAndHandleResponseForMessageThatMayRequiresTan(message, dialogContext) - closeDialog(bank, customer, dialogData) + closeDialog(dialogContext) response.getFirstSegmentById(InstituteSegmentId.AccountTransactionsMt940)?.let { transactions -> @@ -339,14 +337,13 @@ open class FinTsClient @JvmOverloads constructor( } } - protected open fun getBalanceAfterDialogInit(bank: BankData, customer: CustomerData, account: AccountData, - dialogData: DialogData): Response { + protected open fun getBalanceAfterDialogInit(account: AccountData, dialogContext: DialogContext): Response { - dialogData.increaseMessageNumber() + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder - val balanceRequest = messageBuilder.createGetBalanceMessage(bank, customer, account, product, dialogData) + val message = messageBuilder.createGetBalanceMessage(account, dialogContext) - return getAndHandleResponseForMessage(balanceRequest, bank) + return getAndHandleResponseForMessage(message, dialogContext) } @@ -363,8 +360,8 @@ open class FinTsClient @JvmOverloads constructor( open fun getTanMediaList(bank: BankData, customer: CustomerData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): GetTanMediaListResponse { - val response = sendMessageAndHandleResponse(bank, customer) { dialogData -> - messageBuilder.createGetTanMediaListMessage(bank, customer, dialogData, tanMediaKind, tanMediumClass) + val response = sendMessageAndHandleResponse(bank, customer) { dialogContext -> + messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass) } val tanMediaList = if (response.successful == false ) null @@ -392,8 +389,8 @@ open class FinTsClient @JvmOverloads constructor( } - val response = sendMessageAndHandleResponse(bank, customer, false) { dialogData -> - messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, bank, customer, dialogData, + val response = sendMessageAndHandleResponse(bank, customer, false) { dialogContext -> + messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc) } @@ -413,8 +410,8 @@ open class FinTsClient @JvmOverloads constructor( open fun doBankTransfer(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, account: AccountData): FinTsClientResponse { - val response = sendMessageAndHandleResponse(bank, customer) { dialogData -> - messageBuilder.createBankTransferMessage(bankTransferData, bank, customer, account, dialogData) + val response = sendMessageAndHandleResponse(bank, customer) { dialogContext -> + messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext) } return FinTsClientResponse(response) @@ -422,70 +419,72 @@ open class FinTsClient @JvmOverloads constructor( protected open fun sendMessageAndHandleResponse(bank: BankData, customer: CustomerData, messageMayRequiresTan: Boolean = true, - createMessage: (DialogData) -> MessageBuilderResult): Response { - val dialogData = DialogData() + createMessage: (DialogContext) -> MessageBuilderResult): Response { - val initDialogResponse = initDialog(bank, customer, dialogData) + val dialogContext = DialogContext(bank, customer, product) + + val initDialogResponse = initDialog(dialogContext) if (initDialogResponse.successful == false) { return initDialogResponse } - dialogData.increaseMessageNumber() + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder - val message = createMessage(dialogData) + val message = createMessage(dialogContext) - val response = if (messageMayRequiresTan) getAndHandleResponseForMessageThatMayRequiresTan(message, bank, customer, dialogData) - else getAndHandleResponseForMessage(message, bank) + val response = if (messageMayRequiresTan) getAndHandleResponseForMessageThatMayRequiresTan(message, dialogContext) + else getAndHandleResponseForMessage(message, dialogContext) - closeDialog(bank, customer, dialogData) + closeDialog(dialogContext) return response } - protected open fun initDialog(bank: BankData, customer: CustomerData, dialogData: DialogData): Response { + protected open fun initDialog(dialogContext: DialogContext): Response { // we first need to retrieve supported tan procedures and jobs before we can do anything - val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(bank, customer) + val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(dialogContext.bank, dialogContext.customer) if (retrieveBasicBankDataResponse.successful == false) { return retrieveBasicBankDataResponse } // as in the next step we have to supply user's tan procedure, ensure user selected his or her - val tanProcedureSelectedResponse = ensureTanProcedureIsSelected(bank, customer) + val tanProcedureSelectedResponse = ensureTanProcedureIsSelected(dialogContext.bank, dialogContext.customer) if (tanProcedureSelectedResponse.successful == false) { return tanProcedureSelectedResponse } - return initDialogWithoutChecks(bank, customer, dialogData, true) + return initDialogAfterSuccessfulChecks(dialogContext, true) } - protected open fun initDialogWithoutChecks(bank: BankData, customer: CustomerData, dialogData: DialogData, - useStrongAuthentication: Boolean = true): Response { + protected open fun initDialogAfterSuccessfulChecks(dialogContext: DialogContext, + useStrongAuthentication: Boolean = true): Response { - val requestBody = messageBuilder.createInitDialogMessage(bank, customer, product, dialogData, useStrongAuthentication) + val message = messageBuilder.createInitDialogMessage(dialogContext, useStrongAuthentication) - val response = GetUserTanProceduresResponse(getAndHandleResponseForMessage(requestBody, bank)) + val response = GetUserTanProceduresResponse(getAndHandleResponseForMessageThatMayRequiresTan(message, dialogContext)) + dialogContext.response = response + + response.messageHeader?.let { header -> dialogContext.dialogId = header.dialogId } if (response.successful) { - updateBankData(bank, response) - updateCustomerData(customer, bank, response) - - response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId } + updateBankData(dialogContext.bank, response) + updateCustomerData(dialogContext.customer, dialogContext.bank, response) } return response } - protected open fun closeDialog(bank: BankData, customer: CustomerData, dialogData: DialogData) { - dialogData.increaseMessageNumber() + protected open fun closeDialog(dialogContext: DialogContext) { + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder - val dialogEndRequestBody = messageBuilder.createDialogEndMessage(bank, customer, dialogData) + val dialogEndRequestBody = messageBuilder.createDialogEndMessage(dialogContext) - getAndHandleResponseForMessage(dialogEndRequestBody, bank) + getAndHandleResponseForMessage(dialogEndRequestBody, dialogContext) } @@ -529,16 +528,15 @@ open class FinTsClient @JvmOverloads constructor( } - protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: MessageBuilderResult, bank: BankData, - customer: CustomerData, dialogData: DialogData): Response { - val response = getAndHandleResponseForMessage(message, bank) + protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: MessageBuilderResult, dialogContext: DialogContext): Response { + val response = getAndHandleResponseForMessage(message, dialogContext) - val handledResponse = handleMayRequiredTan(response, bank, customer, dialogData) + val handledResponse = handleMayRequiredTan(response, dialogContext) // if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt handledResponse.aufsetzpunkt?.let { continuationId -> if (handledResponse.followUpResponse == null) { // for re-sent messages followUpResponse is already set and dialog already closed -> would be overwritten with an error response that dialog is closed - handledResponse.followUpResponse = getFollowUpMessageForContinuationId(handledResponse, continuationId, message, bank, customer, dialogData) + handledResponse.followUpResponse = getFollowUpMessageForContinuationId(handledResponse, continuationId, message, dialogContext) handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null } @@ -548,47 +546,50 @@ open class FinTsClient @JvmOverloads constructor( } protected open fun getFollowUpMessageForContinuationId(response: Response, continuationId: String, message: MessageBuilderResult, - bank: BankData, customer: CustomerData, dialogData: DialogData): Response? { + dialogContext: DialogContext): Response? { - messageBuilder.rebuildMessageWithContinuationId(message, continuationId, bank, customer, dialogData)?.let { followUpMessage -> - return getAndHandleResponseForMessageThatMayRequiresTan(followUpMessage, bank, customer, dialogData) + messageBuilder.rebuildMessageWithContinuationId(message, continuationId, dialogContext)?.let { followUpMessage -> + return getAndHandleResponseForMessageThatMayRequiresTan(followUpMessage, dialogContext) } return null } - protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: String, bank: BankData, - customer: CustomerData, dialogData: DialogData): Response { - val response = getAndHandleResponseForMessage(message, bank) + protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: String, dialogContext: DialogContext): Response { + val response = getAndHandleResponseForMessage(message, dialogContext) - return handleMayRequiredTan(response, bank, customer, dialogData) + return handleMayRequiredTan(response, dialogContext) } - protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, bank: BankData): Response { + protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, dialogContext: DialogContext): Response { message.createdMessage?.let { requestBody -> - return getAndHandleResponseForMessage(requestBody, bank) + return getAndHandleResponseForMessage(requestBody, dialogContext) } return Response(false, messageCreationError = message) } - protected open fun getAndHandleResponseForMessage(requestBody: String, bank: BankData): Response { - val webResponse = getResponseForMessage(requestBody, bank) + protected open fun getAndHandleResponseForMessage(requestBody: String, dialogContext: DialogContext): Response { + val webResponse = getResponseForMessage(requestBody, dialogContext.bank.finTs3ServerAddress) - return handleResponse(webResponse, bank) + val response = handleResponse(webResponse, dialogContext) + + dialogContext.response = response + + return response } - protected open fun getResponseForMessage(requestBody: String, bank: BankData): WebClientResponse { + protected open fun getResponseForMessage(requestBody: String, finTs3ServerAddress: String): WebClientResponse { log.debug("Sending message:\n${prettyPrintHbciMessage(requestBody)}") val encodedRequestBody = base64Service.encode(requestBody) return webClient.post( - RequestParameters(bank.finTs3ServerAddress, encodedRequestBody, "application/octet-stream") + RequestParameters(finTs3ServerAddress, encodedRequestBody, "application/octet-stream") ) } - protected open fun handleResponse(webResponse: WebClientResponse, bank: BankData): Response { + protected open fun handleResponse(webResponse: WebClientResponse, dialogContext: DialogContext): Response { val responseBody = webResponse.body if (webResponse.isSuccessful && responseBody != null) { @@ -606,6 +607,7 @@ open class FinTsClient @JvmOverloads constructor( } } else { + val bank = dialogContext.bank log.error("Request to $bank (${bank.finTs3ServerAddress}) failed", webResponse.error) } @@ -620,18 +622,20 @@ open class FinTsClient @JvmOverloads constructor( return message.replace("'", "'\r\n") } - protected open fun handleMayRequiredTan(response: Response, bank: BankData, customer: CustomerData, dialogData: DialogData): Response { + protected open fun handleMayRequiredTan(response: Response, dialogContext: DialogContext): Response { // TODO: use response from DialogContext + if (response.isStrongAuthenticationRequired) { response.tanResponse?.let { tanResponse -> + val customer = dialogContext.customer val enteredTanResult = callback.enterTan(customer, createTanChallenge(tanResponse, customer)) if (enteredTanResult.changeTanProcedureTo != null) { return handleUserAsksToChangeTanProcedureAndResendLastMessage(enteredTanResult.changeTanProcedureTo, - bank, customer, dialogData) + dialogContext) } else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) { return handleUserAsksToChangeTanMediumAndResendLastMessage(enteredTanResult.changeTanMediumTo, - bank, customer, dialogData, enteredTanResult.changeTanMediumResultCallback) + dialogContext, enteredTanResult.changeTanMediumResultCallback) } else if (enteredTanResult.enteredTan == null) { // i tried to send a HKTAN with cancelJob = true but then i saw there are no tan procedures that support cancellation (at least not at my bank) @@ -639,7 +643,7 @@ open class FinTsClient @JvmOverloads constructor( response.tanRequiredButUserDidNotEnterOne = true } else { - return sendTanToBank(enteredTanResult.enteredTan, tanResponse, bank, customer, dialogData) + return sendTanToBank(enteredTanResult.enteredTan, tanResponse, dialogContext) } } } @@ -672,44 +676,39 @@ open class FinTsClient @JvmOverloads constructor( } } - protected open fun sendTanToBank(enteredTan: String, tanResponse: TanResponse, bank: BankData, - customer: CustomerData, dialogData: DialogData): Response { + protected open fun sendTanToBank(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext): Response { - dialogData.increaseMessageNumber() - val message = messageBuilder.createSendEnteredTanMessage(enteredTan, tanResponse, bank, customer, dialogData) + dialogContext.increaseMessageNumber() // TODO: move to MessageBuilder - return getAndHandleResponseForMessageThatMayRequiresTan(message, bank, customer, dialogData) + val message = messageBuilder.createSendEnteredTanMessage(enteredTan, tanResponse, dialogContext) + + // TODO: shouldn't we use MessageBuilderResult here as well? + return getAndHandleResponseForMessageThatMayRequiresTan(message, dialogContext) } - protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, bank: BankData, - customer: CustomerData, dialogData: DialogData): Response { + protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, dialogContext: DialogContext): Response { - val lastCreatedMessage = messageBuilder.lastCreatedMessage - - customer.selectedTanProcedure = changeTanProcedureTo + dialogContext.customer.selectedTanProcedure = changeTanProcedureTo - lastCreatedMessage?.let { - closeDialog(bank, customer, dialogData) + val lastCreatedMessage = dialogContext.currentMessage - return resendMessageInNewDialog(lastCreatedMessage, bank, customer) - } + lastCreatedMessage?.let { closeDialog(dialogContext) } - val errorMessage = "There's no last action (like retrieve account transactions, transfer money, ...) to re-send with new TAN procedure. Probably an internal programming error." // TODO: translate - return Response(false, exception = Exception(errorMessage)) // should never come to this + return resendMessageInNewDialog(lastCreatedMessage, dialogContext) } - protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(changeTanMediumTo: TanGeneratorTanMedium, bank: BankData, - customer: CustomerData, dialogData: DialogData, + protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(changeTanMediumTo: TanGeneratorTanMedium, + dialogContext: DialogContext, changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): Response { - val lastCreatedMessage = messageBuilder.lastCreatedMessage + val lastCreatedMessage = dialogContext.currentMessage - lastCreatedMessage?.let { closeDialog(bank, customer, dialogData) } + lastCreatedMessage?.let { closeDialog(dialogContext) } - val changeTanMediumResponse = changeTanMedium(changeTanMediumTo, bank, customer) + val changeTanMediumResponse = changeTanMedium(changeTanMediumTo, dialogContext.bank, dialogContext.customer) changeTanMediumResultCallback?.invoke(changeTanMediumResponse) @@ -718,28 +717,32 @@ open class FinTsClient @JvmOverloads constructor( } - return resendMessageInNewDialog(lastCreatedMessage, bank, customer) + return resendMessageInNewDialog(lastCreatedMessage, dialogContext) } - protected open fun resendMessageInNewDialog(message: MessageBuilderResult, bank: BankData, - customer: CustomerData): Response { + protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext): Response { - val dialogData = DialogData() + lastCreatedMessage?.let { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message + val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product) - val initDialogResponse = initDialog(bank, customer, dialogData) - if (initDialogResponse.successful == false) { - return initDialogResponse + val initDialogResponse = initDialog(newDialogContext) + if (initDialogResponse.successful == false) { + return initDialogResponse + } + + + val newMessage = messageBuilder.rebuildMessage(lastCreatedMessage, newDialogContext) + + val response = getAndHandleResponseForMessageThatMayRequiresTan(newMessage, newDialogContext) + + closeDialog(newDialogContext) + + return response } - - val newMessage = messageBuilder.rebuildMessage(message, bank, customer, dialogData) - - val response = getAndHandleResponseForMessageThatMayRequiresTan(newMessage, bank, customer, dialogData) - - closeDialog(bank, customer, dialogData) - - return response + val errorMessage = "There's no last action (like retrieve account transactions, transfer money, ...) to re-send with new TAN procedure. Probably an internal programming error." // TODO: translate + return Response(false, exception = Exception(errorMessage)) // should never come to this } diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt index 4e5ff767..43426a3e 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt @@ -39,10 +39,6 @@ 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) @@ -51,66 +47,61 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg * * Bei anonymen Dialogen werden Nachrichten weder signiert, noch können sie verschlüsselt und komprimiert werden. */ - open fun createAnonymousDialogInitMessage(bank: BankData, product: ProductData, dialogData: DialogData): String { + open fun createAnonymousDialogInitMessage(dialogContext: DialogContext): MessageBuilderResult { - val customer = CustomerData.Anonymous - - return createMessage(bank, customer, dialogData, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(1), bank, customer), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product) + return createUnsignedMessageBuilderResult(dialogContext, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(1), dialogContext), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext) )) } - open fun createAnonymousDialogEndMessage(bank: BankData, dialogData: DialogData): String { + open fun createAnonymousDialogEndMessage(dialogContext: DialogContext): String { - val customer = CustomerData.Anonymous - - return createMessage(bank, customer, dialogData, listOf( - Dialogende(generator.resetSegmentNumber(1), dialogData) + return createMessage(dialogContext, listOf( + Dialogende(generator.resetSegmentNumber(1), dialogContext) )) } - open fun createInitDialogMessage(bank: BankData, customer: CustomerData, product: ProductData, - dialogData: DialogData, useStrongAuthentication: Boolean = true): String { + open fun createInitDialogMessage(dialogContext: DialogContext, useStrongAuthentication: Boolean = true): MessageBuilderResult { val segments = mutableListOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product) + IdentifikationsSegment(generator.resetSegmentNumber(2), dialogContext), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext) ) if (useStrongAuthentication) { segments.add(ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification)) } - return createSignedMessage(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } - open fun createSynchronizeCustomerSystemIdMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String { + open fun createSynchronizeCustomerSystemIdMessage(dialogContext: DialogContext): MessageBuilderResult { - return createSignedMessage(bank, customer, dialogData, listOf( - IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer), - Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product), + return createMessageBuilderResult(dialogContext, listOf( + IdentifikationsSegment(generator.resetSegmentNumber(2), dialogContext), + Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext), ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification), Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden) )) } - open fun createDialogEndMessage(bank: BankData, customer: CustomerData, dialogData: DialogData): String { + open fun createDialogEndMessage(dialogContext: DialogContext): String { - return createSignedMessage(bank, customer, dialogData, listOf( - Dialogende(generator.resetSegmentNumber(2), dialogData) + return createSignedMessage(dialogContext, listOf( + Dialogende(generator.resetSegmentNumber(2), dialogContext) )) } - open fun createGetTransactionsMessage(parameter: GetTransactionsParameter, bank: BankData, customer: CustomerData, - account: AccountData, product: ProductData, dialogData: DialogData): MessageBuilderResult { + open fun createGetTransactionsMessage(parameter: GetTransactionsParameter, account: AccountData, + dialogContext: DialogContext): MessageBuilderResult { val result = supportsGetTransactionsMt940(account) if (result.isJobVersionSupported) { - val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, bank, account) + val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, dialogContext.bank, account) else if (result.isAllowed(6)) KontoumsaetzeZeitraumMt940Version6(generator.resetSegmentNumber(2), parameter, account) else KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter, account) @@ -119,7 +110,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.AccountTransactionsMt940) ) - return createMessageBuilderResult(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } return result @@ -134,20 +125,20 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun createGetBalanceMessage(bank: BankData, customer: CustomerData, account: AccountData, product: ProductData, dialogData: DialogData): MessageBuilderResult { + open fun createGetBalanceMessage(account: AccountData, dialogContext: DialogContext): MessageBuilderResult { val result = supportsGetBalanceMessage(account) if (result.isJobVersionSupported) { val balanceJob = if (result.isAllowed(5)) SaldenabfrageVersion5(generator.resetSegmentNumber(2), account) - else SaldenabfrageVersion7(generator.resetSegmentNumber(2), account, bank) + else SaldenabfrageVersion7(generator.resetSegmentNumber(2), account, dialogContext.bank) val segments = listOf( balanceJob, ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Balance) ) - return createMessageBuilderResult(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } return result @@ -162,11 +153,11 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun createGetTanMediaListMessage(bank: BankData, customer: CustomerData, dialogData: DialogData, + open fun createGetTanMediaListMessage(dialogContext: DialogContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): MessageBuilderResult { - val result = getSupportedVersionsOfJob(CustomerSegmentId.TanMediaList, customer, listOf(2, 3, 4, 5)) + val result = getSupportedVersionsOfJob(CustomerSegmentId.TanMediaList, dialogContext.customer, listOf(2, 3, 4, 5)) if (result.isJobVersionSupported) { val segments = listOf( @@ -174,41 +165,41 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg generator.resetSegmentNumber(2), tanMediaKind, tanMediumClass) ) - return createMessageBuilderResult(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } return result } - open fun createChangeTanMediumMessage(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, - dialogData: DialogData, tan: String? = null, atc: Int? = null): MessageBuilderResult { + open fun createChangeTanMediumMessage(newActiveTanMedium: TanGeneratorTanMedium, dialogContext: DialogContext, + tan: String? = null, atc: Int? = null): MessageBuilderResult { - val result = getSupportedVersionsOfJob(CustomerSegmentId.ChangeTanMedium, customer, listOf(1, 2, 3)) + val result = getSupportedVersionsOfJob(CustomerSegmentId.ChangeTanMedium, dialogContext.customer, listOf(1, 2, 3)) if (result.isJobVersionSupported) { val segments = listOf( TanGeneratorTanMediumAnOderUmmelden(result.getHighestAllowedVersion!!, generator.resetSegmentNumber(2), - bank, customer, newActiveTanMedium, tan, atc) + dialogContext.bank, dialogContext.customer, newActiveTanMedium, tan, atc) ) - return createMessageBuilderResult(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } return result } - open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, bank: BankData, customer: CustomerData, dialogData: DialogData): String { + open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, dialogContext: DialogContext): String { val tanProcess = if (tanResponse.tanProcess == TanProcess.TanProcess1) TanProcess.TanProcess1 else TanProcess.TanProcess2 - return createSignedMessage(bank, customer, dialogData, enteredTan, listOf( + return createSignedMessage(dialogContext, enteredTan, listOf( ZweiSchrittTanEinreichung(generator.resetSegmentNumber(2), tanProcess, null, tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier) )) } - open fun createBankTransferMessage(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, account: AccountData, dialogData: DialogData): MessageBuilderResult { + open fun createBankTransferMessage(bankTransferData: BankTransferData, account: AccountData, dialogContext: DialogContext): MessageBuilderResult { val messageBuilderResultAndNullableUrn = supportsBankTransferAndSepaVersion(account) val result = messageBuilderResultAndNullableUrn.first @@ -216,11 +207,11 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg if (result.isJobVersionSupported && urn != null) { val segments = listOf( - SepaEinzelueberweisung(generator.resetSegmentNumber(2), urn, customer, account, bank.bic, bankTransferData), + SepaEinzelueberweisung(generator.resetSegmentNumber(2), urn, dialogContext.customer, account, dialogContext.bank.bic, bankTransferData), ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.SepaBankTransfer) ) - return createMessageBuilderResult(bank, customer, dialogData, segments) + return createMessageBuilderResult(dialogContext, segments) } return result @@ -250,8 +241,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - open fun rebuildMessageWithContinuationId(message: MessageBuilderResult, continuationId: String, bank: BankData, - customer: CustomerData, dialogData: DialogData): MessageBuilderResult? { + open fun rebuildMessageWithContinuationId(message: MessageBuilderResult, continuationId: String, dialogContext: DialogContext): MessageBuilderResult? { // val copiedSegments = message.messageBodySegments.map { } val aufsetzpunkte = message.messageBodySegments.flatMap { it.dataElementsAndGroups }.filterIsInstance() @@ -263,70 +253,78 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg aufsetzpunkte.forEach { it.resetContinuationId(continuationId) } - return rebuildMessage(message, bank, customer, dialogData) + return rebuildMessage(message, dialogContext) } - open fun rebuildMessage(message: MessageBuilderResult, bank: BankData, customer: CustomerData, - dialogData: DialogData): MessageBuilderResult { + open fun rebuildMessage(message: MessageBuilderResult, dialogContext: DialogContext): MessageBuilderResult { - dialogData.increaseMessageNumber() + dialogContext.increaseMessageNumber() - return createMessageBuilderResult(bank, customer, dialogData, message.messageBodySegments) + return createMessageBuilderResult(dialogContext, message.messageBodySegments) } - protected open fun createMessageBuilderResult(bank: BankData, customer: CustomerData, dialogData: DialogData, segments: List): MessageBuilderResult { - val message = MessageBuilderResult(createSignedMessage(bank, customer, dialogData, segments), segments) + protected open fun createMessageBuilderResult(dialogContext: DialogContext, segments: List): MessageBuilderResult { + val message = MessageBuilderResult(createSignedMessage(dialogContext, segments), segments) - lastCreatedMessage = message + dialogContext.previousMessageInDialog = dialogContext.currentMessage + + dialogContext.currentMessage = message + + 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 return message } - open fun createSignedMessage(bank: BankData, customer: CustomerData, dialogData: DialogData, - payloadSegments: List): String { + open fun createSignedMessage(dialogContext: DialogContext, payloadSegments: List): String { - return createSignedMessage(bank, customer, dialogData, null, payloadSegments) + return createSignedMessage(dialogContext, null, payloadSegments) } - open fun createSignedMessage(bank: BankData, customer: CustomerData, dialogData: DialogData, - tan: String? = null, payloadSegments: List): String { + open fun createSignedMessage(dialogContext: DialogContext, tan: String? = null, + payloadSegments: List): String { val date = utils.formatDateTodayAsInt() val time = utils.formatTimeNowAsInt() - val signedPayload = signPayload(2, bank, customer, date, time, tan, payloadSegments) + val signedPayload = signPayload(2, dialogContext, date, time, tan, payloadSegments) - val encryptedPayload = encryptPayload(bank, customer, date, time, signedPayload) + val encryptedPayload = encryptPayload(dialogContext, date, time, signedPayload) - return createMessage(bank, customer, dialogData, encryptedPayload) + return createMessage(dialogContext, encryptedPayload) } - open fun createMessage(bank: BankData, customer: CustomerData, dialogData: DialogData, - payloadSegments: List): String { + open fun createMessage(dialogContext: DialogContext, payloadSegments: List): String { val formattedPayload = formatPayload(payloadSegments) val messageSize = formattedPayload.length + MessageHeaderLength + MessageEndingLength + AddedSeparatorsLength - val header = Nachrichtenkopf(ISegmentNumberGenerator.FirstSegmentNumber, messageSize, dialogData) + val header = Nachrichtenkopf(ISegmentNumberGenerator.FirstSegmentNumber, messageSize, dialogContext) - val ending = Nachrichtenabschluss(generator.getNextSegmentNumber(), dialogData) + val ending = Nachrichtenabschluss(generator.getNextSegmentNumber(), dialogContext) return listOf(header.format(), formattedPayload, ending.format()) .joinToString(Separators.SegmentSeparator, postfix = Separators.SegmentSeparator) } - protected open fun signPayload(headerSegmentNumber: Int, bank: BankData, customer: CustomerData, date: Int, time: Int, + protected open fun signPayload(headerSegmentNumber: Int, dialogContext: DialogContext, date: Int, time: Int, tan: String? = null, payloadSegments: List): List { val controlReference = createControlReference() val signatureHeader = PinTanSignaturkopf( headerSegmentNumber, - bank, - customer, + dialogContext, controlReference, date, time @@ -335,7 +333,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val signatureEnding = Signaturabschluss( generator.getNextSegmentNumber(), controlReference, - customer.pin, + dialogContext.customer.pin, tan ) @@ -347,10 +345,10 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } - private fun encryptPayload(bank: BankData, customer: CustomerData, date: Int, time: Int, + private fun encryptPayload(dialogContext: DialogContext, date: Int, time: Int, payload: List): List { - val encryptionHeader = PinTanVerschluesselungskopf(bank, customer, date, time) + val encryptionHeader = PinTanVerschluesselungskopf(dialogContext, date, time) val encryptedData = VerschluesselteDaten(formatPayload(payload) + Separators.SegmentSeparator) diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Dialogende.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Dialogende.kt index 800a0e18..f7573c25 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Dialogende.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Dialogende.kt @@ -4,14 +4,14 @@ import net.dankito.fints.messages.datenelemente.implementierte.DialogId import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.id.CustomerSegmentId -import net.dankito.fints.model.DialogData +import net.dankito.fints.model.DialogContext class Dialogende( segmentNumber: Int, - dialogData: DialogData + dialogContext: DialogContext ) : Segment(listOf( Segmentkopf(CustomerSegmentId.DialogEnd, 1, segmentNumber), - DialogId(dialogData.dialogId) + DialogId(dialogContext.dialogId) )) \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegment.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegment.kt index 451c0a5c..62e14407 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegment.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegment.kt @@ -8,19 +8,17 @@ import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinsti import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf 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.model.MessageBaseData open class IdentifikationsSegment( segmentNumber: Int, - bank: BankData, - customer: CustomerData + baseData: MessageBaseData ) : Segment(listOf( Segmentkopf(CustomerSegmentId.Identification, 2, segmentNumber), - Kreditinstitutskennung(bank.countryCode, bank.bankCode), - KundenID(customer.customerId), - KundensystemID(customer.customerSystemId), - KundensystemStatus(customer.customerSystemStatus, Existenzstatus.Mandatory) + Kreditinstitutskennung(baseData.bank.countryCode, baseData.bank.bankCode), + KundenID(baseData.customer.customerId), + KundensystemID(baseData.customer.customerSystemId), + KundensystemStatus(baseData.customer.customerSystemStatus, Existenzstatus.Mandatory) )) \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenabschluss.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenabschluss.kt index cead5f80..d8213e44 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenabschluss.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenabschluss.kt @@ -4,7 +4,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.Nachrichtennummer import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.id.MessageSegmentId -import net.dankito.fints.model.DialogData +import net.dankito.fints.model.DialogContext /** @@ -12,9 +12,9 @@ import net.dankito.fints.model.DialogData */ open class Nachrichtenabschluss( segmentNumber: Int, - dialogData: DialogData + dialogContext: DialogContext ) : Segment(listOf( Segmentkopf(MessageSegmentId.MessageEnding, 1, segmentNumber), - Nachrichtennummer(dialogData.messageNumber) + Nachrichtennummer(dialogContext.messageNumber) )) \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenkopf.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenkopf.kt index 5574c4db..4b2d3d69 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenkopf.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Nachrichtenkopf.kt @@ -4,18 +4,18 @@ import net.dankito.fints.messages.datenelemente.implementierte.* import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.id.MessageSegmentId -import net.dankito.fints.model.DialogData +import net.dankito.fints.model.DialogContext open class Nachrichtenkopf( segmentNumber: Int, messageSize: Int, - dialogData: DialogData + dialogContext: DialogContext ) : Segment(listOf( Segmentkopf(MessageSegmentId.MessageHeader, 3, segmentNumber), Nachrichtengroesse(messageSize), HbciVersionDatenelement(HbciVersion.FinTs_3_0_0), - DialogId(dialogData.dialogId), - Nachrichtennummer(dialogData.messageNumber) + DialogId(dialogContext.dialogId), + Nachrichtennummer(dialogContext.messageNumber) )) \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt index c10ab189..38ecc9c1 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt @@ -4,22 +4,20 @@ import net.dankito.fints.messages.datenelemente.implementierte.signatur.Operatio import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselversion import net.dankito.fints.messages.datenelemente.implementierte.signatur.SignaturalgorithmusKodiert -import net.dankito.fints.model.BankData -import net.dankito.fints.model.CustomerData +import net.dankito.fints.model.MessageBaseData open class PinTanSignaturkopf( segmentNumber: Int, - bank: BankData, - customer: CustomerData, + baseData: MessageBaseData, securityControlReference: String, date: Int, time: Int ) : Signaturkopf( segmentNumber, - bank, - customer, + baseData.bank, + baseData.customer, securityControlReference, date, time, diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt index 383b7774..aac3a98d 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt @@ -5,19 +5,17 @@ import net.dankito.fints.messages.datenelemente.implementierte.signatur.Operatio import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselart import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselversion -import net.dankito.fints.model.BankData -import net.dankito.fints.model.CustomerData +import net.dankito.fints.model.MessageBaseData open class PinTanVerschluesselungskopf( - bank: BankData, - customer: CustomerData, + baseData: MessageBaseData, date: Int, time: Int ) : Verschluesselungskopf( - bank, - customer, + baseData.bank, + baseData.customer, date, time, OperationsmodusKodiert.FinTsMockValue, diff --git a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verarbeitungsvorbereitung.kt b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verarbeitungsvorbereitung.kt index 06256518..0ffcdf4b 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verarbeitungsvorbereitung.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verarbeitungsvorbereitung.kt @@ -5,21 +5,17 @@ import net.dankito.fints.messages.datenelemente.implementierte.* import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf 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.model.ProductData +import net.dankito.fints.model.MessageBaseData open class Verarbeitungsvorbereitung( segmentNumber: Int, - bank: BankData, - customer: CustomerData, - product: ProductData + baseData: MessageBaseData ) : Segment(listOf( Segmentkopf(CustomerSegmentId.ProcessingPreparation, 3, segmentNumber), - BPDVersion(bank.bpdVersion, Existenzstatus.Mandatory), - UPDVersion(customer.updVersion, Existenzstatus.Mandatory), - DialogspracheDatenelement(customer.selectedLanguage, Existenzstatus.Mandatory), - Produktbezeichnung(product.name, Existenzstatus.Mandatory), - Produktversion(product.version, Existenzstatus.Mandatory) + BPDVersion(baseData.bank.bpdVersion, Existenzstatus.Mandatory), + UPDVersion(baseData.customer.updVersion, Existenzstatus.Mandatory), + DialogspracheDatenelement(baseData.customer.selectedLanguage, Existenzstatus.Mandatory), + Produktbezeichnung(baseData.product.name, Existenzstatus.Mandatory), + Produktversion(baseData.product.version, Existenzstatus.Mandatory) )) \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/model/DialogContext.kt b/fints4k/src/main/kotlin/net/dankito/fints/model/DialogContext.kt new file mode 100644 index 00000000..95e7654f --- /dev/null +++ b/fints4k/src/main/kotlin/net/dankito/fints/model/DialogContext.kt @@ -0,0 +1,29 @@ +package net.dankito.fints.model + +import net.dankito.fints.messages.MessageBuilderResult +import net.dankito.fints.response.Response + + +open class DialogContext( + bank: BankData, + customer: CustomerData, + product: ProductData, + var currentMessage: MessageBuilderResult? = null, + var dialogId: String = InitialDialogId, + var messageNumber: Int = InitialMessageNumber, + var response: Response? = null, + var previousMessageInDialog: MessageBuilderResult? = null +) : MessageBaseData(bank, customer, product) { + + companion object { + const val InitialDialogId = "0" + + const val InitialMessageNumber = 1 + } + + + fun increaseMessageNumber() { + messageNumber++ + } + +} \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/model/DialogData.kt b/fints4k/src/main/kotlin/net/dankito/fints/model/DialogData.kt deleted file mode 100644 index 0561a502..00000000 --- a/fints4k/src/main/kotlin/net/dankito/fints/model/DialogData.kt +++ /dev/null @@ -1,18 +0,0 @@ -package net.dankito.fints.model - - -open class DialogData( - var dialogId: String = "0", - var messageNumber: Int = 1 -) { - - companion object { - val DialogInitDialogData = DialogData("0", 1) - } - - - fun increaseMessageNumber() { - messageNumber++ - } - -} \ No newline at end of file diff --git a/fints4k/src/main/kotlin/net/dankito/fints/model/MessageBaseData.kt b/fints4k/src/main/kotlin/net/dankito/fints/model/MessageBaseData.kt new file mode 100644 index 00000000..5677e48f --- /dev/null +++ b/fints4k/src/main/kotlin/net/dankito/fints/model/MessageBaseData.kt @@ -0,0 +1,8 @@ +package net.dankito.fints.model + + +open class MessageBaseData( + val bank: BankData, + val customer: CustomerData, + val product: ProductData +) \ No newline at end of file diff --git a/fints4k/src/test/kotlin/net/dankito/fints/bankdetails/BanksFinTsDetailsRetriever.kt b/fints4k/src/test/kotlin/net/dankito/fints/bankdetails/BanksFinTsDetailsRetriever.kt index 0c7685d5..e8b5bb80 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/bankdetails/BanksFinTsDetailsRetriever.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/bankdetails/BanksFinTsDetailsRetriever.kt @@ -44,8 +44,8 @@ class BanksFinTsDetailsRetriever { private val finTsClient = object : FinTsClient(NoOpFinTsClientCallback(), Java8Base64Service()) { - fun getAndHandleResponseForMessagePublic(requestBody: String, bank: BankData): Response { - return getAndHandleResponseForMessage(requestBody, bank) + fun getAndHandleResponseForMessagePublic(requestBody: String, dialogContext: DialogContext): Response { + return getAndHandleResponseForMessage(requestBody, dialogContext) } fun updateBankDataPublic(bank: BankData, response: Response) { @@ -109,11 +109,11 @@ class BanksFinTsDetailsRetriever { private fun getAnonymousBankInfo(bank: BankData): Response { - val dialogData = DialogData() - val requestBody = messageBuilder.createAnonymousDialogInitMessage(bank, product, dialogData) + val dialogContext = DialogContext(bank, CustomerData.Anonymous, product) + val requestBody = messageBuilder.createAnonymousDialogInitMessage(dialogContext).createdMessage ?: "" val anonymousBankInfoResponse = - finTsClient.getAndHandleResponseForMessagePublic(requestBody, bank) + finTsClient.getAndHandleResponseForMessagePublic(requestBody, dialogContext) finTsClient.updateBankDataPublic(bank, anonymousBankInfoResponse) return anonymousBankInfoResponse diff --git a/fints4k/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt index 0a5d2fa2..e76980ff 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt @@ -1,9 +1,7 @@ package net.dankito.fints.messages import net.dankito.fints.FinTsTestBase -import net.dankito.fints.model.AccountData -import net.dankito.fints.model.DialogData -import net.dankito.fints.model.GetTransactionsParameter +import net.dankito.fints.model.* import net.dankito.fints.response.segments.AccountType import net.dankito.fints.response.segments.JobParameters import net.dankito.fints.util.FinTsUtils @@ -44,8 +42,11 @@ class MessageBuilderTest : FinTsTestBase() { @Test fun createAnonymousDialogInitMessage() { + // given + val dialogContext = DialogContext(Bank, CustomerData.Anonymous, Product) + // when - val result = underTest.createAnonymousDialogInitMessage(Bank, Product, DialogData.DialogInitDialogData) + val result = underTest.createAnonymousDialogInitMessage(dialogContext).createdMessage // then assertThat(result).isEqualTo( @@ -61,10 +62,10 @@ class MessageBuilderTest : FinTsTestBase() { // given val dialogId = createDialogId() - val dialogData = DialogData(dialogId) + val dialogContext = DialogContext(Bank, Customer, Product, null, dialogId) // when - val result = underTest.createAnonymousDialogEndMessage(Bank, dialogData) + val result = underTest.createAnonymousDialogEndMessage(dialogContext) // then assertThat(normalizeBinaryData(result)).isEqualTo(normalizeBinaryData( @@ -78,8 +79,11 @@ class MessageBuilderTest : FinTsTestBase() { @Test fun createDialogInitMessage() { + // given + val dialogContext = DialogContext(Bank, Customer, Product) + // when - val result = underTest.createSynchronizeCustomerSystemIdMessage(Bank, Customer, Product, DialogData.DialogInitDialogData) + val result = underTest.createSynchronizeCustomerSystemIdMessage(dialogContext).createdMessage ?: "" // then assertThat(normalizeBinaryData(result)).isEqualTo(normalizeBinaryData( @@ -100,10 +104,10 @@ class MessageBuilderTest : FinTsTestBase() { // given val dialogId = createDialogId() - val dialogData = DialogData(dialogId) + val dialogContext = DialogContext(Bank, Customer, Product, null, dialogId) // when - val result = underTest.createDialogEndMessage(Bank, Customer, dialogData) + val result = underTest.createDialogEndMessage(dialogContext) // then assertThat(normalizeBinaryData(result)).isEqualTo(normalizeBinaryData( @@ -120,8 +124,11 @@ class MessageBuilderTest : FinTsTestBase() { @Test fun createGetTransactionsMessage_JobIsNotAllowed() { + // given + val dialogContext = DialogContext(Bank, Customer, Product) + // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Bank, Customer, Account, Product, DialogData.DialogInitDialogData) + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Account, dialogContext) // then assertThat(result.isJobAllowed).isFalse() @@ -136,9 +143,10 @@ class MessageBuilderTest : FinTsTestBase() { Bank.supportedJobs = listOf(getTransactionsJob) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJobWithPreviousVersion)) Customer.addAccount(account) + val dialogContext = DialogContext(Bank, Customer, Product) // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Bank, Customer, account, Product, DialogData.DialogInitDialogData) + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), account, dialogContext) // then assertThat(result.isJobAllowed).isTrue() @@ -153,13 +161,14 @@ class MessageBuilderTest : FinTsTestBase() { Bank.supportedJobs = listOf(getTransactionsJob) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) Customer.addAccount(account) + val dialogContext = DialogContext(Bank, Customer, Product) val fromDate = LocalDate.of(2019, Month.AUGUST, 6).asUtilDate() val toDate = LocalDate.of(2019, Month.OCTOBER, 21).asUtilDate() val maxCountEntries = 99 // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries), Bank, Customer, account, Product, DialogData.DialogInitDialogData) + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries), account, dialogContext) // then assertThat(result.createdMessage).isNotNull() @@ -183,6 +192,7 @@ class MessageBuilderTest : FinTsTestBase() { Bank.supportedJobs = listOf(getTransactionsJob) val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) Customer.addAccount(account) + val dialogContext = DialogContext(Bank, Customer, Product) val fromDate = LocalDate.of(2019, Month.AUGUST, 6).asUtilDate() val toDate = LocalDate.of(2019, Month.OCTOBER, 21).asUtilDate() @@ -190,7 +200,7 @@ class MessageBuilderTest : FinTsTestBase() { val continuationId = "9345-10-26-11.52.15.693455" // when - val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries, false, continuationId), Bank, Customer, account, Product, DialogData.DialogInitDialogData) + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries, false, continuationId), account, dialogContext) // then assertThat(result.createdMessage).isNotNull() diff --git a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegmentTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegmentTest.kt index aff75eac..7cf94a0a 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegmentTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/IdentifikationsSegmentTest.kt @@ -1,6 +1,7 @@ package net.dankito.fints.messages.segmente.implementierte import net.dankito.fints.FinTsTestBase +import net.dankito.fints.model.MessageBaseData import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -11,7 +12,7 @@ class IdentifikationsSegmentTest : FinTsTestBase() { fun format() { // given - val underTest = IdentifikationsSegment(2, Bank, Customer) + val underTest = IdentifikationsSegment(2, MessageBaseData(Bank, Customer, Product)) // when val result = underTest.format() diff --git a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/SignaturkopfTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/SignaturkopfTest.kt index d24e7dce..5407282f 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/SignaturkopfTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/SignaturkopfTest.kt @@ -1,6 +1,7 @@ package net.dankito.fints.messages.segmente.implementierte import net.dankito.fints.FinTsTestBase +import net.dankito.fints.model.MessageBaseData import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -13,7 +14,7 @@ class SignaturkopfTest : FinTsTestBase() { // given val controlReference = "1902675680" - val underTest = PinTanSignaturkopf(2, Bank, Customer, + val underTest = PinTanSignaturkopf(2, MessageBaseData(Bank, Customer, Product), controlReference, Date, Time) // when diff --git a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt index d3417852..e713f6a6 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt @@ -1,6 +1,7 @@ package net.dankito.fints.messages.segmente.implementierte import net.dankito.fints.FinTsTestBase +import net.dankito.fints.model.MessageBaseData import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -12,7 +13,7 @@ class VerschluesselungskopfTest : FinTsTestBase() { // given - val underTest = PinTanVerschluesselungskopf(Bank, Customer, Date, Time) + val underTest = PinTanVerschluesselungskopf(MessageBaseData(Bank, Customer, Product), Date, Time) // when val result = underTest.format()