diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt index 1d429cb0..6d5acdc0 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt @@ -10,6 +10,7 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.Kundensys import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.* +import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId import net.dankito.banking.fints.model.* import net.dankito.banking.fints.response.GetUserTanProceduresResponse import net.dankito.banking.fints.response.InstituteSegmentId @@ -446,21 +447,25 @@ open class FinTsClient( open fun getTanMediaList(bank: BankData, customer: CustomerData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) { - sendMessageAndHandleResponse(bank, customer, true, { dialogContext -> + sendMessageAndHandleResponse(bank, customer, true, CustomerSegmentId.TanMediaList, { dialogContext -> messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass) }) { response -> - // TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures - val tanMediaList = if (response.successful == false ) null - else response.getFirstSegmentById(InstituteSegmentId.TanMediaList) - - tanMediaList?.let { - customer.tanMedia = it.tanMedia - } - - callback(GetTanMediaListResponse(response, tanMediaList)) + handleGetTanMediaListResponse(response, customer, callback) } } + private fun handleGetTanMediaListResponse(response: Response, customer: CustomerData, callback: (GetTanMediaListResponse) -> Unit) { + // TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures + val tanMediaList = if (response.successful == false) null + else response.getFirstSegmentById(InstituteSegmentId.TanMediaList) + + tanMediaList?.let { + customer.tanMedia = it.tanMedia + } + + callback(GetTanMediaListResponse(response, tanMediaList)) + } + open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) { @@ -483,7 +488,7 @@ open class FinTsClient( protected open fun sendChangeTanMediumMessage(bank: BankData, customer: CustomerData, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?, callback: (FinTsClientResponse) -> Unit) { - sendMessageAndHandleResponse(bank, customer, false, { dialogContext -> + sendMessageAndHandleResponse(bank, customer, false, null, { dialogContext -> messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc) }) { response -> callback(FinTsClientResponse(response)) @@ -494,7 +499,7 @@ open class FinTsClient( open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, account: AccountData, callback: (FinTsClientResponse) -> Unit) { - sendMessageAndHandleResponse(bank, customer, true, { dialogContext -> + sendMessageAndHandleResponse(bank, customer, true, null, { dialogContext -> messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext) }) { response -> callback(FinTsClientResponse(response)) @@ -503,22 +508,35 @@ open class FinTsClient( protected open fun sendMessageAndHandleResponse(bank: BankData, customer: CustomerData, messageMayRequiresTan: Boolean = true, + segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null, createMessage: (DialogContext) -> MessageBuilderResult, callback: (Response) -> Unit) { val dialogContext = DialogContext(bank, customer, product) - initDialog(dialogContext) { initDialogResponse -> - if (initDialogResponse.successful == false) { - callback(initDialogResponse) + if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) { + initDialog(dialogContext) { initDialogResponse -> + sendMessageAndHandleResponseAfterDialogInitialization(dialogContext, initDialogResponse, createMessage, callback) } - else { - val message = createMessage(dialogContext) + } + else { + initInitDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(dialogContext, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess) { initDialogResponse -> + sendMessageAndHandleResponseAfterDialogInitialization(dialogContext, initDialogResponse, createMessage, callback) + } + } + } - getAndHandleResponseForMessage(message, dialogContext) { response -> - closeDialog(dialogContext) + private fun sendMessageAndHandleResponseAfterDialogInitialization(dialogContext: DialogContext, initDialogResponse: Response, createMessage: (DialogContext) -> MessageBuilderResult, callback: (Response) -> Unit) { - callback(response) - } + if (initDialogResponse.successful == false) { + callback(initDialogResponse) + } + else { + val message = createMessage(dialogContext) + + getAndHandleResponseForMessage(message, dialogContext) { response -> + closeDialog(dialogContext) + + callback(response) } } } @@ -561,6 +579,21 @@ open class FinTsClient( } } + protected open fun initInitDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?, + callback: (Response) -> Unit) { + + val message = messageBuilder.createInitDialogMessageWithoutStrongCustomerAuthentication(dialogContext, segmentIdForTwoStepTanProcess) + + getAndHandleResponseForMessage(message, dialogContext) { response -> + if (response.successful) { + updateBankData(dialogContext.bank, response) + updateCustomerData(dialogContext.customer, dialogContext.bank, response) + } + + callback(response) + } + } + protected open fun closeDialog(dialogContext: DialogContext) { // bank already closed dialog -> there's no need to send dialog end message 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 c8a0744e..46016726 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 @@ -66,13 +66,58 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg open fun createInitDialogMessage(dialogContext: DialogContext): MessageBuilderResult { + return createInitDialogMessage(dialogContext, null) + } + + /** + * Im Rahmen der PIN/TAN-Management-Geschäftsvorfälle (vgl. Kapitel C.3) ist in be- + stimmten Situationen eine Einreichung ohne starke Kundenauthentifizierung erfor- + derlich (Authentifizierungsklasse 4, vgl. Kapitel B.3). Daher wird in einem solchen + Fall das Element Segmentkennung in HKTAN ab #6 mit der Segmentkennung des + jeweiligen Geschäftsvorfalls belegt, der dann isoliert in diesem Dialog eingereicht + wird. + (PinTan S. 35) + + * Beim Erstzugang mit einem neuen TAN-Verfahren liegt einem Kundenprodukt + ggf. noch keine TAN-Medien-Bezeichnung für dieses Verfahren vor. In diesem + Fall muss der Geschäftsvorfall Anzeige der verfügbaren TAN-Medien + (HKTAB) ohne starke Kundenauthentifizierung durchführbar sein. (..) + + In das DE Segmentkennung in HKTAN wird der Wert HKTAB eingestellt. + Der vom Kundenprodukt hier als Füllwert gelieferte Inhalt des + Elementes Bezeichnung des TAN-Mediums in HKTAN ist vom + Kreditinstitut in dieser Situation zu ignorieren. (..) + + Anschließend hat das Kundensystem den Dialog durch Senden einer + Dialogendenachricht (HKEND) zu beenden. + + Zweiter Dialog – Starke Kundenauthentifizierung + o Nun wird unter Verwendung eines zugelassenen TAN-Verfahrens + und TAN-Mediums ein zweiter Dialog zum Durchführen einer starken + Kundenauthentifizierung eröffnet. Die SCA ist obligatorisch, da es + sich um die erste Nutzung dieses TAN-Verfahrens inkl. des gewähl- + ten TAN-Mediums handelt. + o Im Rahmen dieses Dialoges können nach erfolgreicher Durchführung + der starken Kundenauthentifizierung beliebige Geschäftsvorfälle + durchgeführt werden. + + (PinTan S. 37/38) + */ + open fun createInitDialogMessageWithoutStrongCustomerAuthentication(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { + return createInitDialogMessage(dialogContext, segmentIdForTwoStepTanProcess) + } + + protected open fun createInitDialogMessage(dialogContext: DialogContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): MessageBuilderResult { val segments = mutableListOf( IdentifikationsSegment(generator.resetSegmentNumber(2), dialogContext), Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), dialogContext) ) - if (dialogContext.customer.isTanProcedureSelected) { + if (segmentIdForTwoStepTanProcess != null) { + segments.add(ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, segmentIdForTwoStepTanProcess)) + } + else if (dialogContext.customer.isTanProcedureSelected) { segments.add(ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification)) } @@ -175,6 +220,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg return result } + // TODO: no HKTAN needed? open fun createChangeTanMediumMessage(newActiveTanMedium: TanGeneratorTanMedium, dialogContext: DialogContext, tan: String? = null, atc: Int? = null): MessageBuilderResult {