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