Implemented serializing FinTS data
This commit is contained in:
parent
d7d2702869
commit
7cdb7247c8
|
@ -72,6 +72,7 @@ kotlin {
|
||||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinxSerializationVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinxSerializationVersion")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion")
|
||||||
|
|
||||||
implementation("net.codinux.log:klf:$klfVersion")
|
implementation("net.codinux.log:klf:$klfVersion")
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import net.codinux.banking.fints.response.segments.BankParameters
|
||||||
import net.codinux.banking.fints.util.BicFinder
|
import net.codinux.banking.fints.util.BicFinder
|
||||||
import net.codinux.log.LogLevel
|
import net.codinux.log.LogLevel
|
||||||
import net.codinux.log.LoggerFactory
|
import net.codinux.log.LoggerFactory
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
|
||||||
open class FinTsClient(
|
open class FinTsClient(
|
||||||
|
@ -57,7 +59,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
if (basicAccountDataResponse.successful == false || param.retrieveOnlyAccountInfo || basicAccountDataResponse.finTsModel == null) {
|
if (basicAccountDataResponse.successful == false || param.retrieveOnlyAccountInfo || basicAccountDataResponse.finTsModel == null) {
|
||||||
return GetAccountDataResponse(basicAccountDataResponse.error, basicAccountDataResponse.errorMessage, null,
|
return GetAccountDataResponse(basicAccountDataResponse.error, basicAccountDataResponse.errorMessage, null,
|
||||||
basicAccountDataResponse.messageLogWithoutSensitiveData, basicAccountDataResponse.finTsModel)
|
basicAccountDataResponse.messageLogWithoutSensitiveData, basicAccountDataResponse.finTsModel, basicAccountDataResponse.serializedFinTsModel)
|
||||||
} else {
|
} else {
|
||||||
val bank = basicAccountDataResponse.finTsModel!!
|
val bank = basicAccountDataResponse.finTsModel!!
|
||||||
return getAccountData(param, bank, bank.accounts, basicAccountDataResponse.messageLogWithoutSensitiveData)
|
return getAccountData(param, bank, bank.accounts, basicAccountDataResponse.messageLogWithoutSensitiveData)
|
||||||
|
@ -71,7 +73,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
if (accountsSupportingRetrievingTransactions.isEmpty()) {
|
if (accountsSupportingRetrievingTransactions.isEmpty()) {
|
||||||
val errorMessage = "None of the accounts ${accounts.map { it.productName }} supports retrieving balance or transactions" // TODO: translate
|
val errorMessage = "None of the accounts ${accounts.map { it.productName }} supports retrieving balance or transactions" // TODO: translate
|
||||||
return GetAccountDataResponse(ErrorCode.NoneOfTheAccountsSupportsRetrievingData, errorMessage, mapper.map(bank), previousJobMessageLog ?: listOf(), bank)
|
return GetAccountDataResponse(ErrorCode.NoneOfTheAccountsSupportsRetrievingData, errorMessage, mapper.map(bank), previousJobMessageLog ?: listOf(), bank, serialize(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
for (account in accountsSupportingRetrievingTransactions) {
|
for (account in accountsSupportingRetrievingTransactions) {
|
||||||
|
@ -87,7 +89,7 @@ open class FinTsClient(
|
||||||
val errorCode = unsuccessfulJob?.let { mapper.mapErrorCode(it) }
|
val errorCode = unsuccessfulJob?.let { mapper.mapErrorCode(it) }
|
||||||
?: if (retrievedTransactionsResponses.size < accountsSupportingRetrievingTransactions.size) ErrorCode.DidNotRetrieveAllAccountData else null
|
?: if (retrievedTransactionsResponses.size < accountsSupportingRetrievingTransactions.size) ErrorCode.DidNotRetrieveAllAccountData else null
|
||||||
return GetAccountDataResponse(errorCode, mapper.mapErrorMessages(unsuccessfulJob), mapper.map(bank, retrievedTransactionsResponses, param.retrieveTransactionsTo),
|
return GetAccountDataResponse(errorCode, mapper.mapErrorMessages(unsuccessfulJob), mapper.map(bank, retrievedTransactionsResponses, param.retrieveTransactionsTo),
|
||||||
mapper.mergeMessageLog(previousJobMessageLog, *retrievedTransactionsResponses.map { it.messageLog }.toTypedArray()), bank)
|
mapper.mergeMessageLog(previousJobMessageLog, *retrievedTransactionsResponses.map { it.messageLog }.toTypedArray()), bank, serialize(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun getAccountTransactions(param: GetAccountDataParameter, bank: BankData, account: AccountData): GetAccountTransactionsResponse {
|
protected open suspend fun getAccountTransactions(param: GetAccountDataParameter, bank: BankData, account: AccountData): GetAccountTransactionsResponse {
|
||||||
|
@ -122,7 +124,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
if (getAccountInfoResponse.successful == false) {
|
if (getAccountInfoResponse.successful == false) {
|
||||||
return TransferMoneyResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
|
return TransferMoneyResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
|
||||||
getAccountInfoResponse.messageLog, bank)
|
getAccountInfoResponse.messageLog, bank, serialize(bank))
|
||||||
} else {
|
} else {
|
||||||
return transferMoneyAsync(param, recipientBankIdentifier, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
|
return transferMoneyAsync(param, recipientBankIdentifier, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
|
||||||
}
|
}
|
||||||
|
@ -136,14 +138,14 @@ open class FinTsClient(
|
||||||
val accountToUse: AccountData
|
val accountToUse: AccountData
|
||||||
|
|
||||||
if (accountsSupportingTransfer.isEmpty()) {
|
if (accountsSupportingTransfer.isEmpty()) {
|
||||||
return TransferMoneyResponse(ErrorCode.NoAccountSupportsMoneyTransfer, "None of the accounts $accounts supports money transfer", previousJobResponse?.messageLog ?: listOf(), bank)
|
return TransferMoneyResponse(ErrorCode.NoAccountSupportsMoneyTransfer, "None of the accounts $accounts supports money transfer", previousJobResponse?.messageLog ?: listOf(), bank, serialize(bank))
|
||||||
} else if (accountsSupportingTransfer.size == 1) {
|
} else if (accountsSupportingTransfer.size == 1) {
|
||||||
accountToUse = accountsSupportingTransfer.first()
|
accountToUse = accountsSupportingTransfer.first()
|
||||||
} else {
|
} else {
|
||||||
val selectedAccount = param.selectAccountToUseForTransfer?.invoke(accountsSupportingTransfer)
|
val selectedAccount = param.selectAccountToUseForTransfer?.invoke(accountsSupportingTransfer)
|
||||||
|
|
||||||
if (selectedAccount == null) {
|
if (selectedAccount == null) {
|
||||||
return TransferMoneyResponse(ErrorCode.MoreThanOneAccountSupportsMoneyTransfer, "More than one of the accounts $accountsSupportingTransfer supports money transfer, so we cannot clearly determine which one to use for this transfer", previousJobResponse?.messageLog ?: listOf(), bank)
|
return TransferMoneyResponse(ErrorCode.MoreThanOneAccountSupportsMoneyTransfer, "More than one of the accounts $accountsSupportingTransfer supports money transfer, so we cannot clearly determine which one to use for this transfer", previousJobResponse?.messageLog ?: listOf(), bank, serialize(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
accountToUse = selectedAccount
|
accountToUse = selectedAccount
|
||||||
|
@ -154,7 +156,7 @@ open class FinTsClient(
|
||||||
val response = config.jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier,
|
val response = config.jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier,
|
||||||
param.amount, param.reference, param.instantPayment))
|
param.amount, param.reference, param.instantPayment))
|
||||||
|
|
||||||
return TransferMoneyResponse(mapper.mapErrorCode(response), mapper.mapErrorMessages(response), mapper.mergeMessageLog(previousJobResponse, response), bank)
|
return TransferMoneyResponse(mapper.mapErrorCode(response), mapper.mapErrorMessages(response), mapper.mergeMessageLog(previousJobResponse, response), bank, serialize(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRecipientBankCode(param: TransferMoneyParameter): String? {
|
private fun getRecipientBankCode(param: TransferMoneyParameter): String? {
|
||||||
|
@ -192,7 +194,7 @@ open class FinTsClient(
|
||||||
*/
|
*/
|
||||||
open suspend fun getRequiredDataToSendUserJobs(param: FinTsClientParameter): net.dankito.banking.client.model.response.FinTsClientResponse {
|
open suspend fun getRequiredDataToSendUserJobs(param: FinTsClientParameter): net.dankito.banking.client.model.response.FinTsClientResponse {
|
||||||
if (param.finTsModel != null) {
|
if (param.finTsModel != null) {
|
||||||
return net.dankito.banking.client.model.response.FinTsClientResponse(null, null, emptyList(), param.finTsModel)
|
return net.dankito.banking.client.model.response.FinTsClientResponse(null, null, emptyList(), param.finTsModel, serialize(param.finTsModel))
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultValues = (param as? GetAccountDataParameter)?.defaultBankValues
|
val defaultValues = (param as? GetAccountDataParameter)?.defaultBankValues
|
||||||
|
@ -207,7 +209,7 @@ open class FinTsClient(
|
||||||
val getAccountInfoResponse = getAccountInfo(param, bank)
|
val getAccountInfoResponse = getAccountInfo(param, bank)
|
||||||
|
|
||||||
return net.dankito.banking.client.model.response.FinTsClientResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
|
return net.dankito.banking.client.model.response.FinTsClientResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
|
||||||
getAccountInfoResponse.messageLog, bank)
|
getAccountInfoResponse.messageLog, bank, serialize(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun getAccountInfo(param: FinTsClientParameter, bank: BankData): GetAccountInfoResponse {
|
protected open suspend fun getAccountInfo(param: FinTsClientParameter, bank: BankData): GetAccountInfoResponse {
|
||||||
|
@ -234,4 +236,12 @@ open class FinTsClient(
|
||||||
return GetAccountInfoResponse(context, getAccountsResponse)
|
return GetAccountInfoResponse(context, getAccountsResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JvmName("serializeNullable")
|
||||||
|
@JsName("serializeNullable")
|
||||||
|
private fun serialize(bank: BankData?) =
|
||||||
|
bank?.let { serialize(bank) }
|
||||||
|
|
||||||
|
private fun serialize(bank: BankData): String? = mapper.serialize(bank)
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,10 +1,6 @@
|
||||||
package net.codinux.banking.fints.mapper
|
package net.codinux.banking.fints.mapper
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.atTime
|
|
||||||
import kotlinx.datetime.toInstant
|
|
||||||
import net.codinux.banking.fints.extensions.EuropeBerlin
|
|
||||||
import net.dankito.banking.client.model.*
|
import net.dankito.banking.client.model.*
|
||||||
import net.dankito.banking.client.model.AccountTransaction
|
import net.dankito.banking.client.model.AccountTransaction
|
||||||
import net.dankito.banking.client.model.parameter.FinTsClientParameter
|
import net.dankito.banking.client.model.parameter.FinTsClientParameter
|
||||||
|
@ -19,9 +15,12 @@ import net.codinux.banking.fints.response.segments.AccountType
|
||||||
import net.codinux.banking.fints.util.BicFinder
|
import net.codinux.banking.fints.util.BicFinder
|
||||||
import net.codinux.banking.fints.extensions.minusDays
|
import net.codinux.banking.fints.extensions.minusDays
|
||||||
import net.codinux.banking.fints.extensions.todayAtEuropeBerlin
|
import net.codinux.banking.fints.extensions.todayAtEuropeBerlin
|
||||||
|
import net.codinux.banking.fints.serialization.FinTsModelSerializer
|
||||||
|
|
||||||
|
|
||||||
open class FinTsModelMapper {
|
open class FinTsModelMapper(
|
||||||
|
private val serializer: FinTsModelSerializer = FinTsModelSerializer()
|
||||||
|
) {
|
||||||
|
|
||||||
protected open val bicFinder = BicFinder()
|
protected open val bicFinder = BicFinder()
|
||||||
|
|
||||||
|
@ -204,4 +203,7 @@ open class FinTsModelMapper {
|
||||||
return responses.filterNotNull().flatMap { it.messageLog }
|
return responses.filterNotNull().flatMap { it.messageLog }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun serialize(finTsModel: BankData?): String? =
|
||||||
|
finTsModel?.let { serializer.serializeToJson(finTsModel) }
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,6 +23,11 @@ open class TanMedium(
|
||||||
internal constructor() : this(TanMediumKlasse.AlleMedien, TanMediumStatus.Verfuegbar, null) // for object deserializers
|
internal constructor() : this(TanMediumKlasse.AlleMedien, TanMediumStatus.Verfuegbar, null) // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
|
val identifier: String by lazy {
|
||||||
|
"$mediumClass $mediumName $status ${tanGenerator?.cardNumber} ${mobilePhone?.concealedPhoneNumber ?: mobilePhone?.concealedPhoneNumber}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other == null || this::class != other::class) return false
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package net.codinux.banking.fints.model
|
package net.codinux.banking.fints.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
import net.codinux.banking.fints.FinTsClient
|
import net.codinux.banking.fints.FinTsClient
|
||||||
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
||||||
import net.codinux.banking.fints.messages.segmente.id.CustomerSegmentId
|
import net.codinux.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||||
import net.codinux.banking.fints.response.segments.AccountType
|
import net.codinux.banking.fints.response.segments.AccountType
|
||||||
import net.codinux.banking.fints.response.segments.JobParameters
|
import net.codinux.banking.fints.response.segments.JobParameters
|
||||||
|
|
||||||
|
@Serializable
|
||||||
open class AccountData(
|
open class AccountData(
|
||||||
open val accountIdentifier: String,
|
open val accountIdentifier: String,
|
||||||
open val subAccountAttribute: String?,
|
open val subAccountAttribute: String?,
|
||||||
|
@ -20,6 +23,7 @@ open class AccountData(
|
||||||
open val productName: String?,
|
open val productName: String?,
|
||||||
open val accountLimit: String?,
|
open val accountLimit: String?,
|
||||||
open val allowedJobNames: List<String>,
|
open val allowedJobNames: List<String>,
|
||||||
|
@Transient // can be restored from bank.supportedJobs and this.allowedJobNames
|
||||||
open var allowedJobs: List<JobParameters> = listOf()
|
open var allowedJobs: List<JobParameters> = listOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -40,6 +44,7 @@ open class AccountData(
|
||||||
open var serverTransactionsRetentionDays: Int? = null
|
open var serverTransactionsRetentionDays: Int? = null
|
||||||
|
|
||||||
|
|
||||||
|
@SerialName("supportedFeatures")
|
||||||
protected open val _supportedFeatures = mutableSetOf<AccountFeature>()
|
protected open val _supportedFeatures = mutableSetOf<AccountFeature>()
|
||||||
|
|
||||||
open val supportedFeatures: Collection<AccountFeature>
|
open val supportedFeatures: Collection<AccountFeature>
|
||||||
|
|
|
@ -152,7 +152,7 @@ open class ModelMapper(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun findTanMethod(securityFunction: Sicherheitsfunktion, bank: BankData): TanMethod? {
|
open fun findTanMethod(securityFunction: Sicherheitsfunktion, bank: BankData): TanMethod? {
|
||||||
return bank.tanMethodsSupportedByBank.firstOrNull { it.securityFunction == securityFunction }
|
return bank.tanMethodsSupportedByBank.firstOrNull { it.securityFunction == securityFunction }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ open class ModelMapper(
|
||||||
account.setSupportsFeature(AccountFeature.RealTimeTransfer, messageBuilder.supportsSepaRealTimeTransfer(bank, account))
|
account.setSupportsFeature(AccountFeature.RealTimeTransfer, messageBuilder.supportsSepaRealTimeTransfer(bank, account))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun mapToTanMethods(tanInfo: TanInfo): List<TanMethod> {
|
open fun mapToTanMethods(tanInfo: TanInfo): List<TanMethod> {
|
||||||
return tanInfo.tanProcedureParameters.methodParameters.mapNotNull {
|
return tanInfo.tanProcedureParameters.methodParameters.mapNotNull {
|
||||||
mapToTanMethod(it, tanInfo.segmentVersion)
|
mapToTanMethod(it, tanInfo.segmentVersion)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
package net.codinux.banking.fints.response.segments
|
package net.codinux.banking.fints.response.segments
|
||||||
|
|
||||||
import net.codinux.banking.fints.messages.Separators
|
import net.codinux.banking.fints.messages.Separators
|
||||||
import kotlin.jvm.Transient
|
|
||||||
|
|
||||||
|
|
||||||
open class ReceivedSegment(
|
open class ReceivedSegment(
|
||||||
open val segmentId: String,
|
open val segmentId: String,
|
||||||
@Transient
|
|
||||||
open val segmentNumber: Int,
|
open val segmentNumber: Int,
|
||||||
open val segmentVersion: Int,
|
open val segmentVersion: Int,
|
||||||
@Transient
|
|
||||||
open val referenceSegmentNumber: Int? = null,
|
open val referenceSegmentNumber: Int? = null,
|
||||||
@Transient
|
|
||||||
open val segmentString: String
|
open val segmentString: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package net.codinux.banking.fints.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import net.codinux.banking.fints.model.BankData
|
||||||
|
import net.codinux.log.logger
|
||||||
|
|
||||||
|
open class FinTsModelSerializer {
|
||||||
|
|
||||||
|
private val json: Json by lazy {
|
||||||
|
Json { this.ignoreUnknownKeys = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val prettyPrintJson by lazy {
|
||||||
|
Json {
|
||||||
|
this.ignoreUnknownKeys = true
|
||||||
|
this.prettyPrint = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mapper = SerializedFinTsDataMapper()
|
||||||
|
|
||||||
|
private val log by logger()
|
||||||
|
|
||||||
|
|
||||||
|
open fun serializeToJson(bank: BankData, prettyPrint: Boolean = false): String? {
|
||||||
|
return try {
|
||||||
|
val serializableData = mapper.map(bank)
|
||||||
|
|
||||||
|
val json = if (prettyPrint) prettyPrintJson else json
|
||||||
|
|
||||||
|
json.encodeToString(serializableData)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Could not map fints4k model to JSON" }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun deserializeFromJson(serializedFinTsData: String): BankData? = try {
|
||||||
|
val serializedData = json.decodeFromString<SerializedFinTsData>(serializedFinTsData)
|
||||||
|
|
||||||
|
mapper.map(serializedData)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Could not deserialize BankData from JSON:\n$serializedFinTsData"}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package net.codinux.banking.fints.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.EncodeDefault
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.*
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
|
import net.codinux.banking.fints.model.AccountData
|
||||||
|
import net.codinux.banking.fints.model.PinInfo
|
||||||
|
import net.codinux.banking.fints.model.TanMethod
|
||||||
|
import net.codinux.banking.fints.serialization.jobparameter.DetailedSerializableJobParameters
|
||||||
|
import net.codinux.banking.fints.serialization.jobparameter.SerializableJobParameters
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
@Serializable
|
||||||
|
class SerializedFinTsData(
|
||||||
|
val bankCode: String,
|
||||||
|
val customerId: String,
|
||||||
|
val pin: String,
|
||||||
|
val finTs3ServerAddress: String,
|
||||||
|
val bic: String,
|
||||||
|
|
||||||
|
val bankName: String,
|
||||||
|
val countryCode: Int,
|
||||||
|
val bpdVersion: Int,
|
||||||
|
|
||||||
|
val userId: String,
|
||||||
|
val customerName: String,
|
||||||
|
val updVersion: Int,
|
||||||
|
|
||||||
|
val tanMethodsSupportedByBank: List<TanMethod>,
|
||||||
|
val identifierOfTanMethodsAvailableForUser: List<String> = listOf(),
|
||||||
|
val selectedTanMethodIdentifier: String,
|
||||||
|
val tanMedia: List<TanMedium> = listOf(),
|
||||||
|
val selectedTanMediumIdentifier: String? = null,
|
||||||
|
|
||||||
|
val supportedLanguages: List<Dialogsprache> = listOf(),
|
||||||
|
val selectedLanguage: Dialogsprache = Dialogsprache.Default,
|
||||||
|
val customerSystemId: String = KundensystemID.Anonymous,
|
||||||
|
val customerSystemStatus: KundensystemStatusWerte = KundensystemStatus.SynchronizingCustomerSystemId,
|
||||||
|
|
||||||
|
val countMaxJobsPerMessage: Int = 0,
|
||||||
|
|
||||||
|
val supportedHbciVersions: List<HbciVersion> = listOf(),
|
||||||
|
val supportedJobs: List<SerializableJobParameters> = listOf(),
|
||||||
|
val supportedDetailedJobs: List<DetailedSerializableJobParameters> = listOf(),
|
||||||
|
val jobsRequiringTan: Set<String> = emptySet(),
|
||||||
|
|
||||||
|
val pinInfo: PinInfo? = null,
|
||||||
|
|
||||||
|
val accounts: List<AccountData>
|
||||||
|
) {
|
||||||
|
|
||||||
|
@EncodeDefault
|
||||||
|
private val modelVersion: String = "0.6.0"
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package net.codinux.banking.fints.serialization
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.model.BankData
|
||||||
|
import net.codinux.banking.fints.response.segments.ChangeTanMediaParameters
|
||||||
|
import net.codinux.banking.fints.response.segments.JobParameters
|
||||||
|
import net.codinux.banking.fints.response.segments.RetrieveAccountTransactionsParameters
|
||||||
|
import net.codinux.banking.fints.response.segments.SepaAccountInfoParameters
|
||||||
|
import net.codinux.banking.fints.serialization.jobparameter.*
|
||||||
|
import net.codinux.log.logger
|
||||||
|
|
||||||
|
class SerializedFinTsDataMapper {
|
||||||
|
|
||||||
|
private val log by logger()
|
||||||
|
|
||||||
|
|
||||||
|
fun map(bank: BankData) = SerializedFinTsData(
|
||||||
|
bank.bankCode,
|
||||||
|
bank.customerId,
|
||||||
|
bank.pin,
|
||||||
|
bank.finTs3ServerAddress,
|
||||||
|
bank.bic,
|
||||||
|
|
||||||
|
bank.bankName,
|
||||||
|
bank.countryCode,
|
||||||
|
bank.bpdVersion,
|
||||||
|
|
||||||
|
bank.userId,
|
||||||
|
bank.customerName,
|
||||||
|
bank.updVersion,
|
||||||
|
|
||||||
|
bank.tanMethodsSupportedByBank,
|
||||||
|
bank.tanMethodsAvailableForUser.map { it.securityFunction.code },
|
||||||
|
bank.selectedTanMethod.securityFunction.code,
|
||||||
|
bank.tanMedia,
|
||||||
|
bank.selectedTanMedium?.identifier,
|
||||||
|
|
||||||
|
bank.supportedLanguages,
|
||||||
|
bank.selectedLanguage,
|
||||||
|
bank.customerSystemId,
|
||||||
|
bank.customerSystemStatus,
|
||||||
|
|
||||||
|
bank.countMaxJobsPerMessage,
|
||||||
|
|
||||||
|
bank.supportedHbciVersions,
|
||||||
|
bank.supportedJobs.filterNot { isDetailedJobParameters(it) }.map { mapJobParameters(it) },
|
||||||
|
bank.supportedJobs.filter { isDetailedJobParameters(it) }.mapNotNull { mapDetailedJobParameters(it) },
|
||||||
|
bank.jobsRequiringTan,
|
||||||
|
|
||||||
|
bank.pinInfo,
|
||||||
|
|
||||||
|
bank.accounts
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun isDetailedJobParameters(parameters: JobParameters): Boolean =
|
||||||
|
parameters is RetrieveAccountTransactionsParameters
|
||||||
|
|| parameters is SepaAccountInfoParameters
|
||||||
|
|| parameters is ChangeTanMediaParameters
|
||||||
|
|
||||||
|
private fun mapJobParameters(parameters: JobParameters) = SerializableJobParameters(
|
||||||
|
parameters.jobName,
|
||||||
|
parameters.maxCountJobs,
|
||||||
|
parameters.minimumCountSignatures,
|
||||||
|
parameters.securityClass,
|
||||||
|
|
||||||
|
parameters.segmentId,
|
||||||
|
parameters.segmentNumber,
|
||||||
|
parameters.segmentVersion,
|
||||||
|
|
||||||
|
parameters.segmentString
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun mapDetailedJobParameters(parameters: JobParameters): DetailedSerializableJobParameters? = when (parameters) {
|
||||||
|
is RetrieveAccountTransactionsParameters -> SerializableRetrieveAccountTransactionsParameters(mapJobParameters(parameters), parameters.serverTransactionsRetentionDays, parameters.settingCountEntriesAllowed, parameters.settingAllAccountAllowed, parameters.supportedCamtDataFormats)
|
||||||
|
is SepaAccountInfoParameters -> SerializableSepaAccountInfoParameters(mapJobParameters(parameters), parameters.retrieveSingleAccountAllowed, parameters.nationalAccountRelationshipAllowed, parameters.structuredReferenceAllowed, parameters.settingMaxAllowedEntriesAllowed, parameters.countReservedReferenceLength, parameters.supportedSepaFormats)
|
||||||
|
is ChangeTanMediaParameters -> SerializableChangeTanMediaParameters(mapJobParameters(parameters), parameters.enteringTanListNumberRequired, parameters.enteringCardSequenceNumberRequired, parameters.enteringAtcAndTanRequired, parameters.enteringCardTypeAllowed, parameters.accountInfoRequired, parameters.allowedCardTypes)
|
||||||
|
else -> {
|
||||||
|
log.warn { "${parameters::class} is said to be a DetailedJobParameters class, but found no mapping code for it" }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun map(bank: SerializedFinTsData) = BankData(
|
||||||
|
bank.bankCode,
|
||||||
|
bank.customerId,
|
||||||
|
bank.pin,
|
||||||
|
bank.finTs3ServerAddress,
|
||||||
|
bank.bic,
|
||||||
|
|
||||||
|
bank.bankName,
|
||||||
|
bank.countryCode,
|
||||||
|
bank.bpdVersion,
|
||||||
|
|
||||||
|
bank.userId,
|
||||||
|
bank.customerName,
|
||||||
|
bank.updVersion,
|
||||||
|
|
||||||
|
bank.tanMethodsSupportedByBank,
|
||||||
|
bank.tanMethodsSupportedByBank.filter { it.securityFunction.code in bank.identifierOfTanMethodsAvailableForUser },
|
||||||
|
bank.tanMethodsSupportedByBank.first { it.securityFunction.code == bank.selectedTanMethodIdentifier },
|
||||||
|
bank.tanMedia,
|
||||||
|
bank.selectedTanMediumIdentifier?.let { id -> bank.tanMedia.firstOrNull { it.identifier == id } },
|
||||||
|
|
||||||
|
bank.supportedLanguages,
|
||||||
|
bank.selectedLanguage,
|
||||||
|
bank.customerSystemId,
|
||||||
|
bank.customerSystemStatus,
|
||||||
|
|
||||||
|
bank.countMaxJobsPerMessage,
|
||||||
|
|
||||||
|
bank.supportedHbciVersions,
|
||||||
|
bank.supportedJobs.map { mapJobParameters(it) } + bank.supportedDetailedJobs.map { mapDetailedJobParameters(it) },
|
||||||
|
bank.jobsRequiringTan
|
||||||
|
).apply {
|
||||||
|
pinInfo = bank.pinInfo
|
||||||
|
|
||||||
|
bank.accounts.forEach { account ->
|
||||||
|
account.allowedJobs = this.supportedJobs.filter { it.jobName in account.allowedJobNames }
|
||||||
|
this.addAccount(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapJobParameters(parameters: SerializableJobParameters) = JobParameters(
|
||||||
|
parameters.jobName,
|
||||||
|
parameters.maxCountJobs,
|
||||||
|
parameters.minimumCountSignatures,
|
||||||
|
parameters.securityClass,
|
||||||
|
|
||||||
|
parameters.segmentString
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun mapDetailedJobParameters(parameters: DetailedSerializableJobParameters): JobParameters = when (parameters) {
|
||||||
|
is SerializableRetrieveAccountTransactionsParameters -> RetrieveAccountTransactionsParameters(mapJobParameters(parameters.jobParameters), parameters.serverTransactionsRetentionDays, parameters.settingCountEntriesAllowed, parameters.settingAllAccountAllowed, parameters.supportedCamtDataFormats)
|
||||||
|
is SerializableSepaAccountInfoParameters -> SepaAccountInfoParameters(mapJobParameters(parameters.jobParameters), parameters.retrieveSingleAccountAllowed, parameters.nationalAccountRelationshipAllowed, parameters.structuredReferenceAllowed, parameters.settingMaxAllowedEntriesAllowed, parameters.countReservedReferenceLength, parameters.supportedSepaFormats)
|
||||||
|
is SerializableChangeTanMediaParameters -> ChangeTanMediaParameters(mapJobParameters(parameters.jobParameters), parameters.enteringTanListNumberRequired, parameters.enteringCardSequenceNumberRequired, parameters.enteringAtcAndTanRequired, parameters.enteringCardTypeAllowed, parameters.accountInfoRequired, parameters.allowedCardTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package net.codinux.banking.fints.serialization.jobparameter
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed class DetailedSerializableJobParameters {
|
||||||
|
|
||||||
|
abstract val jobParameters: SerializableJobParameters
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString() = jobParameters.toString()
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package net.codinux.banking.fints.serialization.jobparameter
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("ChangeTanMediaParameters")
|
||||||
|
class SerializableChangeTanMediaParameters(
|
||||||
|
override val jobParameters: SerializableJobParameters,
|
||||||
|
|
||||||
|
val enteringTanListNumberRequired: Boolean,
|
||||||
|
val enteringCardSequenceNumberRequired: Boolean,
|
||||||
|
val enteringAtcAndTanRequired: Boolean,
|
||||||
|
val enteringCardTypeAllowed: Boolean,
|
||||||
|
val accountInfoRequired: Boolean,
|
||||||
|
val allowedCardTypes: List<Int>
|
||||||
|
) : DetailedSerializableJobParameters()
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.fints.serialization.jobparameter
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SerializableJobParameters(
|
||||||
|
val jobName: String,
|
||||||
|
val maxCountJobs: Int,
|
||||||
|
val minimumCountSignatures: Int,
|
||||||
|
val securityClass: Int?,
|
||||||
|
|
||||||
|
val segmentId: String,
|
||||||
|
val segmentNumber: Int,
|
||||||
|
val segmentVersion: Int,
|
||||||
|
|
||||||
|
val segmentString: String
|
||||||
|
) {
|
||||||
|
override fun toString() = "$jobName $segmentVersion"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package net.codinux.banking.fints.serialization.jobparameter
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("RetrieveAccountTransactionsParameters")
|
||||||
|
class SerializableRetrieveAccountTransactionsParameters(
|
||||||
|
override val jobParameters: SerializableJobParameters,
|
||||||
|
|
||||||
|
val serverTransactionsRetentionDays: Int,
|
||||||
|
val settingCountEntriesAllowed: Boolean,
|
||||||
|
val settingAllAccountAllowed: Boolean,
|
||||||
|
val supportedCamtDataFormats: List<String> = emptyList()
|
||||||
|
) : DetailedSerializableJobParameters() {
|
||||||
|
override fun toString() = "${super.toString()}, serverTransactionsRetentionDays = $serverTransactionsRetentionDays, supportedCamtDataFormats = $supportedCamtDataFormats"
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.fints.serialization.jobparameter
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("SepaAccountInfoParameters")
|
||||||
|
class SerializableSepaAccountInfoParameters(
|
||||||
|
override val jobParameters: SerializableJobParameters,
|
||||||
|
|
||||||
|
val retrieveSingleAccountAllowed: Boolean,
|
||||||
|
val nationalAccountRelationshipAllowed: Boolean,
|
||||||
|
val structuredReferenceAllowed: Boolean,
|
||||||
|
val settingMaxAllowedEntriesAllowed: Boolean,
|
||||||
|
val countReservedReferenceLength: Int,
|
||||||
|
val supportedSepaFormats: List<String>
|
||||||
|
) : DetailedSerializableJobParameters() {
|
||||||
|
override fun toString() = "${super.toString()}, supportedSepaFormats = $supportedSepaFormats"
|
||||||
|
}
|
|
@ -9,7 +9,8 @@ open class FinTsClientResponse(
|
||||||
open val error: ErrorCode?,
|
open val error: ErrorCode?,
|
||||||
open val errorMessage: String?,
|
open val errorMessage: String?,
|
||||||
open val messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
open val messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
||||||
open val finTsModel: BankData? = null
|
open val finTsModel: BankData? = null,
|
||||||
|
open val serializedFinTsModel: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
internal constructor() : this(null, null, listOf()) // for object deserializers
|
internal constructor() : this(null, null, listOf()) // for object deserializers
|
||||||
|
|
|
@ -10,8 +10,9 @@ open class GetAccountDataResponse(
|
||||||
errorMessage: String?,
|
errorMessage: String?,
|
||||||
open val customerAccount: CustomerAccount?,
|
open val customerAccount: CustomerAccount?,
|
||||||
messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
||||||
finTsModel: BankData? = null
|
finTsModel: BankData? = null,
|
||||||
) : FinTsClientResponse(error, errorMessage, messageLogWithoutSensitiveData, finTsModel) {
|
serializedFinTsModel: String? = null
|
||||||
|
) : FinTsClientResponse(error, errorMessage, messageLogWithoutSensitiveData, finTsModel, serializedFinTsModel) {
|
||||||
|
|
||||||
internal constructor() : this(null, null, null, listOf()) // for object deserializers
|
internal constructor() : this(null, null, null, listOf()) // for object deserializers
|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,6 @@ open class TransferMoneyResponse(
|
||||||
error: ErrorCode?,
|
error: ErrorCode?,
|
||||||
errorMessage: String?,
|
errorMessage: String?,
|
||||||
messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
messageLogWithoutSensitiveData: List<MessageLogEntry>,
|
||||||
finTsModel: BankData? = null
|
finTsModel: BankData? = null,
|
||||||
) : FinTsClientResponse(error, errorMessage, messageLogWithoutSensitiveData, finTsModel)
|
serializedFinTsModel: String? = null
|
||||||
|
) : FinTsClientResponse(error, errorMessage, messageLogWithoutSensitiveData, finTsModel, serializedFinTsModel)
|
|
@ -0,0 +1,586 @@
|
||||||
|
package net.codinux.banking.fints.serialization
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.messages.MessageBuilder
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.Dialogsprache
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.HbciVersion
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus
|
||||||
|
import net.codinux.banking.fints.model.AccountData
|
||||||
|
import net.codinux.banking.fints.model.AccountFeature
|
||||||
|
import net.codinux.banking.fints.model.BankData
|
||||||
|
import net.codinux.banking.fints.model.mapper.ModelMapper
|
||||||
|
import net.codinux.banking.fints.response.InstituteSegmentId
|
||||||
|
import net.codinux.banking.fints.response.ResponseParser
|
||||||
|
import net.codinux.banking.fints.response.segments.AccountType
|
||||||
|
import net.codinux.banking.fints.response.segments.TanInfo
|
||||||
|
import net.codinux.banking.fints.response.segments.TanMediaList
|
||||||
|
import net.codinux.banking.fints.test.assertContains
|
||||||
|
import net.codinux.banking.fints.test.assertSize
|
||||||
|
import net.codinux.banking.fints.test.assertTrue
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
class FinTsModelSerializerTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val bankCode = "10010010"
|
||||||
|
private val bic = "ABCDDEBBXXX"
|
||||||
|
private val bankName = "Abzockbank"
|
||||||
|
private val serverAddress = "https://abzockbank.de/fints"
|
||||||
|
private val bpd = 17
|
||||||
|
|
||||||
|
private val customerId = "SuperUser"
|
||||||
|
private val password = "Liebe"
|
||||||
|
private val customerName = "Monika Superfrau"
|
||||||
|
private val upd = 27
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val underTest = FinTsModelSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun serializeToJson() {
|
||||||
|
val bank = createTestData()
|
||||||
|
|
||||||
|
val result = underTest.serializeToJson(bank, true)
|
||||||
|
|
||||||
|
assertEquals(serializedFinTsData, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deserializeFromJson() {
|
||||||
|
val result = underTest.deserializeFromJson(serializedFinTsData)
|
||||||
|
|
||||||
|
assertNotNull(result)
|
||||||
|
|
||||||
|
assertSize(8, result.tanMethodsSupportedByBank)
|
||||||
|
assertSize(4, result.tanMethodsAvailableForUser)
|
||||||
|
assertContains(result.tanMethodsSupportedByBank, result.tanMethodsAvailableForUser) // check that it contains exactly the same object instances
|
||||||
|
assertNotNull(result.selectedTanMethod)
|
||||||
|
assertContains(result.tanMethodsSupportedByBank, result.selectedTanMethod) // check that it contains exactly the same object instance
|
||||||
|
|
||||||
|
assertSize(3, result.tanMedia)
|
||||||
|
assertNotNull(result.selectedTanMedium)
|
||||||
|
assertContains(result.tanMedia, result.selectedTanMedium) // check that it contains exactly the same object instance
|
||||||
|
|
||||||
|
assertSize(14, result.supportedJobs)
|
||||||
|
assertSize(33, result.jobsRequiringTan)
|
||||||
|
|
||||||
|
result.accounts.forEach { account ->
|
||||||
|
assertTrue(account.allowedJobs.isNotEmpty())
|
||||||
|
assertContains(result.supportedJobs, account.allowedJobs) // check that it contains exactly the same object instances
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(serializedFinTsData, underTest.serializeToJson(result, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createTestData(): BankData {
|
||||||
|
val parser = ResponseParser()
|
||||||
|
val mapper = ModelMapper(MessageBuilder())
|
||||||
|
|
||||||
|
val bankResponse = parser.parse("""
|
||||||
|
HIRMS:5:2:4+3050::BPD nicht mehr aktuell, aktuelle Version enthalten.+3920::Zugelassene Zwei-Schritt-Verfahren für den Benutzer.:910:911:912:913+0020::Der Auftrag wurde ausgeführt.'
|
||||||
|
HISALS:145:5:4+1+1'
|
||||||
|
HISALS:12:8:4+1+1+0+J'
|
||||||
|
HIKAZS:123:5:4+1+1+360:J:N'
|
||||||
|
HICCSS:96:1:4+1+1+0'
|
||||||
|
HIIPZS:22:1:4+1+1+0+;:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.09'
|
||||||
|
DIKKUS:67:2:4+1+1+0+90:N:J'
|
||||||
|
HITABS:153:4:4+1+1+0'
|
||||||
|
HITAUS:154:1:4+1+1+0+N:N:J'
|
||||||
|
HITANS:169:6:4+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0'
|
||||||
|
HITANS:170:7:4+1+1+1+N:N:0:922:2:pushTAN-dec:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:2:180:1:1:J:J:923:2:pushTAN-cas:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:5:180:1:1:J:J'
|
||||||
|
HITAB:5:4:3+1+G:2:1234567890::::::::::SparkassenCard (Debitkarte)::::::::+G:1:1234567891::::::::::SparkassenCard (Debitkarte)::::::::'
|
||||||
|
HITAB:5:4:3+0+A:1:::::::::::Alle Geräte::::::::'
|
||||||
|
HIPINS:78:1:3+1+1+0+5:20:6:VR-NetKey oder Alias::HKTAN:N:HKKAZ:J:HKSAL:N:HKEKA:N:HKPAE:J:HKPSP:N:HKQTG:N:HKCSA:J:HKCSB:N:HKCSL:J:HKCCS:J:HKSPA:N:HKDSE:J:HKBSE:J:HKBME:J:HKCDL:J:HKPPD:J:HKCDN:J:HKDSB:N:HKCUB:N:HKDSW:J:HKAUB:J:HKBBS:N:HKDMB:N:HKDBS:N:HKBMB:N:HKECA:N:HKCMB:N:HKCME:J:HKCML:J:HKWDU:N:HKWPD:N:HKDME:J:HKCCM:J:HKCDB:N:HKCDE:J:HKCSE:J:HKCUM:J:HKKAU:N:HKKIF:N:HKBAZ:N:HKZDF:J:HKCAZ:J:HKDDB:N:HKDDE:J:HKDDL:J:HKDDN:J:HKKAA:N:HKPOF:N:HKIPS:N:HKIPZ:J:HKBML:J:HKBSA:J:HKBSL:J:HKDML:J:HKDSA:J:HKDSL:J:HKZDA:J:HKZDL:N:GKVPU:N:GKVPD:N'
|
||||||
|
HISPAS:42:1:4+20+1+0+N:N:N:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02'
|
||||||
|
HISPAS:43:2:4+20+1+0+N:N:N:N:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02'
|
||||||
|
HISPAS:44:3:4+20+1+0+N:N:N:N:0:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02'
|
||||||
|
""".trimIndent().replace("\n", "").replace("\r", ""))
|
||||||
|
|
||||||
|
val tanMethods = bankResponse.getSegmentsById<TanInfo>(InstituteSegmentId.TanInfo).flatMap { mapper.mapToTanMethods(it) }
|
||||||
|
val tanMedia = bankResponse.getSegmentsById<TanMediaList>(InstituteSegmentId.TanMediaList).flatMap { it.tanMedia }
|
||||||
|
|
||||||
|
return BankData(
|
||||||
|
bankCode, customerId, password, serverAddress, bic, bankName, Laenderkennzeichen.Germany, bpd,
|
||||||
|
customerId, customerName, upd,
|
||||||
|
|
||||||
|
tanMethods, tanMethods.filter { it.securityFunction.code.startsWith("91") }, tanMethods.first { it.securityFunction.code == "912" },
|
||||||
|
tanMedia, tanMedia.first { it.status == TanMediumStatus.Aktiv },
|
||||||
|
|
||||||
|
listOf(Dialogsprache.German, Dialogsprache.English), Dialogsprache.German, "47", KundensystemStatusWerte.Benoetigt,
|
||||||
|
1, listOf(HbciVersion.FinTs_3_0_0)
|
||||||
|
).apply {
|
||||||
|
this.addAccount(createCheckingAccount(this, listOf("HKSAL", "HKKAZ", "HKCCS", "HKIPZ")))
|
||||||
|
this.addAccount(createCreditCardAccount(this, listOf("DKKKU")))
|
||||||
|
|
||||||
|
mapper.updateBankData(this, bankResponse)
|
||||||
|
|
||||||
|
this.jobsRequiringTan = this.jobsRequiringTan.sorted().toSet() // sort them for comparability in tests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCheckingAccount(bank: BankData, allowedJobNames: List<String>) =
|
||||||
|
createAccount(bank, AccountType.Girokonto, "12345678", "Kontokorrent", allowedJobNames, AccountFeature.entries)
|
||||||
|
|
||||||
|
private fun createCreditCardAccount(bank: BankData, allowedJobNames: List<String>) =
|
||||||
|
createAccount(bank, AccountType.Kreditkartenkonto, "4321876521096543", "Visa-Card", allowedJobNames, listOf(AccountFeature.RetrieveAccountTransactions))
|
||||||
|
|
||||||
|
private fun createAccount(bank: BankData, type: AccountType, accountIdentifier: String, productName: String? = null, allowedJobNames: List<String>, features: Collection<AccountFeature> = emptyList(), subAccountAttribute: String? = null) = AccountData(
|
||||||
|
accountIdentifier, subAccountAttribute, Laenderkennzeichen.Germany, bankCode, "DE11$bankCode$accountIdentifier", customerId, type, "EUR", customerName, productName, "T:1000,:EUR", allowedJobNames, bank.supportedJobs.filter { allowedJobNames.contains(it.jobName) }
|
||||||
|
).apply {
|
||||||
|
this.serverTransactionsRetentionDays = 270
|
||||||
|
|
||||||
|
features.forEach { feature ->
|
||||||
|
this.setSupportsFeature(feature, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val serializedFinTsData = """
|
||||||
|
{
|
||||||
|
"bankCode": "10010010",
|
||||||
|
"customerId": "SuperUser",
|
||||||
|
"pin": "Liebe",
|
||||||
|
"finTs3ServerAddress": "https://abzockbank.de/fints",
|
||||||
|
"bic": "ABCDDEBBXXX",
|
||||||
|
"bankName": "Abzockbank",
|
||||||
|
"countryCode": 280,
|
||||||
|
"bpdVersion": 17,
|
||||||
|
"userId": "SuperUser",
|
||||||
|
"customerName": "Monika Superfrau",
|
||||||
|
"updVersion": 27,
|
||||||
|
"tanMethodsSupportedByBank": [
|
||||||
|
{
|
||||||
|
"displayName": "chipTAN manuell",
|
||||||
|
"securityFunction": "PIN_TAN_910",
|
||||||
|
"type": "ChipTanManuell",
|
||||||
|
"hhdVersion": "HHD_1_3",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "chipTAN optisch",
|
||||||
|
"securityFunction": "PIN_TAN_911",
|
||||||
|
"type": "ChipTanFlickercode",
|
||||||
|
"hhdVersion": "HHD_1_3",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "chipTAN-USB",
|
||||||
|
"securityFunction": "PIN_TAN_912",
|
||||||
|
"type": "ChipTanUsb",
|
||||||
|
"hhdVersion": "HHD_1_3",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "chipTAN-QR",
|
||||||
|
"securityFunction": "PIN_TAN_913",
|
||||||
|
"type": "ChipTanQrCode",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "smsTAN",
|
||||||
|
"securityFunction": "PIN_TAN_920",
|
||||||
|
"type": "SmsTan",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric",
|
||||||
|
"nameOfTanMediumRequired": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "pushTAN",
|
||||||
|
"securityFunction": "PIN_TAN_921",
|
||||||
|
"type": "AppTan",
|
||||||
|
"maxTanInputLength": 6,
|
||||||
|
"allowedTanFormat": "Numeric",
|
||||||
|
"nameOfTanMediumRequired": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "pushTAN 2.0",
|
||||||
|
"securityFunction": "PIN_TAN_922",
|
||||||
|
"type": "DecoupledTan",
|
||||||
|
"nameOfTanMediumRequired": true,
|
||||||
|
"hktanVersion": 7,
|
||||||
|
"decoupledParameters": {
|
||||||
|
"manualConfirmationAllowed": true,
|
||||||
|
"periodicStateRequestsAllowed": true,
|
||||||
|
"maxNumberOfStateRequests": 180,
|
||||||
|
"initialDelayInSecondsForStateRequest": 1,
|
||||||
|
"delayInSecondsForNextStateRequest": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "pushTAN 2.0",
|
||||||
|
"securityFunction": "PIN_TAN_923",
|
||||||
|
"type": "DecoupledTan",
|
||||||
|
"nameOfTanMediumRequired": true,
|
||||||
|
"hktanVersion": 7,
|
||||||
|
"decoupledParameters": {
|
||||||
|
"manualConfirmationAllowed": true,
|
||||||
|
"periodicStateRequestsAllowed": true,
|
||||||
|
"maxNumberOfStateRequests": 180,
|
||||||
|
"initialDelayInSecondsForStateRequest": 1,
|
||||||
|
"delayInSecondsForNextStateRequest": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"identifierOfTanMethodsAvailableForUser": [
|
||||||
|
"910",
|
||||||
|
"911",
|
||||||
|
"912",
|
||||||
|
"913"
|
||||||
|
],
|
||||||
|
"selectedTanMethodIdentifier": "912",
|
||||||
|
"tanMedia": [
|
||||||
|
{
|
||||||
|
"mediumClass": "TanGenerator",
|
||||||
|
"status": "Verfuegbar",
|
||||||
|
"mediumName": "SparkassenCard (Debitkarte)",
|
||||||
|
"tanGenerator": {
|
||||||
|
"cardNumber": "1234567890",
|
||||||
|
"cardSequenceNumber": null,
|
||||||
|
"cardType": null,
|
||||||
|
"validFrom": null,
|
||||||
|
"validTo": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediumClass": "TanGenerator",
|
||||||
|
"status": "Aktiv",
|
||||||
|
"mediumName": "SparkassenCard (Debitkarte)",
|
||||||
|
"tanGenerator": {
|
||||||
|
"cardNumber": "1234567891",
|
||||||
|
"cardSequenceNumber": null,
|
||||||
|
"cardType": null,
|
||||||
|
"validFrom": null,
|
||||||
|
"validTo": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediumClass": "AlleMedien",
|
||||||
|
"status": "Aktiv",
|
||||||
|
"mediumName": "Alle Geräte"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectedTanMediumIdentifier": "TanGenerator SparkassenCard (Debitkarte) Aktiv 1234567891 null",
|
||||||
|
"supportedLanguages": [
|
||||||
|
"German",
|
||||||
|
"English"
|
||||||
|
],
|
||||||
|
"selectedLanguage": "German",
|
||||||
|
"customerSystemId": "47",
|
||||||
|
"customerSystemStatus": "Benoetigt",
|
||||||
|
"countMaxJobsPerMessage": 1,
|
||||||
|
"supportedHbciVersions": [
|
||||||
|
"FinTs_3_0_0"
|
||||||
|
],
|
||||||
|
"supportedJobs": [
|
||||||
|
{
|
||||||
|
"jobName": "HKSAL",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": null,
|
||||||
|
"segmentId": "HISALS",
|
||||||
|
"segmentNumber": 145,
|
||||||
|
"segmentVersion": 5,
|
||||||
|
"segmentString": "HISALS:145:5:4+1+1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKSAL",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HISALS",
|
||||||
|
"segmentNumber": 12,
|
||||||
|
"segmentVersion": 8,
|
||||||
|
"segmentString": "HISALS:12:8:4+1+1+0+J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKCCS",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HICCSS",
|
||||||
|
"segmentNumber": 96,
|
||||||
|
"segmentVersion": 1,
|
||||||
|
"segmentString": "HICCSS:96:1:4+1+1+0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKIPZ",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HIIPZS",
|
||||||
|
"segmentNumber": 22,
|
||||||
|
"segmentVersion": 1,
|
||||||
|
"segmentString": "HIIPZS:22:1:4+1+1+0+;:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.09"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKTAB",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HITABS",
|
||||||
|
"segmentNumber": 153,
|
||||||
|
"segmentVersion": 4,
|
||||||
|
"segmentString": "HITABS:153:4:4+1+1+0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKTAN",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 1,
|
||||||
|
"segmentId": "HITANS",
|
||||||
|
"segmentNumber": 169,
|
||||||
|
"segmentVersion": 6,
|
||||||
|
"segmentString": "HITANS:169:6:4+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKTAN",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 1,
|
||||||
|
"segmentId": "HITANS",
|
||||||
|
"segmentNumber": 170,
|
||||||
|
"segmentVersion": 7,
|
||||||
|
"segmentString": "HITANS:170:7:4+1+1+1+N:N:0:922:2:pushTAN-dec:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:2:180:1:1:J:J:923:2:pushTAN-cas:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:5:180:1:1:J:J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jobName": "HKPIN",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HIPINS",
|
||||||
|
"segmentNumber": 78,
|
||||||
|
"segmentVersion": 1,
|
||||||
|
"segmentString": "HIPINS:78:1:3+1+1+0+5:20:6:VR-NetKey oder Alias::HKTAN:N:HKKAZ:J:HKSAL:N:HKEKA:N:HKPAE:J:HKPSP:N:HKQTG:N:HKCSA:J:HKCSB:N:HKCSL:J:HKCCS:J:HKSPA:N:HKDSE:J:HKBSE:J:HKBME:J:HKCDL:J:HKPPD:J:HKCDN:J:HKDSB:N:HKCUB:N:HKDSW:J:HKAUB:J:HKBBS:N:HKDMB:N:HKDBS:N:HKBMB:N:HKECA:N:HKCMB:N:HKCME:J:HKCML:J:HKWDU:N:HKWPD:N:HKDME:J:HKCCM:J:HKCDB:N:HKCDE:J:HKCSE:J:HKCUM:J:HKKAU:N:HKKIF:N:HKBAZ:N:HKZDF:J:HKCAZ:J:HKDDB:N:HKDDE:J:HKDDL:J:HKDDN:J:HKKAA:N:HKPOF:N:HKIPS:N:HKIPZ:J:HKBML:J:HKBSA:J:HKBSL:J:HKDML:J:HKDSA:J:HKDSL:J:HKZDA:J:HKZDL:N:GKVPU:N:GKVPD:N"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supportedDetailedJobs": [
|
||||||
|
{
|
||||||
|
"type": "RetrieveAccountTransactionsParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "HKKAZ",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": null,
|
||||||
|
"segmentId": "HIKAZS",
|
||||||
|
"segmentNumber": 123,
|
||||||
|
"segmentVersion": 5,
|
||||||
|
"segmentString": "HIKAZS:123:5:4+1+1+360:J:N"
|
||||||
|
},
|
||||||
|
"serverTransactionsRetentionDays": 360,
|
||||||
|
"settingCountEntriesAllowed": true,
|
||||||
|
"settingAllAccountAllowed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "RetrieveAccountTransactionsParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "DKKKU",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "DIKKUS",
|
||||||
|
"segmentNumber": 67,
|
||||||
|
"segmentVersion": 2,
|
||||||
|
"segmentString": "DIKKUS:67:2:4+1+1+0+90:N:J"
|
||||||
|
},
|
||||||
|
"serverTransactionsRetentionDays": 90,
|
||||||
|
"settingCountEntriesAllowed": false,
|
||||||
|
"settingAllAccountAllowed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ChangeTanMediaParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "HKTAU",
|
||||||
|
"maxCountJobs": 1,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HITAUS",
|
||||||
|
"segmentNumber": 154,
|
||||||
|
"segmentVersion": 1,
|
||||||
|
"segmentString": "HITAUS:154:1:4+1+1+0+N:N:J"
|
||||||
|
},
|
||||||
|
"enteringTanListNumberRequired": false,
|
||||||
|
"enteringCardSequenceNumberRequired": false,
|
||||||
|
"enteringAtcAndTanRequired": true,
|
||||||
|
"enteringCardTypeAllowed": false,
|
||||||
|
"accountInfoRequired": false,
|
||||||
|
"allowedCardTypes": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SepaAccountInfoParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "HKSPA",
|
||||||
|
"maxCountJobs": 20,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HISPAS",
|
||||||
|
"segmentNumber": 42,
|
||||||
|
"segmentVersion": 1,
|
||||||
|
"segmentString": "HISPAS:42:1:4+20+1+0+N:N:N:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02"
|
||||||
|
},
|
||||||
|
"retrieveSingleAccountAllowed": false,
|
||||||
|
"nationalAccountRelationshipAllowed": false,
|
||||||
|
"structuredReferenceAllowed": false,
|
||||||
|
"settingMaxAllowedEntriesAllowed": false,
|
||||||
|
"countReservedReferenceLength": 0,
|
||||||
|
"supportedSepaFormats": [
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.003.02",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.001.02"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SepaAccountInfoParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "HKSPA",
|
||||||
|
"maxCountJobs": 20,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HISPAS",
|
||||||
|
"segmentNumber": 43,
|
||||||
|
"segmentVersion": 2,
|
||||||
|
"segmentString": "HISPAS:43:2:4+20+1+0+N:N:N:N:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02"
|
||||||
|
},
|
||||||
|
"retrieveSingleAccountAllowed": false,
|
||||||
|
"nationalAccountRelationshipAllowed": false,
|
||||||
|
"structuredReferenceAllowed": false,
|
||||||
|
"settingMaxAllowedEntriesAllowed": false,
|
||||||
|
"countReservedReferenceLength": 0,
|
||||||
|
"supportedSepaFormats": [
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.003.02",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.001.02"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SepaAccountInfoParameters",
|
||||||
|
"jobParameters": {
|
||||||
|
"jobName": "HKSPA",
|
||||||
|
"maxCountJobs": 20,
|
||||||
|
"minimumCountSignatures": 1,
|
||||||
|
"securityClass": 0,
|
||||||
|
"segmentId": "HISPAS",
|
||||||
|
"segmentNumber": 44,
|
||||||
|
"segmentVersion": 3,
|
||||||
|
"segmentString": "HISPAS:44:3:4+20+1+0+N:N:N:N:0:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.001.02"
|
||||||
|
},
|
||||||
|
"retrieveSingleAccountAllowed": false,
|
||||||
|
"nationalAccountRelationshipAllowed": false,
|
||||||
|
"structuredReferenceAllowed": false,
|
||||||
|
"settingMaxAllowedEntriesAllowed": false,
|
||||||
|
"countReservedReferenceLength": 0,
|
||||||
|
"supportedSepaFormats": [
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.003.02",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
|
||||||
|
"urn:iso:std:iso:20022:tech:xsd:pain.008.001.02"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jobsRequiringTan": [
|
||||||
|
"HKAUB",
|
||||||
|
"HKBME",
|
||||||
|
"HKBML",
|
||||||
|
"HKBSA",
|
||||||
|
"HKBSE",
|
||||||
|
"HKBSL",
|
||||||
|
"HKCAZ",
|
||||||
|
"HKCCM",
|
||||||
|
"HKCCS",
|
||||||
|
"HKCDE",
|
||||||
|
"HKCDL",
|
||||||
|
"HKCDN",
|
||||||
|
"HKCME",
|
||||||
|
"HKCML",
|
||||||
|
"HKCSA",
|
||||||
|
"HKCSE",
|
||||||
|
"HKCSL",
|
||||||
|
"HKCUM",
|
||||||
|
"HKDDE",
|
||||||
|
"HKDDL",
|
||||||
|
"HKDDN",
|
||||||
|
"HKDME",
|
||||||
|
"HKDML",
|
||||||
|
"HKDSA",
|
||||||
|
"HKDSE",
|
||||||
|
"HKDSL",
|
||||||
|
"HKDSW",
|
||||||
|
"HKIPZ",
|
||||||
|
"HKKAZ",
|
||||||
|
"HKPAE",
|
||||||
|
"HKPPD",
|
||||||
|
"HKZDA",
|
||||||
|
"HKZDF"
|
||||||
|
],
|
||||||
|
"pinInfo": {
|
||||||
|
"minPinLength": 5,
|
||||||
|
"maxPinLength": 20,
|
||||||
|
"minTanLength": 6,
|
||||||
|
"userIdHint": "VR-NetKey oder Alias",
|
||||||
|
"customerIdHint": null
|
||||||
|
},
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"accountIdentifier": "12345678",
|
||||||
|
"subAccountAttribute": null,
|
||||||
|
"bankCountryCode": 280,
|
||||||
|
"bankCode": "10010010",
|
||||||
|
"iban": "DE111001001012345678",
|
||||||
|
"customerId": "SuperUser",
|
||||||
|
"accountType": "Girokonto",
|
||||||
|
"currency": "EUR",
|
||||||
|
"accountHolderName": "Monika Superfrau",
|
||||||
|
"productName": "Kontokorrent",
|
||||||
|
"accountLimit": "T:1000,:EUR",
|
||||||
|
"allowedJobNames": [
|
||||||
|
"HKSAL",
|
||||||
|
"HKKAZ",
|
||||||
|
"HKCCS",
|
||||||
|
"HKIPZ"
|
||||||
|
],
|
||||||
|
"serverTransactionsRetentionDays": 270,
|
||||||
|
"supportedFeatures": [
|
||||||
|
"RetrieveBalance",
|
||||||
|
"RetrieveAccountTransactions",
|
||||||
|
"TransferMoney",
|
||||||
|
"RealTimeTransfer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountIdentifier": "4321876521096543",
|
||||||
|
"subAccountAttribute": null,
|
||||||
|
"bankCountryCode": 280,
|
||||||
|
"bankCode": "10010010",
|
||||||
|
"iban": "DE11100100104321876521096543",
|
||||||
|
"customerId": "SuperUser",
|
||||||
|
"accountType": "Kreditkartenkonto",
|
||||||
|
"currency": "EUR",
|
||||||
|
"accountHolderName": "Monika Superfrau",
|
||||||
|
"productName": "Visa-Card",
|
||||||
|
"accountLimit": "T:1000,:EUR",
|
||||||
|
"allowedJobNames": [
|
||||||
|
"DKKKU"
|
||||||
|
],
|
||||||
|
"serverTransactionsRetentionDays": 270,
|
||||||
|
"supportedFeatures": [
|
||||||
|
"RetrieveAccountTransactions"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modelVersion": "0.6.0"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
|
@ -61,6 +61,12 @@ fun <T : Any?> assertContains(collection: Collection<T>, vararg items: T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : Any?> assertContains(collection: Collection<T>, items: Collection<T>) {
|
||||||
|
items.forEach { item ->
|
||||||
|
kotlin.test.assertContains(collection, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified T : Throwable> assertThrows(action: () -> Unit) {
|
inline fun <reified T : Throwable> assertThrows(action: () -> Unit) {
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in New Issue