Compare commits
126 commits
v1.0.0-Alp
...
main
Author | SHA1 | Date | |
---|---|---|---|
bf17bde9f5 | |||
9f3e4eff4d | |||
f89de94aa7 | |||
684b3fb40e | |||
df692ea222 | |||
636963b3d4 | |||
4802493886 | |||
ecf930fcad | |||
ce3b1d32d7 | |||
c39789dfde | |||
ab0b676216 | |||
20fe60d9f6 | |||
529caeaa87 | |||
3d6c68e743 | |||
7cdb7247c8 | |||
d7d2702869 | |||
67b58117e1 | |||
66801a1c7a | |||
2410504ede | |||
2a3b962af5 | |||
8346fb5077 | |||
8dc2174081 | |||
05322aface | |||
dcbbe043f0 | |||
65d983a5e7 | |||
9aad2a5101 | |||
be3a2df6d9 | |||
dea8be3bfa | |||
cba2f25335 | |||
825217ef88 | |||
b3cb76e77d | |||
b0c2f38bd6 | |||
fca1542b5c | |||
07672d1189 | |||
62aa04a667 | |||
3aa0edfb34 | |||
e4d605531e | |||
a42de32260 | |||
95e60b2706 | |||
fd9eadf45e | |||
7ddeb88475 | |||
90a7543641 | |||
d1de7f5eb0 | |||
ef8045fa96 | |||
2031cb9e9f | |||
e260eaa535 | |||
891641fc6f | |||
c158097d3a | |||
6908f52e48 | |||
61d8f2c342 | |||
6bf7fdcb44 | |||
fbafbb62e3 | |||
9372d17313 | |||
9b1a5fa929 | |||
42bf002626 | |||
20f06387c5 | |||
75320da2be | |||
be2908517f | |||
c4f504dd0a | |||
0848586894 | |||
83c2882567 | |||
f069f9155c | |||
bf5ee4890e | |||
ed4214fd49 | |||
b8fe9e78e1 | |||
da2bf8d469 | |||
113b817627 | |||
bd18644c0d | |||
b32cf94e25 | |||
8cc2f3bdcd | |||
59b8213163 | |||
cb34c86665 | |||
70c1082531 | |||
30e9a57b96 | |||
bf76de4f23 | |||
47e2b851b9 | |||
f90e280b74 | |||
9600e2f11b | |||
b2fb04372f | |||
3b05a8b9c8 | |||
d689c7663f | |||
6238b5abb2 | |||
1f8c1d303e | |||
09c2080481 | |||
e36c27c0e0 | |||
6865f64880 | |||
3f9921a62e | |||
504fbaf13b | |||
fb70bcd443 | |||
952fa9c13a | |||
b47fcabc1c | |||
878d32ea16 | |||
5f7b07a601 | |||
7e8d005fcb | |||
21215300b3 | |||
b826ee3c8f | |||
32defa20f7 | |||
c5432883ef | |||
dae6084ebb | |||
c564750832 | |||
d16289d824 | |||
a2ac04f424 | |||
8247584a61 | |||
34e40aeee1 | |||
5d7ea48a46 | |||
d055b61ed9 | |||
87c59e63ee | |||
40b916a49f | |||
eabe2c4930 | |||
0f151a8f4f | |||
33a86607df | |||
f154e60e1e | |||
7e5a455c24 | |||
d8b0c89be7 | |||
3d385b5bdf | |||
4f3924f2cd | |||
c0796cfc38 | |||
9857a0565d | |||
a3dbe8f142 | |||
88f6d53ccb | |||
850beb8421 | |||
da50b72898 | |||
237802b18d | |||
88c64f1ff1 | |||
5e7d880499 | |||
199506547b |
419 changed files with 6410 additions and 2998 deletions
24
README.md
24
README.md
|
@ -4,35 +4,49 @@ fints4k is an implementation of the FinTS 3.0 online banking protocol used by mo
|
||||||
|
|
||||||
It's fast, easy extendable and running on multiple platforms: JVM, Android, (iOS, JavaScript, Windows, MacOS, Linux).
|
It's fast, easy extendable and running on multiple platforms: JVM, Android, (iOS, JavaScript, Windows, MacOS, Linux).
|
||||||
|
|
||||||
However it's not a full implementation of FinTS standard but implements all common use cases:
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Retrieving account information, balances and turnovers (Kontoumsätze und -saldo).
|
- Retrieving account information, balances and turnovers (Kontoumsätze und -saldo).
|
||||||
- Transfer money and real-time transfers (SEPA Überweisungen und Echtzeitüberweisung).
|
- Transfer money and real-time transfers (SEPA Überweisungen und Echtzeitüberweisung).
|
||||||
- Supports TAN methods chipTAN manual, Flickercode, QrCode and Photo (Matrix code), pushTAN, smsTAN and appTAN.
|
- Supports TAN methods chipTAN manual, Flickercode, QrCode and Photo (Matrix code), pushTAN, smsTAN and appTAN.
|
||||||
|
|
||||||
|
However, this is quite a low level implementation and in most cases not what you want to use.
|
||||||
|
In most cases you want to use a higher level abstraction like [FinTs4kBankingClient](https://git.dankito.net/codinux/BankingClient).
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
Not uploaded to Maven Central yet, will do this the next few days!
|
Not uploaded to Maven Central yet, will do this the next few days!
|
||||||
|
|
||||||
Gradle:
|
Gradle:
|
||||||
```
|
```
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
setUrl("https://maven.dankito.net/api/packages/codinux/maven")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'net.dankito.banking:fints4k:0.1.0'
|
implementation("net.codinux.banking:fints4k:1.0.0-Alpha-13")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Maven:
|
Maven:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
// add Repository https://maven.dankito.net/api/packages/codinux/maven
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.dankito.banking</groupId>
|
<groupId>net.dankito.banking</groupId>
|
||||||
<artifactId>fints4k</artifactId>
|
<artifactId>fints4k-jvm</artifactId>
|
||||||
<version>0.1.0</version>
|
<version>1.0.0-Alpha-11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
Quite outdated, have to update it. In most cases use [FinTs4kBankingClient](https://git.dankito.net/codinux/BankingClient).
|
||||||
|
|
||||||
See e.g. [JavaShowcase](fints4k/src/test/java/net/dankito/banking/fints/JavaShowcase.java) or [FinTsClientTest](fints4k/src/test/kotlin/net/dankito/banking/fints/FinTsClientTest.kt).
|
See e.g. [JavaShowcase](fints4k/src/test/java/net/dankito/banking/fints/JavaShowcase.java) or [FinTsClientTest](fints4k/src/test/kotlin/net/dankito/banking/fints/FinTsClientTest.kt).
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
|
@ -7,9 +7,9 @@ import kotlinx.coroutines.withContext
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.dankito.banking.fints.FinTsClient
|
import net.codinux.banking.fints.FinTsClient
|
||||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
import net.codinux.banking.fints.callback.SimpleFinTsClientCallback
|
||||||
import net.dankito.banking.fints.model.TanChallenge
|
import net.codinux.banking.fints.model.TanChallenge
|
||||||
import net.dankito.utils.multiplatform.extensions.millisSinceEpochAtSystemDefaultTimeZone
|
import net.dankito.utils.multiplatform.extensions.millisSinceEpochAtSystemDefaultTimeZone
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
|
@ -5,7 +5,7 @@ import net.codinux.banking.fints4k.android.Presenter
|
||||||
import net.codinux.banking.fints4k.android.R
|
import net.codinux.banking.fints4k.android.R
|
||||||
import net.codinux.banking.fints4k.android.adapter.viewholder.AccountTransactionsViewHolder
|
import net.codinux.banking.fints4k.android.adapter.viewholder.AccountTransactionsViewHolder
|
||||||
import net.dankito.banking.client.model.AccountTransaction
|
import net.dankito.banking.client.model.AccountTransaction
|
||||||
import net.dankito.banking.fints.util.toBigDecimal
|
import net.codinux.banking.fints.util.toBigDecimal
|
||||||
import net.dankito.utils.android.extensions.setTextColorToColorResource
|
import net.dankito.utils.android.extensions.setTextColorToColorResource
|
||||||
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
|
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
|
@ -15,9 +15,9 @@ import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import net.codinux.banking.fints4k.android.Presenter
|
import net.codinux.banking.fints4k.android.Presenter
|
||||||
import net.codinux.banking.fints4k.android.R
|
import net.codinux.banking.fints4k.android.R
|
||||||
import net.dankito.banking.fints.model.FlickerCodeTanChallenge
|
import net.codinux.banking.fints.model.FlickerCodeTanChallenge
|
||||||
import net.dankito.banking.fints.model.ImageTanChallenge
|
import net.codinux.banking.fints.model.ImageTanChallenge
|
||||||
import net.dankito.banking.fints.model.TanChallenge
|
import net.codinux.banking.fints.model.TanChallenge
|
||||||
import net.dankito.utils.android.extensions.getSpannedFromHtml
|
import net.dankito.utils.android.extensions.getSpannedFromHtml
|
||||||
import net.dankito.utils.android.extensions.show
|
import net.dankito.utils.android.extensions.show
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,12 @@ import net.dankito.banking.client.model.CustomerAccount
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
||||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import net.dankito.banking.fints.FinTsClient
|
import net.codinux.banking.fints.FinTsClient
|
||||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
import net.codinux.banking.fints.callback.SimpleFinTsClientCallback
|
||||||
import net.dankito.banking.fints.extensions.toStringWithMinDigits
|
import net.codinux.banking.fints.extensions.toStringWithMinDigits
|
||||||
import net.dankito.banking.fints.getAccountData
|
import net.codinux.banking.fints.getAccountData
|
||||||
import net.dankito.banking.fints.model.TanChallenge
|
import net.codinux.banking.fints.model.TanChallenge
|
||||||
import net.dankito.banking.fints.transferMoney
|
import net.codinux.banking.fints.transferMoney
|
||||||
import util.CsvWriter
|
import util.CsvWriter
|
||||||
import util.OutputFormat
|
import util.OutputFormat
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import NativeApp
|
import NativeApp
|
||||||
import net.dankito.banking.fints.model.TanMethodType
|
import net.codinux.banking.fints.model.TanMethodType
|
||||||
|
|
||||||
|
|
||||||
data class CommonConfig(
|
data class CommonConfig(
|
||||||
|
|
|
@ -5,10 +5,10 @@ import com.github.ajalt.clikt.core.requireObject
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import net.dankito.banking.fints.model.AccountData
|
import net.codinux.banking.fints.model.AccountData
|
||||||
import net.dankito.banking.fints.model.Amount
|
import net.codinux.banking.fints.model.Amount
|
||||||
import net.dankito.banking.fints.model.Currency
|
import net.codinux.banking.fints.model.Currency
|
||||||
import net.dankito.banking.fints.model.Money
|
import net.codinux.banking.fints.model.Money
|
||||||
|
|
||||||
|
|
||||||
class TransferMoneyCommand : CliktCommand("Transfers money from your account to a recipient", name = "transfer", printHelpOnEmptyArgs = true) {
|
class TransferMoneyCommand : CliktCommand("Transfers money from your account to a recipient", name = "transfer", printHelpOnEmptyArgs = true) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.minus
|
import kotlinx.datetime.minus
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
||||||
import net.dankito.banking.fints.model.TanMethodType
|
import net.codinux.banking.fints.model.TanMethodType
|
||||||
import net.dankito.banking.fints.extensions.todayAtEuropeBerlin
|
import net.codinux.banking.fints.extensions.todayAtEuropeBerlin
|
||||||
import util.OutputFormat
|
import util.OutputFormat
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
||||||
import net.dankito.banking.fints.model.Money
|
import net.codinux.banking.fints.model.Money
|
||||||
|
|
||||||
|
|
||||||
fun FinTsClient.getAccountData(bankCode: String, loginName: String, password: String): GetAccountDataResponse {
|
fun FinTsClient.getAccountData(bankCode: String, loginName: String, password: String): GetAccountDataResponse {
|
|
@ -41,8 +41,8 @@ open class CsvWriter {
|
||||||
protected open suspend fun writeToFile(stream: AsyncStream, valueSeparator: String, customer: CustomerAccount, account: BankAccount, transaction: AccountTransaction) {
|
protected open suspend fun writeToFile(stream: AsyncStream, valueSeparator: String, customer: CustomerAccount, account: BankAccount, transaction: AccountTransaction) {
|
||||||
val amount = if (valueSeparator == ";") transaction.amount.amount.string.replace('.', ',') else transaction.amount.amount.string.replace(',', '.')
|
val amount = if (valueSeparator == ";") transaction.amount.amount.string.replace('.', ',') else transaction.amount.amount.string.replace(',', '.')
|
||||||
|
|
||||||
stream.writeString(listOf(customer.bankName, account.identifier, transaction.valueDate, amount, transaction.amount.currency, ensureNotNull(transaction.bookingText), wrap(transaction.reference),
|
stream.writeString(listOf(customer.bankName, account.identifier, transaction.valueDate, amount, transaction.amount.currency, ensureNotNull(transaction.postingText), wrap(transaction.reference ?: ""),
|
||||||
ensureNotNull(transaction.otherPartyName), ensureNotNull(transaction.otherPartyBankCode), ensureNotNull(transaction.otherPartyAccountId)).joinToString(valueSeparator))
|
ensureNotNull(transaction.otherPartyName), ensureNotNull(transaction.otherPartyBankId), ensureNotNull(transaction.otherPartyAccountId)).joinToString(valueSeparator))
|
||||||
|
|
||||||
stream.writeString(NewLine)
|
stream.writeString(NewLine)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.dankito.banking.client.model.AccountTransaction
|
import net.dankito.banking.client.model.AccountTransaction
|
||||||
import net.dankito.banking.fints.model.TanChallenge
|
import net.codinux.banking.fints.model.TanChallenge
|
||||||
import react.*
|
import react.*
|
||||||
import react.dom.*
|
import react.dom.*
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
|
|
|
@ -2,8 +2,8 @@ import io.ktor.util.encodeBase64
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.js.onChangeFunction
|
import kotlinx.html.js.onChangeFunction
|
||||||
import kotlinx.html.style
|
import kotlinx.html.style
|
||||||
import net.dankito.banking.fints.model.ImageTanChallenge
|
import net.codinux.banking.fints.model.ImageTanChallenge
|
||||||
import net.dankito.banking.fints.model.TanChallenge
|
import net.codinux.banking.fints.model.TanChallenge
|
||||||
import org.w3c.dom.HTMLInputElement
|
import org.w3c.dom.HTMLInputElement
|
||||||
import react.Props
|
import react.Props
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
|
|
|
@ -6,11 +6,11 @@ import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
||||||
import net.dankito.banking.fints.FinTsClient
|
import net.codinux.banking.fints.FinTsClient
|
||||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
import net.codinux.banking.fints.callback.SimpleFinTsClientCallback
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.webclient.KtorWebClient
|
import net.codinux.banking.fints.webclient.KtorWebClient
|
||||||
import net.dankito.banking.fints.webclient.ProxyingWebClient
|
import net.codinux.banking.fints.webclient.ProxyingWebClient
|
||||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||||
|
|
||||||
open class Presenter {
|
open class Presenter {
|
||||||
|
|
16
build.gradle
16
build.gradle
|
@ -1,16 +1,16 @@
|
||||||
// TODO: move to versions.gradle
|
// TODO: move to versions.gradle
|
||||||
ext {
|
ext {
|
||||||
appVersionName = '1.0.0-Alpha-11'
|
appVersionName = "1.0.0-Alpha-16-SNAPSHOT"
|
||||||
|
|
||||||
|
|
||||||
/* Test */
|
/* Test */
|
||||||
|
|
||||||
assertJVersion = '3.12.2'
|
assertJVersion = "3.12.2"
|
||||||
|
|
||||||
mockitoVersion = '2.22.0'
|
mockitoVersion = "2.22.0"
|
||||||
mockitoKotlinVersion = '1.6.0'
|
mockitoKotlinVersion = "1.6.0"
|
||||||
|
|
||||||
logbackVersion = '1.2.3'
|
logbackVersion = "1.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -48,4 +48,10 @@ task publishAllToMavenLocal {
|
||||||
dependsOn = [
|
dependsOn = [
|
||||||
"fints4k:publishToMavenLocal",
|
"fints4k:publishToMavenLocal",
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
task publishAll {
|
||||||
|
dependsOn = [
|
||||||
|
"fints4k:publish",
|
||||||
|
]
|
||||||
}
|
}
|
26
docs/Vokabular.md
Normal file
26
docs/Vokabular.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
| | |
|
||||||
|
|--------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Geschäftsvorfall | Business Transaction / Job |
|
||||||
|
| Verwendungszweck | Remittance information, reference, (payment) purpose |
|
||||||
|
| Überweisung | Remittance (techn.), money transfer, bank transfer, wire transfer (Amerik.), credit transfer |
|
||||||
|
| Buchungsschlüssel | posting key |
|
||||||
|
| Buchungstext | posting text |
|
||||||
|
| | |
|
||||||
|
| Ende-zu-Ende Referenz | End to End Reference |
|
||||||
|
| Kundenreferenz | Reference of the submitting customer |
|
||||||
|
| Mandatsreferenz | mandate reference |
|
||||||
|
| Creditor Identifier | Creditor Identifier |
|
||||||
|
| Originators Identification Code | Originators Identification Code |
|
||||||
|
| Compensation Amount | Compensation Amount |
|
||||||
|
| Original Amount | Original Amount |
|
||||||
|
| Abweichender Überweisender (CT-AT08) / Abweichender Zahlungsempfänger (DD-AT38) | payer’s/debtor’s reference party (for credit transfer / payee’s / creditor’s reference party (for a direct debit) |
|
||||||
|
| Abweichender Zahlungsempfänger (CT-AT28) / Abweichender Zahlungspflichtiger (DDAT15) | payee’s/creditor’s reference party / payer’s/debtor’s reference party |
|
||||||
|
| | |
|
||||||
|
| Überweisender | Payer, debtor |
|
||||||
|
| Zahlungsempfänger | Payee, creditor |
|
||||||
|
| Zahlungseingang | Payment receipt |
|
||||||
|
| Lastschrift | direct debit |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
| Primanoten-Nr. | Journal no. |
|
||||||
|
| | |
|
|
@ -8,11 +8,15 @@ plugins {
|
||||||
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(8)
|
jvmToolchain(11)
|
||||||
|
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
// suppresses compiler warning: [EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING] 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta.
|
// suppresses compiler warning: [EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING] 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta.
|
||||||
freeCompilerArgs.add("-Xexpect-actual-classes")
|
freeCompilerArgs.add("-Xexpect-actual-classes")
|
||||||
|
|
||||||
|
if (System.getProperty("idea.debugger.dispatch.addr") != null) {
|
||||||
|
freeCompilerArgs.add("-Xdebug")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +38,7 @@ kotlin {
|
||||||
browser {
|
browser {
|
||||||
testTask {
|
testTask {
|
||||||
useKarma {
|
useKarma {
|
||||||
// useChromeHeadless()
|
useChromeHeadless()
|
||||||
useFirefoxHeadless()
|
useFirefoxHeadless()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,10 +47,13 @@ kotlin {
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wasmJs() // ktor is not available for wasmJs yet
|
||||||
|
|
||||||
|
|
||||||
linuxX64()
|
linuxX64()
|
||||||
mingwX64()
|
mingwX64()
|
||||||
|
|
||||||
|
iosX64()
|
||||||
iosArm64()
|
iosArm64()
|
||||||
iosSimulatorArm64()
|
iosSimulatorArm64()
|
||||||
macosX64()
|
macosX64()
|
||||||
|
@ -69,8 +76,9 @@ 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:kmp-log:$klfVersion")
|
implementation("net.codinux.log:klf:$klfVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import net.dankito.banking.client.model.parameter.FinTsClientParameter
|
import net.dankito.banking.client.model.parameter.FinTsClientParameter
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
|
@ -6,15 +6,21 @@ import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import net.dankito.banking.client.model.response.ErrorCode
|
import net.dankito.banking.client.model.response.ErrorCode
|
||||||
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
import net.dankito.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
import net.dankito.banking.client.model.response.TransferMoneyResponse
|
||||||
import net.dankito.banking.fints.callback.FinTsClientCallback
|
import net.codinux.banking.fints.callback.FinTsClientCallback
|
||||||
import net.dankito.banking.fints.config.FinTsClientConfiguration
|
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||||
import net.dankito.banking.fints.mapper.FinTsModelMapper
|
import net.codinux.banking.fints.mapper.FinTsModelMapper
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.KundensystemID
|
||||||
import net.dankito.banking.fints.response.client.FinTsClientResponse
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.response.client.GetAccountInfoResponse
|
import net.codinux.banking.fints.response.client.FinTsClientResponse
|
||||||
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
|
import net.codinux.banking.fints.response.client.GetAccountInfoResponse
|
||||||
import net.dankito.banking.fints.response.segments.AccountType
|
import net.codinux.banking.fints.response.client.GetAccountTransactionsResponse
|
||||||
import net.dankito.banking.fints.util.BicFinder
|
import net.codinux.banking.fints.response.segments.AccountType
|
||||||
|
import net.codinux.banking.fints.response.segments.BankParameters
|
||||||
|
import net.codinux.banking.fints.util.BicFinder
|
||||||
|
import net.codinux.log.LogLevel
|
||||||
|
import net.codinux.log.LoggerFactory
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
|
||||||
open class FinTsClient(
|
open class FinTsClient(
|
||||||
|
@ -35,56 +41,59 @@ open class FinTsClient(
|
||||||
protected open val bicFinder = BicFinder()
|
protected open val bicFinder = BicFinder()
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
LoggerFactory.getLogger("net.codinux.banking.fints.log.MessageLogCollector").level = if (config.options.appendFinTsMessagesToLog) {
|
||||||
|
LogLevel.Debug
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open suspend fun getAccountDataAsync(bankCode: String, loginName: String, password: String): GetAccountDataResponse {
|
open suspend fun getAccountDataAsync(bankCode: String, loginName: String, password: String): GetAccountDataResponse {
|
||||||
return getAccountDataAsync(GetAccountDataParameter(bankCode, loginName, password))
|
return getAccountDataAsync(GetAccountDataParameter(bankCode, loginName, password))
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun getAccountDataAsync(param: GetAccountDataParameter): GetAccountDataResponse {
|
open suspend fun getAccountDataAsync(param: GetAccountDataParameter): GetAccountDataResponse {
|
||||||
val finTsServerAddress = config.finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
|
val basicAccountDataResponse = getRequiredDataToSendUserJobs(param)
|
||||||
if (finTsServerAddress.isNullOrBlank()) {
|
val bank = basicAccountDataResponse.finTsModel
|
||||||
return GetAccountDataResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not support FinTS 3.0 or we don't know its FinTS server address", null, listOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
val bank = mapper.mapToBankData(param, finTsServerAddress)
|
if (basicAccountDataResponse.successful == false || param.retrieveOnlyAccountInfo || bank == null) {
|
||||||
val accounts = param.accounts
|
return GetAccountDataResponse(basicAccountDataResponse.error, basicAccountDataResponse.errorMessage, null,
|
||||||
|
basicAccountDataResponse.messageLog, bank)
|
||||||
if (accounts.isNullOrEmpty() || param.retrieveOnlyAccountInfo) { // then first retrieve customer's bank accounts
|
|
||||||
val getAccountInfoResponse = getAccountInfo(param, bank)
|
|
||||||
|
|
||||||
if (getAccountInfoResponse.successful == false || param.retrieveOnlyAccountInfo) {
|
|
||||||
return GetAccountDataResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse), null,
|
|
||||||
getAccountInfoResponse.messageLog, bank)
|
|
||||||
} else {
|
|
||||||
return getAccountData(param, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return getAccountData(param, bank, accounts.map { mapper.mapToAccountData(it, param) }, null)
|
return getAccountData(param, bank, bank.accounts, basicAccountDataResponse.messageLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun getAccountData(param: GetAccountDataParameter, bank: BankData, accounts: List<AccountData>, previousJobResponse: FinTsClientResponse?): GetAccountDataResponse {
|
protected open suspend fun getAccountData(param: GetAccountDataParameter, bank: BankData, accounts: List<AccountData>, previousJobMessageLog: List<MessageLogEntry>?): GetAccountDataResponse {
|
||||||
val retrievedTransactionsResponses = mutableListOf<GetAccountTransactionsResponse>()
|
val retrievedTransactionsResponses = mutableListOf<GetAccountTransactionsResponse>()
|
||||||
|
|
||||||
val accountsSupportingRetrievingTransactions = accounts.filter { it.supportsRetrievingBalance || it.supportsRetrievingAccountTransactions }
|
val accountsSupportingRetrievingTransactions = accounts.filter { it.supportsRetrievingBalance || it.supportsRetrievingAccountTransactions }
|
||||||
|
|
||||||
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), previousJobResponse?.messageLog ?: listOf(), bank)
|
return GetAccountDataResponse(ErrorCode.NoneOfTheAccountsSupportsRetrievingData, errorMessage, mapper.map(bank), previousJobMessageLog ?: listOf(), bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountsSupportingRetrievingTransactions.forEach { account ->
|
for (account in accountsSupportingRetrievingTransactions) {
|
||||||
retrievedTransactionsResponses.add(getAccountData(param, bank, account))
|
val response = getAccountTransactions(param, bank, account)
|
||||||
|
retrievedTransactionsResponses.add(response)
|
||||||
|
|
||||||
|
if (response.tanRequiredButWeWereToldToAbortIfSo || response.userCancelledAction) { // if user cancelled action or TAN is required but we were told to abort then, then don't continue with next account
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val unsuccessfulJob = retrievedTransactionsResponses.firstOrNull { it.successful == false }
|
val unsuccessfulJob = retrievedTransactionsResponses.firstOrNull { it.successful == false }
|
||||||
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),
|
return GetAccountDataResponse(errorCode, mapper.mapErrorMessages(unsuccessfulJob), mapper.map(bank, retrievedTransactionsResponses, param.retrieveTransactionsTo),
|
||||||
mapper.mergeMessageLog(previousJobResponse, *retrievedTransactionsResponses.toTypedArray()), bank)
|
mapper.mergeMessageLog(previousJobMessageLog, *retrievedTransactionsResponses.map { it.messageLog }.toTypedArray()), bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun getAccountData(param: GetAccountDataParameter, bank: BankData, account: AccountData): GetAccountTransactionsResponse {
|
protected open suspend fun getAccountTransactions(param: GetAccountDataParameter, bank: BankData, account: AccountData): GetAccountTransactionsResponse {
|
||||||
val context = JobContext(JobContextType.GetTransactions, this.callback, config, bank, account)
|
val context = JobContext(JobContextType.GetTransactions, this.callback, config, bank, account, param.preferredTanMethods, param.tanMethodsNotSupportedByApplication, param.preferredTanMedium)
|
||||||
|
|
||||||
return config.jobExecutor.getTransactionsAsync(context, mapper.toGetAccountTransactionsParameter(param, bank, account))
|
return config.jobExecutor.getTransactionsAsync(context, mapper.toGetAccountTransactionsParameter(param, bank, account))
|
||||||
}
|
}
|
||||||
|
@ -142,7 +151,7 @@ open class FinTsClient(
|
||||||
accountToUse = selectedAccount
|
accountToUse = selectedAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
val context = JobContext(JobContextType.TransferMoney, this.callback, config, bank, accountToUse)
|
val context = JobContext(JobContextType.TransferMoney, this.callback, config, bank, accountToUse, param.preferredTanMethods, param.tanMethodsNotSupportedByApplication, param.preferredTanMedium)
|
||||||
|
|
||||||
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))
|
||||||
|
@ -163,25 +172,65 @@ open class FinTsClient(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures all basic data to initialize a dialog with strong customer authorization is retrieved so you can send your
|
||||||
|
* actual jobs (Geschäftsvorfälle) to your bank's FinTS server.
|
||||||
|
*
|
||||||
|
* These data include:
|
||||||
|
* - Bank communication data like FinTS server address, BIC, bank name, bank code used for FinTS.
|
||||||
|
* - BPD (BankParameterDaten): bank name, BPD version, supported languages, supported HBCI versions, supported TAN methods,
|
||||||
|
* max count jobs per message (Anzahl Geschäftsvorfallsarten) (see [BankParameters] [BankParameters](src/commonMain/kotlin/net/codinux/banking/fints/response/segmentsBankParameters) ).
|
||||||
|
* - Min and max online banking password length, min TAN length, hint for login name (for all: if available)
|
||||||
|
* - UPD (UserParameterDaten): username, UPD version.
|
||||||
|
* - Customer system ID (Kundensystem-ID, see [KundensystemID]), TAN methods available for user and may user's TAN media.
|
||||||
|
* - Which jobs the bank supports and which jobs need strong customer authorization (= require HKTAN segment).
|
||||||
|
* - Which jobs the user is allowed to use.
|
||||||
|
* - Which jobs can be called for a specific bank account.
|
||||||
|
*
|
||||||
|
* When implementing your own jobs, call this method first, then send an init dialog message and in next message your actual jobs.
|
||||||
|
*
|
||||||
|
* More or less implements everything of 02 FinTS_3.0_Formals.pdf so that you can start directly with the jobs from
|
||||||
|
* 04 FinTS_3.0_Messages_Geschaeftsvorfaelle.pdf
|
||||||
|
*/
|
||||||
|
open suspend fun getRequiredDataToSendUserJobs(param: FinTsClientParameter): net.dankito.banking.client.model.response.FinTsClientResponse {
|
||||||
|
param.finTsModelOrDeserialized?.let { finTsModel ->
|
||||||
|
return net.dankito.banking.client.model.response.FinTsClientResponse(null, null, emptyList(), finTsModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultValues = (param as? GetAccountDataParameter)?.defaultBankValues
|
||||||
|
|
||||||
|
val finTsServerAddress = defaultValues?.finTs3ServerAddress ?: config.finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
|
||||||
|
if (finTsServerAddress.isNullOrBlank()) {
|
||||||
|
return net.dankito.banking.client.model.response.FinTsClientResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not support FinTS 3.0 or we don't know its FinTS server address", emptyList(), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bank = mapper.mapToBankData(param, finTsServerAddress, defaultValues)
|
||||||
|
|
||||||
|
val getAccountInfoResponse = getAccountInfo(param, bank)
|
||||||
|
|
||||||
|
return net.dankito.banking.client.model.response.FinTsClientResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
|
||||||
|
getAccountInfoResponse.messageLog, bank)
|
||||||
|
}
|
||||||
|
|
||||||
protected open suspend fun getAccountInfo(param: FinTsClientParameter, bank: BankData): GetAccountInfoResponse {
|
protected open suspend fun getAccountInfo(param: FinTsClientParameter, bank: BankData): GetAccountInfoResponse {
|
||||||
param.finTsModel?.let {
|
param.finTsModelOrDeserialized?.let {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
// return GetAccountInfoResponse(it)
|
// return GetAccountInfoResponse(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val context = JobContext(JobContextType.GetAccountInfo, this.callback, config, bank)
|
val context = JobContext(JobContextType.GetAccountInfo, this.callback, config, bank, null, param.preferredTanMethods, param.tanMethodsNotSupportedByApplication, param.preferredTanMedium)
|
||||||
|
|
||||||
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
|
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
|
||||||
|
|
||||||
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, param.preferredTanMethods, param.preferredTanMedium)
|
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context)
|
||||||
|
|
||||||
|
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
||||||
|
strong customer authorization TAN media is required */
|
||||||
|
|
||||||
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
||||||
return GetAccountInfoResponse(context, newUserInfoResponse)
|
return GetAccountInfoResponse(context, newUserInfoResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
|
||||||
strong customer authorization TAN media is required */
|
|
||||||
|
|
||||||
val getAccountsResponse = config.jobExecutor.getAccounts(context)
|
val getAccountsResponse = config.jobExecutor.getAccounts(context)
|
||||||
|
|
||||||
return GetAccountInfoResponse(context, getAccountsResponse)
|
return GetAccountInfoResponse(context, getAccountsResponse)
|
|
@ -1,17 +1,14 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.*
|
||||||
import net.dankito.banking.fints.callback.FinTsClientCallback
|
import net.codinux.banking.fints.callback.FinTsClientCallback
|
||||||
import net.dankito.banking.fints.config.FinTsClientConfiguration
|
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||||
import net.dankito.banking.fints.extensions.minusDays
|
import net.codinux.banking.fints.extensions.minusDays
|
||||||
import net.dankito.banking.fints.extensions.todayAtEuropeBerlin
|
import net.codinux.banking.fints.extensions.todayAtEuropeBerlin
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.*
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.*
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.response.BankResponse
|
import net.codinux.banking.fints.response.BankResponse
|
||||||
import net.dankito.banking.fints.response.client.*
|
import net.codinux.banking.fints.response.client.*
|
||||||
import net.dankito.banking.fints.webclient.IWebClient
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,19 +23,6 @@ open class FinTsClientDeprecated(
|
||||||
constructor(callback: FinTsClientCallback) : this(FinTsClientConfiguration(), callback)
|
constructor(callback: FinTsClientCallback) : this(FinTsClientConfiguration(), callback)
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves information about bank (e.g. supported HBCI versions, FinTS server address,
|
|
||||||
* supported jobs, ...).
|
|
||||||
*
|
|
||||||
* On success [bank] parameter is updated afterwards.
|
|
||||||
*/
|
|
||||||
open fun getAnonymousBankInfoAsync(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
|
||||||
|
|
||||||
GlobalScope.launch {
|
|
||||||
callback(getAnonymousBankInfo(bank))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves information about bank (e.g. supported HBCI versions, FinTS server address,
|
* Retrieves information about bank (e.g. supported HBCI versions, FinTS server address,
|
||||||
* supported jobs, ...).
|
* supported jobs, ...).
|
||||||
|
@ -53,13 +37,13 @@ open class FinTsClientDeprecated(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open suspend fun addAccountAsync(parameter: AddAccountParameter): AddAccountResponse {
|
open suspend fun addAccountAsync(param: AddAccountParameter): AddAccountResponse {
|
||||||
val bank = parameter.bank
|
val bank = param.bank
|
||||||
val context = JobContext(JobContextType.AddAccount, this.callback, config, bank)
|
val context = JobContext(JobContextType.AddAccount, this.callback, config, bank, null, param.preferredTanMethods, param.tanMethodsNotSupportedByApplication, param.preferredTanMedium)
|
||||||
|
|
||||||
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
|
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
|
||||||
|
|
||||||
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium)
|
val newUserInfoResponse = config.jobExecutor.retrieveBasicDataLikeUsersTanMethods(context)
|
||||||
|
|
||||||
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
||||||
return AddAccountResponse(context, newUserInfoResponse)
|
return AddAccountResponse(context, newUserInfoResponse)
|
||||||
|
@ -68,7 +52,7 @@ open class FinTsClientDeprecated(
|
||||||
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
||||||
strong customer authorization TAN media is required */
|
strong customer authorization TAN media is required */
|
||||||
|
|
||||||
return addAccountGetAccountsAndTransactions(context, parameter)
|
return addAccountGetAccountsAndTransactions(context, param)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun addAccountGetAccountsAndTransactions(context: JobContext, parameter: AddAccountParameter): AddAccountResponse {
|
protected open suspend fun addAccountGetAccountsAndTransactions(context: JobContext, parameter: AddAccountParameter): AddAccountResponse {
|
||||||
|
@ -134,11 +118,11 @@ open class FinTsClientDeprecated(
|
||||||
return GetAccountTransactionsParameter(bank, account, account.supportsRetrievingBalance, ninetyDaysAgo, abortIfTanIsRequired = true)
|
return GetAccountTransactionsParameter(bank, account, account.supportsRetrievingBalance, ninetyDaysAgo, abortIfTanIsRequired = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
|
open suspend fun getAccountTransactionsAsync(param: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
|
||||||
|
|
||||||
val context = JobContext(JobContextType.GetTransactions, this.callback, config, parameter.bank, parameter.account)
|
val context = JobContext(JobContextType.GetTransactions, this.callback, config, param.bank, param.account)
|
||||||
|
|
||||||
return config.jobExecutor.getTransactionsAsync(context, parameter)
|
return config.jobExecutor.getTransactionsAsync(context, param)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +135,7 @@ open class FinTsClientDeprecated(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open suspend fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData): FinTsClientResponse {
|
open suspend fun changeTanMedium(newActiveTanMedium: TanMedium, bank: BankData): FinTsClientResponse {
|
||||||
val context = JobContext(JobContextType.ChangeTanMedium, this.callback, config, bank)
|
val context = JobContext(JobContextType.ChangeTanMedium, this.callback, config, bank)
|
||||||
|
|
||||||
val response = config.jobExecutor.changeTanMedium(context, newActiveTanMedium)
|
val response = config.jobExecutor.changeTanMedium(context, newActiveTanMedium)
|
|
@ -1,11 +1,11 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import net.dankito.banking.fints.callback.FinTsClientCallback
|
import net.codinux.banking.fints.callback.FinTsClientCallback
|
||||||
import net.dankito.banking.fints.config.FinTsClientConfiguration
|
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.response.client.AddAccountResponse
|
import net.codinux.banking.fints.response.client.AddAccountResponse
|
||||||
import net.dankito.banking.fints.response.client.FinTsClientResponse
|
import net.codinux.banking.fints.response.client.FinTsClientResponse
|
||||||
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
|
import net.codinux.banking.fints.response.client.GetAccountTransactionsResponse
|
||||||
|
|
||||||
|
|
||||||
open class FinTsClientForCustomer(
|
open class FinTsClientForCustomer(
|
|
@ -1,26 +1,28 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.fints.extensions.*
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import net.dankito.banking.fints.messages.MessageBuilder
|
import net.codinux.banking.fints.messages.MessageBuilder
|
||||||
import net.dankito.banking.fints.messages.MessageBuilderResult
|
import net.codinux.banking.fints.messages.MessageBuilderResult
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.*
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.*
|
||||||
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
import net.codinux.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||||
import net.dankito.banking.fints.messages.segmente.id.ISegmentId
|
import net.codinux.banking.fints.messages.segmente.id.ISegmentId
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.model.mapper.ModelMapper
|
import net.codinux.banking.fints.model.mapper.ModelMapper
|
||||||
import net.dankito.banking.fints.response.BankResponse
|
import net.codinux.banking.fints.response.BankResponse
|
||||||
import net.dankito.banking.fints.response.InstituteSegmentId
|
import net.codinux.banking.fints.response.InstituteSegmentId
|
||||||
import net.dankito.banking.fints.response.client.*
|
import net.codinux.banking.fints.response.client.*
|
||||||
import net.dankito.banking.fints.response.segments.*
|
import net.codinux.banking.fints.response.segments.*
|
||||||
import net.dankito.banking.fints.tan.FlickerCodeDecoder
|
import net.codinux.banking.fints.tan.FlickerCodeDecoder
|
||||||
import net.dankito.banking.fints.tan.TanImageDecoder
|
import net.codinux.banking.fints.tan.TanImageDecoder
|
||||||
import net.dankito.banking.fints.util.TanMethodSelector
|
import net.codinux.banking.fints.util.TanMethodSelector
|
||||||
import net.dankito.banking.fints.extensions.minusDays
|
import net.codinux.log.Log
|
||||||
import net.dankito.banking.fints.extensions.todayAtEuropeBerlin
|
import kotlin.math.max
|
||||||
import net.dankito.banking.fints.extensions.todayAtSystemDefaultTimeZone
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,8 +74,7 @@ open class FinTsJobExecutor(
|
||||||
*
|
*
|
||||||
* Be aware this method resets BPD, UPD and selected TAN method!
|
* Be aware this method resets BPD, UPD and selected TAN method!
|
||||||
*/
|
*/
|
||||||
open suspend fun retrieveBasicDataLikeUsersTanMethods(context: JobContext, preferredTanMethods: List<TanMethodType>? = null, preferredTanMedium: String? = null,
|
open suspend fun retrieveBasicDataLikeUsersTanMethods(context: JobContext): BankResponse {
|
||||||
closeDialog: Boolean = false): BankResponse {
|
|
||||||
val bank = context.bank
|
val bank = context.bank
|
||||||
|
|
||||||
// just to ensure settings are in its initial state and that bank sends us bank parameter (BPD),
|
// just to ensure settings are in its initial state and that bank sends us bank parameter (BPD),
|
||||||
|
@ -89,7 +90,7 @@ open class FinTsJobExecutor(
|
||||||
bank.resetSelectedTanMethod()
|
bank.resetSelectedTanMethod()
|
||||||
|
|
||||||
// this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN methods
|
// this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN methods
|
||||||
context.startNewDialog(closeDialog, versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1)
|
context.startNewDialog(versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1)
|
||||||
|
|
||||||
val message = messageBuilder.createInitDialogMessage(context)
|
val message = messageBuilder.createInitDialogMessage(context)
|
||||||
|
|
||||||
|
@ -102,12 +103,10 @@ open class FinTsJobExecutor(
|
||||||
if (bank.tanMethodsAvailableForUser.isEmpty()) { // could not retrieve supported tan methods for user
|
if (bank.tanMethodsAvailableForUser.isEmpty()) { // could not retrieve supported tan methods for user
|
||||||
return getTanMethodsResponse
|
return getTanMethodsResponse
|
||||||
} else {
|
} else {
|
||||||
getUsersTanMethod(context, preferredTanMethods)
|
getUsersTanMethod(context)
|
||||||
|
|
||||||
if (bank.isTanMethodSelected == false) {
|
if (bank.isTanMethodSelected && bank.tanMedia.isEmpty() && bank.tanMethodsAvailableForUser.any { it.nameOfTanMediumRequired } && isJobSupported(bank, CustomerSegmentId.TanMediaList)) { // tan media not retrieved yet
|
||||||
return getTanMethodsResponse
|
getTanMediaList(context, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, context.preferredTanMedium)
|
||||||
} else if (bank.tanMedia.isEmpty() && isJobSupported(bank, CustomerSegmentId.TanMediaList)) { // tan media not retrieved yet
|
|
||||||
getTanMediaList(context, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, preferredTanMedium)
|
|
||||||
|
|
||||||
return getTanMethodsResponse // TODO: judge if bank requires selecting TAN media and if though evaluate getTanMediaListResponse
|
return getTanMethodsResponse // TODO: judge if bank requires selecting TAN media and if though evaluate getTanMediaListResponse
|
||||||
} else {
|
} else {
|
||||||
|
@ -146,6 +145,7 @@ open class FinTsJobExecutor(
|
||||||
return BankResponse(true, internalError = "Die TAN Verfahren der Bank konnten nicht ermittelt werden") // TODO: translate
|
return BankResponse(true, internalError = "Die TAN Verfahren der Bank konnten nicht ermittelt werden") // TODO: translate
|
||||||
} else {
|
} else {
|
||||||
bank.tanMethodsAvailableForUser = bank.tanMethodsSupportedByBank
|
bank.tanMethodsAvailableForUser = bank.tanMethodsSupportedByBank
|
||||||
|
.filterNot { context.tanMethodsNotSupportedByApplication.contains(it.type) }
|
||||||
|
|
||||||
val didSelectTanMethod = getUsersTanMethod(context)
|
val didSelectTanMethod = getUsersTanMethod(context)
|
||||||
|
|
||||||
|
@ -202,6 +202,29 @@ open class FinTsJobExecutor(
|
||||||
var balance: Money? = balanceResponse?.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
|
var balance: Money? = balanceResponse?.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
|
||||||
Money(it.balance, it.currency)
|
Money(it.balance, it.currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: for larger portfolios there can be a Aufsetzpunkt, but for balances we currently do not support sending multiple messages
|
||||||
|
val statementOfHoldings = balanceResponse?.getFirstSegmentById<SecuritiesAccountBalanceSegment>(InstituteSegmentId.SecuritiesAccountBalance)?.let {
|
||||||
|
val statementOfHoldings = it.statementOfHoldings
|
||||||
|
val statementOfHolding = statementOfHoldings.firstOrNull { it.totalBalance != null }
|
||||||
|
if (statementOfHolding != null) {
|
||||||
|
balance = Money(statementOfHolding.totalBalance!!, statementOfHolding.currency ?: Currency.DefaultCurrencyCode)
|
||||||
|
}
|
||||||
|
statementOfHoldings
|
||||||
|
} ?: emptyList()
|
||||||
|
|
||||||
|
if (parameter.account.supportsRetrievingAccountTransactions == false) {
|
||||||
|
if (balanceResponse == null) {
|
||||||
|
return GetAccountTransactionsResponse(context, BankResponse(false, "Balance could not be retrieved"), RetrievedAccountData.unsuccessful(parameter.account))
|
||||||
|
} else {
|
||||||
|
val successful = balance != null || balanceResponse.tanRequiredButWeWereToldToAbortIfSo
|
||||||
|
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, emptyList(), emptyList(), statementOfHoldings, Instant.nowExt(), null, null, balanceResponse?.internalError)
|
||||||
|
|
||||||
|
return GetAccountTransactionsResponse(context, balanceResponse, retrievedData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val bookedTransactions = mutableSetOf<AccountTransaction>()
|
val bookedTransactions = mutableSetOf<AccountTransaction>()
|
||||||
val unbookedTransactions = mutableSetOf<Any>()
|
val unbookedTransactions = mutableSetOf<Any>()
|
||||||
|
|
||||||
|
@ -217,27 +240,30 @@ open class FinTsJobExecutor(
|
||||||
context.bank, parameter.account)
|
context.bank, parameter.account)
|
||||||
|
|
||||||
bookedTransactions.addAll(chunkTransaction)
|
bookedTransactions.addAll(chunkTransaction)
|
||||||
remainingMt940String = remainder
|
remainingMt940String = remainder ?: ""
|
||||||
|
|
||||||
parameter.retrievedChunkListener?.invoke(bookedTransactions)
|
parameter.retrievedChunkListener?.invoke(bookedTransactions)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { creditCardTransactionsSegment ->
|
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { creditCardTransactionsSegment ->
|
||||||
balance = Money(creditCardTransactionsSegment.balance.amount, creditCardTransactionsSegment.balance.currency ?: "EUR")
|
balance = Money(creditCardTransactionsSegment.balance.amount, creditCardTransactionsSegment.balance.currency ?: "EUR")
|
||||||
bookedTransactions.addAll(creditCardTransactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, it.description, it.bookingDate, it.transactionDescriptionBase ?: "", null, null, "", it.valueDate) })
|
bookedTransactions.addAll(creditCardTransactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, it.description, it.bookingDate, it.valueDate, it.transactionDescriptionBase ?: "", null, null) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val startTime = Instant.nowExt()
|
||||||
|
|
||||||
val response = getAndHandleResponseForMessage(context, message)
|
val response = getAndHandleResponseForMessage(context, message)
|
||||||
|
|
||||||
closeDialog(context)
|
closeDialog(context)
|
||||||
|
|
||||||
val successful = response.tanRequiredButWeWereToldToAbortIfSo
|
val successful = response.tanRequiredButWeWereToldToAbortIfSo
|
||||||
|| (response.successful && (parameter.alsoRetrieveBalance == false || balance != null))
|
|| (response.successful && (parameter.alsoRetrieveBalance == false || balance != null))
|
||||||
|
|| (parameter.account.supportsRetrievingAccountTransactions == false && balance != null)
|
||||||
val fromDate = parameter.fromDate
|
val fromDate = parameter.fromDate
|
||||||
?: parameter.account.countDaysForWhichTransactionsAreKept?.let { LocalDate.todayAtSystemDefaultTimeZone().minusDays(it) }
|
?: parameter.account.serverTransactionsRetentionDays?.let { LocalDate.todayAtSystemDefaultTimeZone().minusDays(it) }
|
||||||
?: bookedTransactions.minByOrNull { it.valueDate }?.valueDate
|
?: bookedTransactions.minByOrNull { it.valueDate }?.valueDate
|
||||||
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: LocalDate.todayAtEuropeBerlin(), response.internalError)
|
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, statementOfHoldings, startTime, fromDate, parameter.toDate ?: LocalDate.todayAtEuropeBerlin(), response.internalError)
|
||||||
|
|
||||||
return GetAccountTransactionsResponse(context, response, retrievedData,
|
return GetAccountTransactionsResponse(context, response, retrievedData,
|
||||||
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null)
|
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null)
|
||||||
|
@ -307,6 +333,7 @@ open class FinTsJobExecutor(
|
||||||
|
|
||||||
bank.selectedTanMedium = preferredTanMedium?.let { bank.tanMedia.firstOrNull { it.mediumName == preferredTanMedium } }
|
bank.selectedTanMedium = preferredTanMedium?.let { bank.tanMedia.firstOrNull { it.mediumName == preferredTanMedium } }
|
||||||
?: bank.selectedTanMedium?.let { selected -> bank.tanMedia.firstOrNull { it.mediumName == selected.mediumName } } // try to find selectedTanMedium in new TanMedia instances
|
?: bank.selectedTanMedium?.let { selected -> bank.tanMedia.firstOrNull { it.mediumName == selected.mediumName } } // try to find selectedTanMedium in new TanMedia instances
|
||||||
|
?: bank.tanMedia.firstOrNull { it.status == TanMediumStatus.Aktiv && it.mediumName != null }
|
||||||
?: bank.tanMedia.firstOrNull { it.mediumName != null }
|
?: bank.tanMedia.firstOrNull { it.mediumName != null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +341,7 @@ open class FinTsJobExecutor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open suspend fun changeTanMedium(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium): BankResponse {
|
open suspend fun changeTanMedium(context: JobContext, newActiveTanMedium: TanMedium): BankResponse {
|
||||||
val bank = context.bank
|
val bank = context.bank
|
||||||
|
|
||||||
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
|
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
|
||||||
|
@ -331,7 +358,7 @@ open class FinTsJobExecutor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun sendChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?): BankResponse {
|
protected open suspend fun sendChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanMedium, enteredAtc: EnterTanGeneratorAtcResult?): BankResponse {
|
||||||
|
|
||||||
return sendMessageInNewDialogAndHandleResponse(context, null, true) {
|
return sendMessageInNewDialogAndHandleResponse(context, null, true) {
|
||||||
messageBuilder.createChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc?.tan, enteredAtc?.atc)
|
messageBuilder.createChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc?.tan, enteredAtc?.atc)
|
||||||
|
@ -374,20 +401,36 @@ open class FinTsJobExecutor(
|
||||||
protected open suspend fun handleEnteringTanRequired(context: JobContext, tanResponse: TanResponse, response: BankResponse): BankResponse {
|
protected open suspend fun handleEnteringTanRequired(context: JobContext, tanResponse: TanResponse, response: BankResponse): BankResponse {
|
||||||
// on all platforms run on Dispatchers.Main, but on iOS skip this (or wrap in withContext(Dispatchers.IO) )
|
// on all platforms run on Dispatchers.Main, but on iOS skip this (or wrap in withContext(Dispatchers.IO) )
|
||||||
// val enteredTanResult = GlobalScope.async {
|
// val enteredTanResult = GlobalScope.async {
|
||||||
val tanChallenge = createTanChallenge(tanResponse, modelMapper.mapToActionRequiringTan(context.type), context.bank, context.account)
|
val tanChallenge = createTanChallenge(tanResponse, modelMapper.mapToActionRequiringTan(context.type), context.bank, context.account)
|
||||||
|
|
||||||
context.callback.enterTan(tanChallenge)
|
context.callback.enterTan(tanChallenge)
|
||||||
|
|
||||||
while (tanChallenge.enterTanResult == null) {
|
mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse)
|
||||||
delay(250)
|
|
||||||
|
|
||||||
mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse)
|
var invocationCount = 0 // TODO: remove again
|
||||||
|
|
||||||
// TODO: add a timeout of e.g. 30 min
|
while (tanChallenge.isEnteringTanDone == false) {
|
||||||
|
delay(500)
|
||||||
|
|
||||||
|
if (++invocationCount % 10 == 0) {
|
||||||
|
Log.info { "Waiting for TAN input invocation count: $invocationCount" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val now = Instant.nowExt()
|
||||||
|
if ((tanChallenge.tanExpirationTime != null && now > tanChallenge.tanExpirationTime) ||
|
||||||
|
// most TANs a valid 5 - 15 minutes. So terminate wait process after that time
|
||||||
|
(tanChallenge.tanExpirationTime == null && now > tanChallenge.challengeCreationTimestamp.plusMinutes(15))) {
|
||||||
|
if (tanChallenge.isEnteringTanDone == false) {
|
||||||
|
Log.info { "Terminating waiting for TAN input" } // TODO: remove again
|
||||||
|
|
||||||
|
tanChallenge.tanExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val enteredTanResult = tanChallenge.enterTanResult!!
|
val enteredTanResult = tanChallenge.enterTanResult!!
|
||||||
// }
|
|
||||||
|
|
||||||
return handleEnterTanResult(context, enteredTanResult, tanResponse, response)
|
return handleEnterTanResult(context, enteredTanResult, tanResponse, response)
|
||||||
}
|
}
|
||||||
|
@ -401,27 +444,82 @@ open class FinTsJobExecutor(
|
||||||
return when (tanMethod.type) {
|
return when (tanMethod.type) {
|
||||||
TanMethodType.ChipTanFlickercode ->
|
TanMethodType.ChipTanFlickercode ->
|
||||||
FlickerCodeTanChallenge(
|
FlickerCodeTanChallenge(
|
||||||
FlickerCodeDecoder().decodeChallenge(challenge, tanMethod.hhdVersion ?: HHDVersion.HHD_1_4), // HHD 1.4 is currently the most used version
|
FlickerCodeDecoder().decodeChallenge(challenge, tanMethod.hhdVersion ?: getFallbackHhdVersion(challenge)),
|
||||||
forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account)
|
forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account, tanResponse.tanExpirationTime)
|
||||||
|
|
||||||
TanMethodType.ChipTanQrCode, TanMethodType.ChipTanPhotoTanMatrixCode,
|
TanMethodType.ChipTanQrCode, TanMethodType.ChipTanPhotoTanMatrixCode,
|
||||||
TanMethodType.QrCode, TanMethodType.photoTan ->
|
TanMethodType.QrCode, TanMethodType.photoTan ->
|
||||||
ImageTanChallenge(TanImageDecoder().decodeChallenge(challenge), forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account)
|
ImageTanChallenge(TanImageDecoder().decodeChallenge(challenge), forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account, tanResponse.tanExpirationTime)
|
||||||
|
|
||||||
else -> TanChallenge(forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account)
|
else -> TanChallenge(forAction, messageToShowToUser, challenge, tanMethod, tanResponse.tanMediaIdentifier, bank, account, tanResponse.tanExpirationTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, tanResponse: TanResponse) {
|
protected open fun getFallbackHhdVersion(challenge: String): HHDVersion {
|
||||||
|
if (challenge.length <= 35) { // is this true in all circumstances?
|
||||||
|
return HHDVersion.HHD_1_3
|
||||||
|
}
|
||||||
|
|
||||||
|
return HHDVersion.HHD_1_4 // HHD 1.4 is currently the most used version
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open suspend fun mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, tanResponse: TanResponse) {
|
||||||
context.bank.selectedTanMethod.decoupledParameters?.let { decoupledTanMethodParameters ->
|
context.bank.selectedTanMethod.decoupledParameters?.let { decoupledTanMethodParameters ->
|
||||||
if (tanResponse.tanProcess == TanProcess.AppTan && decoupledTanMethodParameters.periodicStateRequestsAllowed) {
|
if (decoupledTanMethodParameters.periodicStateRequestsAllowed) {
|
||||||
automaticallyRetrieveIfUserEnteredDecoupledTan(context, tanChallenge)
|
val responseAfterApprovingDecoupledTan =
|
||||||
|
automaticallyRetrieveIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse, decoupledTanMethodParameters)
|
||||||
|
|
||||||
|
if (responseAfterApprovingDecoupledTan != null) {
|
||||||
|
tanChallenge.userApprovedDecoupledTan(responseAfterApprovingDecoupledTan)
|
||||||
|
} else {
|
||||||
|
tanChallenge.userDidNotEnterTan()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun automaticallyRetrieveIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge) {
|
protected open suspend fun automaticallyRetrieveIfUserEnteredDecoupledTan(context: JobContext, tanChallenge: TanChallenge, tanResponse: TanResponse, parameters: DecoupledTanMethodParameters): BankResponse? {
|
||||||
log.info { "automaticallyRetrieveIfUserEnteredDecoupledTan() called for $tanChallenge" }
|
log.info { "automaticallyRetrieveIfUserEnteredDecoupledTan() called for $tanChallenge" }
|
||||||
|
|
||||||
|
delay(max(5, parameters.initialDelayInSecondsForStateRequest).seconds)
|
||||||
|
|
||||||
|
var iteration = 0
|
||||||
|
val minWaitTime = when {
|
||||||
|
parameters.maxNumberOfStateRequests <= 10 -> 30
|
||||||
|
parameters.maxNumberOfStateRequests <= 24 -> 10
|
||||||
|
else -> 3
|
||||||
|
}
|
||||||
|
val delayForNextStateRequest = max(minWaitTime, parameters.delayInSecondsForNextStateRequest).seconds
|
||||||
|
|
||||||
|
while (iteration < parameters.maxNumberOfStateRequests) {
|
||||||
|
try {
|
||||||
|
val message = messageBuilder.createDecoupledTanStatusMessage(context, tanResponse)
|
||||||
|
|
||||||
|
val response = getAndHandleResponseForMessage(context, message)
|
||||||
|
|
||||||
|
val tanFeedbacks = response.segmentFeedbacks.filter { it.referenceSegmentNumber == MessageBuilder.SignedMessagePayloadFirstSegmentNumber }
|
||||||
|
if (tanFeedbacks.isNotEmpty()) {
|
||||||
|
// new feedback code for Decoupled TAN: 0900 Sicherheitsfreigabe gültig
|
||||||
|
// Sparkasse responds for pushTan with: HIRMS:4:2:3+0020::Der Auftrag wurde ausgeführt.+0020::Die gebuchten Umsätze wurden übermittelt.'
|
||||||
|
val isTanApproved = tanFeedbacks.any { it.feedbacks.any { it.responseCode == 900 || it.responseCode == 20 } }
|
||||||
|
if (isTanApproved) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration++
|
||||||
|
// sometimes delayInSecondsForNextStateRequests is only 1 or 2 seconds, that's too fast i think
|
||||||
|
delay(delayForNextStateRequest)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Could not check status of Decoupled TAN" }
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tanChallenge.tanExpired()
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun handleEnterTanResult(context: JobContext, enteredTanResult: EnterTanResult, tanResponse: TanResponse,
|
protected open suspend fun handleEnterTanResult(context: JobContext, enteredTanResult: EnterTanResult, tanResponse: TanResponse,
|
||||||
|
@ -429,19 +527,18 @@ open class FinTsJobExecutor(
|
||||||
|
|
||||||
if (enteredTanResult.changeTanMethodTo != null) {
|
if (enteredTanResult.changeTanMethodTo != null) {
|
||||||
return handleUserAsksToChangeTanMethodAndResendLastMessage(context, enteredTanResult.changeTanMethodTo)
|
return handleUserAsksToChangeTanMethodAndResendLastMessage(context, enteredTanResult.changeTanMethodTo)
|
||||||
}
|
} else if (enteredTanResult.changeTanMediumTo != null) {
|
||||||
else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) {
|
|
||||||
return handleUserAsksToChangeTanMediumAndResendLastMessage(context, enteredTanResult.changeTanMediumTo,
|
return handleUserAsksToChangeTanMediumAndResendLastMessage(context, enteredTanResult.changeTanMediumTo,
|
||||||
enteredTanResult.changeTanMediumResultCallback)
|
enteredTanResult.changeTanMediumResultCallback)
|
||||||
}
|
} else if (enteredTanResult.userApprovedDecoupledTan == true && enteredTanResult.responseAfterApprovingDecoupledTan != null) {
|
||||||
else if (enteredTanResult.enteredTan == null) {
|
return enteredTanResult.responseAfterApprovingDecoupledTan
|
||||||
|
} else if (enteredTanResult.enteredTan == null) {
|
||||||
// i tried to send a HKTAN with cancelJob = true but then i saw there are no tan methods that support cancellation (at least not at my bank)
|
// i tried to send a HKTAN with cancelJob = true but then i saw there are no tan methods that support cancellation (at least not at my bank)
|
||||||
// but it's not required anyway, tan times out after some time. Simply don't respond anything and close dialog
|
// but it's not required anyway, tan times out after some time. Simply don't respond anything and close dialog
|
||||||
response.tanRequiredButUserDidNotEnterOne = true
|
response.tanRequiredButUserDidNotEnterOne = true
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return sendTanToBank(context, enteredTanResult.enteredTan, tanResponse)
|
return sendTanToBank(context, enteredTanResult.enteredTan, tanResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,7 +562,7 @@ open class FinTsJobExecutor(
|
||||||
return resendMessageInNewDialog(context, lastCreatedMessage)
|
return resendMessageInNewDialog(context, lastCreatedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun handleUserAsksToChangeTanMediumAndResendLastMessage(context: JobContext, changeTanMediumTo: TanGeneratorTanMedium,
|
protected open suspend fun handleUserAsksToChangeTanMediumAndResendLastMessage(context: JobContext, changeTanMediumTo: TanMedium,
|
||||||
changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): BankResponse {
|
changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): BankResponse {
|
||||||
|
|
||||||
val lastCreatedMessage = context.dialog.currentMessage
|
val lastCreatedMessage = context.dialog.currentMessage
|
||||||
|
@ -493,7 +590,8 @@ open class FinTsJobExecutor(
|
||||||
|
|
||||||
val initDialogResponse = initDialogWithStrongCustomerAuthentication(context)
|
val initDialogResponse = initDialogWithStrongCustomerAuthentication(context)
|
||||||
|
|
||||||
if (initDialogResponse.successful == false) {
|
// if lastCreatedMessage was a dialog init message, there's no need to send this message again, we just initialized a new dialog in initDialogWithStrongCustomerAuthentication()
|
||||||
|
if (initDialogResponse.successful == false || lastCreatedMessage.isDialogInitMessage()) {
|
||||||
return initDialogResponse
|
return initDialogResponse
|
||||||
} else {
|
} else {
|
||||||
val newMessage = messageBuilder.rebuildMessage(context, lastCreatedMessage)
|
val newMessage = messageBuilder.rebuildMessage(context, lastCreatedMessage)
|
||||||
|
@ -566,7 +664,7 @@ open class FinTsJobExecutor(
|
||||||
|
|
||||||
protected open suspend fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context: JobContext): BankResponse {
|
protected open suspend fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context: JobContext): BankResponse {
|
||||||
|
|
||||||
context.startNewDialog(false) // don't know if it's ok for all invocations of this method to set closeDialog to false (was actually only set in getAccounts())
|
context.startNewDialog() // don't know if it's ok for all invocations of this method to set closeDialog to false (was actually only set in getAccounts())
|
||||||
|
|
||||||
val message = messageBuilder.createInitDialogMessage(context)
|
val message = messageBuilder.createInitDialogMessage(context)
|
||||||
|
|
||||||
|
@ -642,7 +740,7 @@ open class FinTsJobExecutor(
|
||||||
return BankResponse(true, noTanMethodSelected = noTanMethodSelected, internalError = errorMessage)
|
return BankResponse(true, noTanMethodSelected = noTanMethodSelected, internalError = errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun getUsersTanMethod(context: JobContext, preferredTanMethods: List<TanMethodType>? = null): Boolean {
|
open suspend fun getUsersTanMethod(context: JobContext): Boolean {
|
||||||
val bank = context.bank
|
val bank = context.bank
|
||||||
|
|
||||||
if (bank.tanMethodsAvailableForUser.size == 1) { // user has only one TAN method -> set it and we're done
|
if (bank.tanMethodsAvailableForUser.size == 1) { // user has only one TAN method -> set it and we're done
|
||||||
|
@ -650,13 +748,13 @@ open class FinTsJobExecutor(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tanMethodSelector.findPreferredTanMethod(bank.tanMethodsAvailableForUser, preferredTanMethods)?.let {
|
tanMethodSelector.findPreferredTanMethod(bank.tanMethodsAvailableForUser, context.preferredTanMethods, context.tanMethodsNotSupportedByApplication)?.let {
|
||||||
bank.selectedTanMethod = it
|
bank.selectedTanMethod = it
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// we know user's supported tan methods, now ask user which one to select
|
// we know user's supported tan methods, now ask user which one to select
|
||||||
val suggestedTanMethod = tanMethodSelector.getSuggestedTanMethod(bank.tanMethodsAvailableForUser)
|
val suggestedTanMethod = tanMethodSelector.getSuggestedTanMethod(bank.tanMethodsAvailableForUser, context.tanMethodsNotSupportedByApplication)
|
||||||
|
|
||||||
val selectedTanMethod = context.callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod)
|
val selectedTanMethod = context.callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod)
|
||||||
|
|
||||||
|
@ -677,14 +775,14 @@ open class FinTsJobExecutor(
|
||||||
|
|
||||||
protected open fun updateBankAndCustomerDataIfResponseSuccessful(context: JobContext, response: BankResponse) {
|
protected open fun updateBankAndCustomerDataIfResponseSuccessful(context: JobContext, response: BankResponse) {
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
updateBankAndCustomerData(context.bank, response)
|
updateBankAndCustomerData(context.bank, response, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun updateBankAndCustomerData(bank: BankData, response: BankResponse) {
|
protected open fun updateBankAndCustomerData(bank: BankData, response: BankResponse, context: JobContext) {
|
||||||
updateBankData(bank, response)
|
updateBankData(bank, response)
|
||||||
|
|
||||||
modelMapper.updateCustomerData(bank, response)
|
modelMapper.updateCustomerData(bank, response, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
package net.dankito.banking.fints
|
package net.codinux.banking.fints
|
||||||
|
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import net.dankito.banking.fints.messages.MessageBuilder
|
import net.codinux.banking.fints.messages.MessageBuilder
|
||||||
import net.dankito.banking.fints.messages.MessageBuilderResult
|
import net.codinux.banking.fints.messages.MessageBuilderResult
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.response.BankResponse
|
import net.codinux.banking.fints.response.BankResponse
|
||||||
import net.dankito.banking.fints.response.segments.TanResponse
|
import net.codinux.banking.fints.response.segments.TanResponse
|
||||||
import net.dankito.banking.fints.util.IBase64Service
|
import net.codinux.banking.fints.util.IBase64Service
|
||||||
import net.dankito.banking.fints.util.PureKotlinBase64Service
|
import net.codinux.banking.fints.util.PureKotlinBase64Service
|
||||||
import net.dankito.banking.fints.webclient.IWebClient
|
import net.codinux.banking.fints.webclient.IWebClient
|
||||||
import net.dankito.banking.fints.webclient.KtorWebClient
|
import net.codinux.banking.fints.webclient.KtorWebClient
|
||||||
import net.dankito.banking.fints.webclient.WebClientResponse
|
import net.codinux.banking.fints.webclient.WebClientResponse
|
||||||
import net.dankito.banking.fints.extensions.getAllExceptionMessagesJoined
|
import net.codinux.banking.fints.extensions.getAllExceptionMessagesJoined
|
||||||
|
import net.codinux.banking.fints.response.segments.ReceivedSegment
|
||||||
|
|
||||||
|
|
||||||
open class RequestExecutor(
|
open class RequestExecutor(
|
||||||
|
@ -104,9 +105,11 @@ open class RequestExecutor(
|
||||||
try {
|
try {
|
||||||
val decodedResponse = decodeBase64Response(responseBody)
|
val decodedResponse = decodeBase64Response(responseBody)
|
||||||
|
|
||||||
addMessageLog(context, MessageLogEntryType.Received, decodedResponse)
|
val parsedResponse = context.responseParser.parse(decodedResponse)
|
||||||
|
|
||||||
return context.responseParser.parse(decodedResponse)
|
addMessageLog(context, MessageLogEntryType.Received, decodedResponse, parsedResponse.receivedSegments)
|
||||||
|
|
||||||
|
return parsedResponse
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(context, "Could not decode responseBody:\r\n'$responseBody'", e)
|
logError(context, "Could not decode responseBody:\r\n'$responseBody'", e)
|
||||||
|
|
||||||
|
@ -164,8 +167,8 @@ open class RequestExecutor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun addMessageLog(context: JobContext, type: MessageLogEntryType, message: String) {
|
protected open fun addMessageLog(context: JobContext, type: MessageLogEntryType, message: String, parsedSegments: List<ReceivedSegment> = emptyList()) {
|
||||||
context.addMessageLog(type, message)
|
context.addMessageLog(type, message, parsedSegments)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun logError(context: JobContext, message: String, e: Exception?) {
|
protected open fun logError(context: JobContext, message: String, e: Exception?) {
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.callback
|
package net.codinux.banking.fints.callback
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
|
|
||||||
|
|
||||||
interface FinTsClientCallback {
|
interface FinTsClientCallback {
|
||||||
|
@ -25,6 +25,13 @@ interface FinTsClientCallback {
|
||||||
*
|
*
|
||||||
* If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc]
|
* If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc]
|
||||||
*/
|
*/
|
||||||
suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult
|
suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanMedium): EnterTanGeneratorAtcResult
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets fired when a FinTS message get sent to bank server, a FinTS message is received from bank server or an error occurred.
|
||||||
|
*
|
||||||
|
* Be aware, in order that this message gets fired [net.codinux.banking.fints.config.FinTsClientOptions.fireCallbackOnMessageLogs] has to be set to true.
|
||||||
|
*/
|
||||||
|
fun messageLogAdded(messageLogEntry: MessageLogEntry)
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.callback
|
package net.codinux.banking.fints.callback
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
|
|
||||||
|
|
||||||
open class NoOpFinTsClientCallback : FinTsClientCallback {
|
open class NoOpFinTsClientCallback : FinTsClientCallback {
|
||||||
|
@ -14,8 +14,12 @@ open class NoOpFinTsClientCallback : FinTsClientCallback {
|
||||||
return tanChallenge.userDidNotEnterTan()
|
return tanChallenge.userDidNotEnterTan()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
|
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanMedium): EnterTanGeneratorAtcResult {
|
||||||
return EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
return EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun messageLogAdded(messageLogEntry: MessageLogEntry) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,18 +1,19 @@
|
||||||
package net.dankito.banking.fints.callback
|
package net.codinux.banking.fints.callback
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
|
|
||||||
|
|
||||||
open class SimpleFinTsClientCallback(
|
open class SimpleFinTsClientCallback(
|
||||||
protected open val enterTan: ((tanChallenge: TanChallenge) -> Unit)? = null,
|
protected open val askUserForTanMethod: ((supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?) -> TanMethod?)? = null,
|
||||||
protected open val enterTanGeneratorAtc: ((bank: BankData, tanMedium: TanGeneratorTanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
protected open val messageLogAdded: ((MessageLogEntry) -> Unit)? = null,
|
||||||
protected open val askUserForTanMethod: ((supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?) -> TanMethod?)? = null
|
protected open val enterTanGeneratorAtc: ((bank: BankData, tanMedium: TanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
||||||
|
protected open val enterTan: ((tanChallenge: TanChallenge) -> Unit)? = null
|
||||||
) : FinTsClientCallback {
|
) : FinTsClientCallback {
|
||||||
|
|
||||||
constructor() : this(null) // Swift does not support default parameter values -> create constructor overloads
|
constructor() : this(null as ((tanChallenge: TanChallenge) -> Unit)?) // Swift does not support default parameter values -> create constructor overloads
|
||||||
|
|
||||||
constructor(enterTan: ((tanChallenge: TanChallenge) -> Unit)?) : this(enterTan, null)
|
constructor(enterTan: ((tanChallenge: TanChallenge) -> Unit)?) : this(null, null, null, enterTan)
|
||||||
|
|
||||||
|
|
||||||
override suspend fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?): TanMethod? {
|
override suspend fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?): TanMethod? {
|
||||||
|
@ -24,8 +25,12 @@ open class SimpleFinTsClientCallback(
|
||||||
enterTan?.invoke(tanChallenge) ?: run { tanChallenge.userDidNotEnterTan() }
|
enterTan?.invoke(tanChallenge) ?: run { tanChallenge.userDidNotEnterTan() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
|
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanMedium): EnterTanGeneratorAtcResult {
|
||||||
return enterTanGeneratorAtc?.invoke(bank, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
return enterTanGeneratorAtc?.invoke(bank, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun messageLogAdded(messageLogEntry: MessageLogEntry) {
|
||||||
|
messageLogAdded?.invoke(messageLogEntry)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
package net.dankito.banking.fints.config
|
package net.codinux.banking.fints.config
|
||||||
|
|
||||||
import net.dankito.banking.fints.FinTsJobExecutor
|
import net.codinux.banking.fints.FinTsJobExecutor
|
||||||
import net.dankito.banking.fints.RequestExecutor
|
import net.codinux.banking.fints.RequestExecutor
|
||||||
import net.dankito.banking.fints.messages.MessageBuilder
|
import net.codinux.banking.fints.messages.MessageBuilder
|
||||||
import net.dankito.banking.fints.model.mapper.ModelMapper
|
import net.codinux.banking.fints.model.mapper.ModelMapper
|
||||||
import net.dankito.banking.fints.util.FinTsServerAddressFinder
|
import net.codinux.banking.fints.util.FinTsServerAddressFinder
|
||||||
import net.dankito.banking.fints.util.IBase64Service
|
import net.codinux.banking.fints.util.IBase64Service
|
||||||
import net.dankito.banking.fints.util.PureKotlinBase64Service
|
import net.codinux.banking.fints.util.PureKotlinBase64Service
|
||||||
import net.dankito.banking.fints.util.TanMethodSelector
|
import net.codinux.banking.fints.util.TanMethodSelector
|
||||||
import net.dankito.banking.fints.webclient.IWebClient
|
import net.codinux.banking.fints.webclient.IWebClient
|
||||||
import net.dankito.banking.fints.webclient.KtorWebClient
|
import net.codinux.banking.fints.webclient.KtorWebClient
|
||||||
|
|
||||||
class FinTsClientConfiguration(
|
class FinTsClientConfiguration(
|
||||||
var options: FinTsClientOptions = FinTsClientOptions(),
|
var options: FinTsClientOptions = FinTsClientOptions(),
|
|
@ -0,0 +1,40 @@
|
||||||
|
package net.codinux.banking.fints.config
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.model.ProductData
|
||||||
|
|
||||||
|
data class FinTsClientOptions(
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If FinTS messages sent to and received from bank servers and errors should be collected. They are then accessible
|
||||||
|
* via [net.codinux.banking.fints.response.client.FinTsClientResponse.messageLog].
|
||||||
|
*
|
||||||
|
* Set to false by default.
|
||||||
|
*/
|
||||||
|
val collectMessageLog: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true then [net.codinux.banking.fints.callback.FinTsClientCallback.messageLogAdded] get fired when a
|
||||||
|
* FinTS message get sent to bank server, a FinTS message is received from bank server or an error occurred.
|
||||||
|
*
|
||||||
|
* Defaults to false.
|
||||||
|
*/
|
||||||
|
val fireCallbackOnMessageLogs: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If sensitive data like user name, password, login name should be removed from FinTS messages before being logged.
|
||||||
|
*
|
||||||
|
* Defaults to true.
|
||||||
|
*/
|
||||||
|
val removeSensitiveDataFromMessageLog: Boolean = true,
|
||||||
|
|
||||||
|
val appendFinTsMessagesToLog: Boolean = false,
|
||||||
|
|
||||||
|
val closeDialogs: Boolean = true,
|
||||||
|
|
||||||
|
val version: String = "1.0.0", // TODO: get version dynamically
|
||||||
|
val productName: String = "15E53C26816138699C7B6A3E8" // TODO: extract constant // TODO: get product number for fints4k and Bankmeister (if we stick with that name)
|
||||||
|
) {
|
||||||
|
|
||||||
|
val product: ProductData by lazy { ProductData(productName, version) }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.DateTimeUnit
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.plus
|
||||||
|
|
||||||
|
|
||||||
|
// should actually be named `now()`, but that name is already shadowed by deprecated Instant.Companion.now() method
|
||||||
|
fun Instant.Companion.nowExt(): Instant = Clock.System.now()
|
||||||
|
|
||||||
|
fun Instant.plusMinutes(minutes: Int) = this.plus(minutes, DateTimeUnit.MINUTE)
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.extensions
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.*
|
||||||
import kotlin.js.JsName
|
import kotlin.js.JsName
|
||||||
|
@ -12,7 +12,7 @@ fun LocalDate.Companion.todayAtSystemDefaultTimeZone(): LocalDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LocalDate.Companion.todayAtEuropeBerlin(): LocalDate {
|
fun LocalDate.Companion.todayAtEuropeBerlin(): LocalDate {
|
||||||
return nowAt(TimeZone.europeBerlin)
|
return nowAt(TimeZone.EuropeBerlin)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsName("nowAtForDate")
|
@JsName("nowAtForDate")
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.extensions
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.*
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ fun LocalDateTime.Companion.nowAtUtc(): LocalDateTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LocalDateTime.Companion.nowAtEuropeBerlin(): LocalDateTime {
|
fun LocalDateTime.Companion.nowAtEuropeBerlin(): LocalDateTime {
|
||||||
return nowAt(TimeZone.europeBerlin)
|
return nowAt(TimeZone.EuropeBerlin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LocalDateTime.Companion.nowAt(timeZone: TimeZone): LocalDateTime {
|
fun LocalDateTime.Companion.nowAt(timeZone: TimeZone): LocalDateTime {
|
||||||
return Clock.System.now().toLocalDateTime(timeZone)
|
return Instant.nowExt().toLocalDateTime(timeZone)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.extensions
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
|
||||||
fun Int.toStringWithMinDigits(minimumCountDigits: Int, fillerString: Char = '0'): String {
|
fun Int.toStringWithMinDigits(minimumCountDigits: Int, fillerString: Char = '0'): String {
|
|
@ -0,0 +1,11 @@
|
||||||
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
fun randomWithSeed(): Random = Random(randomSeed())
|
||||||
|
|
||||||
|
fun randomSeed(): Long {
|
||||||
|
return Instant.nowExt().nanosecondsOfSecond.toLong() + Instant.nowExt().toEpochMilliseconds()
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.extensions
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.extensions
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
|
||||||
fun Throwable.getAllExceptionMessagesJoined(maxDepth: Int = 5): String {
|
fun Throwable.getAllExceptionMessagesJoined(maxDepth: Int = 5): String {
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.fints.extensions
|
||||||
|
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
|
||||||
|
|
||||||
|
val TimeZone.Companion.EuropeBerlin: TimeZone
|
||||||
|
get() = TimeZone.of("Europe/Berlin")
|
|
@ -0,0 +1,10 @@
|
||||||
|
package net.codinux.banking.fints.log
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|
||||||
|
interface IMessageLogAppender {
|
||||||
|
|
||||||
|
fun logError(loggingClass: KClass<*>, message: String, e: Throwable? = null)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.fints.log
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.model.AccountData
|
||||||
|
import net.codinux.banking.fints.model.BankData
|
||||||
|
import net.codinux.banking.fints.model.MessageType
|
||||||
|
import net.codinux.banking.fints.model.JobContextType
|
||||||
|
|
||||||
|
|
||||||
|
class MessageContext(
|
||||||
|
val jobType: JobContextType,
|
||||||
|
val messageType: MessageType,
|
||||||
|
val jobNumber: Int,
|
||||||
|
val dialogNumber: Int,
|
||||||
|
val messageNumber: Int,
|
||||||
|
val bank: BankData,
|
||||||
|
val account: AccountData?
|
||||||
|
) {
|
||||||
|
override fun toString() = "${jobNumber}_${dialogNumber}_$messageNumber ${bank.bankCode} $jobType $messageType"
|
||||||
|
}
|
|
@ -1,19 +1,22 @@
|
||||||
package net.dankito.banking.fints.log
|
package net.codinux.banking.fints.log
|
||||||
|
|
||||||
import net.codinux.log.LoggerFactory
|
import net.codinux.log.LoggerFactory
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import net.dankito.banking.fints.config.FinTsClientOptions
|
import net.codinux.banking.fints.callback.FinTsClientCallback
|
||||||
import net.dankito.banking.fints.model.BankData
|
import net.codinux.banking.fints.config.FinTsClientOptions
|
||||||
import net.dankito.banking.fints.model.MessageLogEntry
|
import net.codinux.banking.fints.model.BankData
|
||||||
import net.dankito.banking.fints.model.MessageLogEntryType
|
import net.codinux.banking.fints.model.MessageLogEntry
|
||||||
import net.dankito.banking.fints.extensions.getInnerException
|
import net.codinux.banking.fints.model.MessageLogEntryType
|
||||||
import net.dankito.banking.fints.extensions.nthIndexOf
|
import net.codinux.banking.fints.extensions.getInnerException
|
||||||
import net.dankito.banking.fints.extensions.toStringWithMinDigits
|
import net.codinux.banking.fints.extensions.nthIndexOf
|
||||||
import net.dankito.banking.fints.util.FinTsUtils
|
import net.codinux.banking.fints.extensions.toStringWithMinDigits
|
||||||
|
import net.codinux.banking.fints.response.segments.ReceivedSegment
|
||||||
|
import net.codinux.banking.fints.util.FinTsUtils
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|
||||||
open class MessageLogCollector(
|
open class MessageLogCollector(
|
||||||
|
private val callback: FinTsClientCallback,
|
||||||
private val options: FinTsClientOptions = FinTsClientOptions(),
|
private val options: FinTsClientOptions = FinTsClientOptions(),
|
||||||
private val finTsUtils: FinTsUtils = FinTsUtils()
|
private val finTsUtils: FinTsUtils = FinTsUtils()
|
||||||
) {
|
) {
|
||||||
|
@ -34,43 +37,57 @@ open class MessageLogCollector(
|
||||||
|
|
||||||
// in either case remove sensitive data after response is parsed as otherwise some information like account holder name and accounts may is not set yet on BankData
|
// in either case remove sensitive data after response is parsed as otherwise some information like account holder name and accounts may is not set yet on BankData
|
||||||
open val messageLog: List<MessageLogEntry>
|
open val messageLog: List<MessageLogEntry>
|
||||||
// safe CPU cycles by only formatting and removing sensitive data if messageLog is really requested
|
// safe CPU cycles by only removing sensitive data if messageLog is really requested
|
||||||
get() = _messageLog.map { MessageLogEntry(it.type, it.context, it.messageTrace, createMessageForLog(it), it.error, it.time) }
|
get() = _messageLog.map {
|
||||||
|
val message = createMessageForLog(it)
|
||||||
|
val messageWithoutSensitiveData = if (options.removeSensitiveDataFromMessageLog) {
|
||||||
|
safelyRemoveSensitiveDataFromMessage(message, it.context.bank)
|
||||||
|
} else {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
private fun createMessageForLog(logEntry: MessageLogEntry): String {
|
MessageLogEntry(it.type, it.context, it.messageTrace, message, messageWithoutSensitiveData, it.error, it.parsedSegments, it.time)
|
||||||
val message = if (logEntry.type == MessageLogEntryType.Error) {
|
|
||||||
logEntry.messageTrace + logEntry.message + (if (logEntry.error != null) NewLine + getStackTrace(logEntry.error!!) else "")
|
|
||||||
} else {
|
|
||||||
logEntry.messageTrace + "\n" + prettyPrintFinTsMessage(logEntry.message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (options.removeSensitiveDataFromMessageLog) {
|
private fun createMessageForLog(logEntry: MessageLogEntry): String =
|
||||||
safelyRemoveSensitiveDataFromMessage(message, logEntry.context.bank)
|
if (logEntry.type == MessageLogEntryType.Error) {
|
||||||
|
logEntry.message + (if (logEntry.error != null) NewLine + getStackTrace(logEntry.error!!) else "")
|
||||||
} else {
|
} else {
|
||||||
message
|
logEntry.message
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open fun addMessageLog(type: MessageLogEntryType, message: String, context: MessageContext) {
|
open fun addMessageLog(type: MessageLogEntryType, message: String, context: MessageContext, parsedSegments: List<ReceivedSegment> = emptyList()) {
|
||||||
val messageTrace = createMessageTraceString(type, context)
|
val messageTrace = createMessageTraceString(type, context)
|
||||||
|
val prettyPrintMessage = prettyPrintMessageIfRequired(message)
|
||||||
|
|
||||||
addMessageLogEntry(type, context, messageTrace, message)
|
log.debug { "$messageTrace\n$prettyPrintMessage" }
|
||||||
|
|
||||||
log.debug { "$messageTrace\n${prettyPrintFinTsMessage(message)}" }
|
addMessageLogEntry(type, context, messageTrace, prettyPrintMessage, null, parsedSegments)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun logError(loggingClass: KClass<*>, message: String, context: MessageContext, e: Exception? = null) {
|
open fun logError(loggingClass: KClass<*>, message: String, context: MessageContext, e: Throwable? = null) {
|
||||||
val type = MessageLogEntryType.Error
|
val type = MessageLogEntryType.Error
|
||||||
val messageTrace = createMessageTraceString(type, context)
|
val messageTrace = createMessageTraceString(type, context)
|
||||||
|
val prettyPrintMessage = prettyPrintFinTsMessage(message) // error messages almost always get logged / displayed -> pretty print
|
||||||
|
|
||||||
LoggerFactory.getLogger(loggingClass).error(e) { messageTrace + messageTrace }
|
LoggerFactory.getLogger(loggingClass).error(e) { "$messageTrace\n$prettyPrintMessage" }
|
||||||
|
|
||||||
addMessageLogEntry(type, context, messageTrace, message, e)
|
addMessageLogEntry(type, context, messageTrace, prettyPrintMessage, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun addMessageLogEntry(type: MessageLogEntryType, context: MessageContext, messageTrace: String, message: String, error: Throwable? = null) {
|
protected open fun addMessageLogEntry(type: MessageLogEntryType, context: MessageContext, messageTrace: String, message: String, error: Throwable? = null, parsedSegments: List<ReceivedSegment> = emptyList()) {
|
||||||
_messageLog.add(MessageLogEntry(type, context, messageTrace, message, error))
|
if (options.collectMessageLog || options.fireCallbackOnMessageLogs) {
|
||||||
|
val newEntry = MessageLogEntry(type, context, messageTrace, message, null, error, parsedSegments)
|
||||||
|
|
||||||
|
if (options.collectMessageLog) {
|
||||||
|
_messageLog.add(newEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.fireCallbackOnMessageLogs) {
|
||||||
|
callback.messageLogAdded(newEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +95,7 @@ open class MessageLogCollector(
|
||||||
return "${twoDigits(context.jobNumber)}_${twoDigits(context.dialogNumber)}_${twoDigits(context.messageNumber)}_" +
|
return "${twoDigits(context.jobNumber)}_${twoDigits(context.dialogNumber)}_${twoDigits(context.messageNumber)}_" +
|
||||||
"${context.bank.bankCode}_${context.bank.customerId}" +
|
"${context.bank.bankCode}_${context.bank.customerId}" +
|
||||||
"${ context.account?.let { "_${it.accountIdentifier}" } ?: "" }_" +
|
"${ context.account?.let { "_${it.accountIdentifier}" } ?: "" }_" +
|
||||||
"${context.jobType.name}_${context.dialogType.name} " +
|
"${context.jobType.name}_${context.messageType.name} " +
|
||||||
"${getMessageTypeString(type)}:"
|
"${getMessageTypeString(type)}:"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,12 +105,19 @@ open class MessageLogCollector(
|
||||||
|
|
||||||
protected open fun getMessageTypeString(type: MessageLogEntryType): String {
|
protected open fun getMessageTypeString(type: MessageLogEntryType): String {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
MessageLogEntryType.Sent -> "Sending message"
|
MessageLogEntryType.Sent -> "01 Sending message"
|
||||||
MessageLogEntryType.Received -> "Received message"
|
MessageLogEntryType.Received -> "02 Received message"
|
||||||
MessageLogEntryType.Error -> "Error"
|
MessageLogEntryType.Error -> "03 Error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun prettyPrintMessageIfRequired(message: String): String =
|
||||||
|
if (options.collectMessageLog || options.fireCallbackOnMessageLogs || log.isDebugEnabled) { // only use CPU cycles if message will ever be used / displayed
|
||||||
|
prettyPrintFinTsMessage(message)
|
||||||
|
} else {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun prettyPrintFinTsMessage(message: String): String =
|
protected open fun prettyPrintFinTsMessage(message: String): String =
|
||||||
finTsUtils.prettyPrintFinTsMessage(message)
|
finTsUtils.prettyPrintFinTsMessage(message)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.mapper
|
package net.codinux.banking.fints.mapper
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.dankito.banking.client.model.*
|
import net.dankito.banking.client.model.*
|
||||||
|
@ -7,14 +7,14 @@ import net.dankito.banking.client.model.parameter.FinTsClientParameter
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
||||||
import net.dankito.banking.client.model.response.ErrorCode
|
import net.dankito.banking.client.model.response.ErrorCode
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.response.client.FinTsClientResponse
|
import net.codinux.banking.fints.response.client.FinTsClientResponse
|
||||||
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
|
import net.codinux.banking.fints.response.client.GetAccountTransactionsResponse
|
||||||
import net.dankito.banking.fints.response.segments.AccountType
|
import net.codinux.banking.fints.response.segments.AccountType
|
||||||
import net.dankito.banking.fints.util.BicFinder
|
import net.codinux.banking.fints.util.BicFinder
|
||||||
import net.dankito.banking.fints.extensions.minusDays
|
import net.codinux.banking.fints.extensions.minusDays
|
||||||
import net.dankito.banking.fints.extensions.todayAtEuropeBerlin
|
import net.codinux.banking.fints.extensions.todayAtEuropeBerlin
|
||||||
|
|
||||||
|
|
||||||
open class FinTsModelMapper {
|
open class FinTsModelMapper {
|
||||||
|
@ -22,8 +22,11 @@ open class FinTsModelMapper {
|
||||||
protected open val bicFinder = BicFinder()
|
protected open val bicFinder = BicFinder()
|
||||||
|
|
||||||
|
|
||||||
open fun mapToBankData(param: FinTsClientParameter, finTsServerAddress: String): BankData {
|
open fun mapToBankData(param: FinTsClientParameter, finTsServerAddress: String, defaultValues: BankData? = null): BankData {
|
||||||
return BankData(param.bankCode, param.loginName, param.password, finTsServerAddress, bicFinder.findBic(param.bankCode) ?: "")
|
return BankData(
|
||||||
|
param.bankCode, param.loginName, param.password, finTsServerAddress,
|
||||||
|
defaultValues?.bic ?: bicFinder.findBic(param.bankCode) ?: "", defaultValues?.bankName ?: ""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapToAccountData(credentials: BankAccountIdentifier, param: FinTsClientParameter): AccountData {
|
open fun mapToAccountData(credentials: BankAccountIdentifier, param: FinTsClientParameter): AccountData {
|
||||||
|
@ -51,7 +54,7 @@ open class FinTsModelMapper {
|
||||||
|
|
||||||
open fun map(account: AccountData): BankAccount {
|
open fun map(account: AccountData): BankAccount {
|
||||||
return BankAccount(account.accountIdentifier, account.subAccountAttribute, account.iban, account.accountHolderName, map(account.accountType), account.productName,
|
return BankAccount(account.accountIdentifier, account.subAccountAttribute, account.iban, account.accountHolderName, map(account.accountType), account.productName,
|
||||||
account.currency ?: Currency.DefaultCurrencyCode, account.accountLimit, account.countDaysForWhichTransactionsAreKept, account.isAccountTypeSupportedByApplication,
|
account.currency ?: Currency.DefaultCurrencyCode, account.accountLimit, account.serverTransactionsRetentionDays, account.isAccountTypeSupportedByApplication,
|
||||||
account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, account.supportsTransferringMoney, account.supportsRealTimeTransfer)
|
account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, account.supportsTransferringMoney, account.supportsRealTimeTransfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,16 +73,34 @@ open class FinTsModelMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun map(bank: BankData, retrievedTransactionsResponses: List<GetAccountTransactionsResponse>): CustomerAccount {
|
open fun map(bank: BankData, retrievedTransactionsResponses: List<GetAccountTransactionsResponse>, retrieveTransactionsTo: LocalDate? = null): CustomerAccount {
|
||||||
val customerAccount = map(bank)
|
val customerAccount = map(bank)
|
||||||
val retrievedData = retrievedTransactionsResponses.mapNotNull { it.retrievedData }
|
val retrievedData = retrievedTransactionsResponses.mapNotNull { it.retrievedData }
|
||||||
|
|
||||||
customerAccount.accounts.forEach { bankAccount ->
|
customerAccount.accounts.forEach { bankAccount ->
|
||||||
retrievedData.firstOrNull { it.account.accountIdentifier == bankAccount.identifier }?.let { accountTransactionsResponse ->
|
retrievedData.firstOrNull { it.account.accountIdentifier == bankAccount.identifier }?.let { accountTransactionsResponse ->
|
||||||
bankAccount.balance = accountTransactionsResponse.balance ?: Money.Zero
|
accountTransactionsResponse.balance?.let { balance ->
|
||||||
bankAccount.retrievedTransactionsFrom = accountTransactionsResponse.retrievedTransactionsFrom
|
bankAccount.balance = balance
|
||||||
bankAccount.retrievedTransactionsTo = accountTransactionsResponse.retrievedTransactionsTo
|
}
|
||||||
bankAccount.bookedTransactions = map(accountTransactionsResponse)
|
|
||||||
|
if (accountTransactionsResponse.retrievedTransactionsFrom != null && (bankAccount.retrievedTransactionsFrom == null ||
|
||||||
|
accountTransactionsResponse.retrievedTransactionsFrom!! < bankAccount.retrievedTransactionsFrom!!)) {
|
||||||
|
bankAccount.retrievedTransactionsFrom = accountTransactionsResponse.retrievedTransactionsFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
val retrievalTime = accountTransactionsResponse.retrievalTime
|
||||||
|
if (retrieveTransactionsTo == null && (bankAccount.lastAccountUpdateTime == null || bankAccount.lastAccountUpdateTime!! <= retrievalTime || // if retrieveTransactionsTo is set, then we don't retrieve all current transactions -> don't set lastAccountUpdateTime
|
||||||
|
(bankAccount.supportsRetrievingTransactions == false && accountTransactionsResponse.statementOfHoldings.isNotEmpty()))) { // TODO: really check for supportsRetrievingTransactions == false if statementOfHoldings are set? Are there really accounts that support HKWPD and HKKAZ?
|
||||||
|
bankAccount.lastAccountUpdateTime = retrievalTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountTransactionsResponse.bookedTransactions.isNotEmpty()) {
|
||||||
|
bankAccount.bookedTransactions = bankAccount.bookedTransactions.toMutableList().apply {
|
||||||
|
addAll(map(accountTransactionsResponse))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bankAccount.statementOfHoldings = accountTransactionsResponse.statementOfHoldings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,15 +111,29 @@ open class FinTsModelMapper {
|
||||||
return data.bookedTransactions.map { map(it) }
|
return data.bookedTransactions.map { map(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun map(transaction: net.dankito.banking.fints.model.AccountTransaction): AccountTransaction {
|
open fun map(transaction: net.codinux.banking.fints.model.AccountTransaction): AccountTransaction {
|
||||||
return AccountTransaction(transaction.amount, transaction.unparsedReference, transaction.bookingDate,
|
return AccountTransaction(
|
||||||
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, transaction.valueDate,
|
transaction.amount, transaction.reference,
|
||||||
transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance,
|
transaction.bookingDate, transaction.valueDate,
|
||||||
transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
|
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
|
||||||
transaction.compensationAmount, transaction.originalAmount, transaction.sepaReference, transaction.deviantOriginator, transaction.deviantRecipient,
|
|
||||||
transaction.referenceWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement,
|
transaction.postingText,
|
||||||
transaction.currencyType, transaction.bookingKey, transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails,
|
|
||||||
transaction.transactionReferenceNumber, transaction.relatedReferenceNumber)
|
transaction.openingBalance, transaction.closingBalance,
|
||||||
|
|
||||||
|
transaction.statementNumber, transaction.sheetNumber,
|
||||||
|
|
||||||
|
transaction.customerReference, transaction.bankReference, transaction.furtherInformation,
|
||||||
|
|
||||||
|
transaction.endToEndReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
|
||||||
|
transaction.compensationAmount, transaction.originalAmount, transaction.deviantOriginator, transaction.deviantRecipient,
|
||||||
|
transaction.referenceWithNoSpecialType,
|
||||||
|
|
||||||
|
transaction.journalNumber, transaction.textKeyAddition,
|
||||||
|
transaction.orderReferenceNumber, transaction.referenceNumber,
|
||||||
|
|
||||||
|
transaction.isReversal
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,6 +192,10 @@ open class FinTsModelMapper {
|
||||||
else errorMessages.joinToString("\r\n")
|
else errorMessages.joinToString("\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun mergeMessageLog(vararg messageLogs: List<MessageLogEntry>?): List<MessageLogEntry> {
|
||||||
|
return messageLogs.filterNotNull().flatten()
|
||||||
|
}
|
||||||
|
|
||||||
open fun mergeMessageLog(vararg responses: FinTsClientResponse?): List<MessageLogEntry> {
|
open fun mergeMessageLog(vararg responses: FinTsClientResponse?): List<MessageLogEntry> {
|
||||||
return responses.filterNotNull().flatMap { it.messageLog }
|
return responses.filterNotNull().flatMap { it.messageLog }
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
|
|
||||||
enum class Existenzstatus {
|
enum class Existenzstatus {
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
import io.ktor.utils.io.charsets.Charsets
|
import io.ktor.utils.io.charsets.Charsets
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
import net.dankito.banking.fints.extensions.randomWithSeed
|
import net.codinux.banking.fints.extensions.randomWithSeed
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Aufsetzpunkt
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.Aufsetzpunkt
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemID
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.KundensystemID
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Synchronisierungsmodus
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.Synchronisierungsmodus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.*
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedienArtVersion
|
import net.codinux.banking.fints.messages.segmente.Segment
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse
|
import net.codinux.banking.fints.messages.segmente.Synchronisierung
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanProcess
|
import net.codinux.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||||
import net.dankito.banking.fints.messages.segmente.Segment
|
import net.codinux.banking.fints.messages.segmente.id.ISegmentId
|
||||||
import net.dankito.banking.fints.messages.segmente.Synchronisierung
|
import net.codinux.banking.fints.messages.segmente.implementierte.*
|
||||||
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
import net.codinux.banking.fints.messages.segmente.implementierte.depot.Depotaufstellung
|
||||||
import net.dankito.banking.fints.messages.segmente.id.ISegmentId
|
import net.codinux.banking.fints.messages.segmente.implementierte.sepa.SepaBankTransferBase
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.*
|
import net.codinux.banking.fints.messages.segmente.implementierte.tan.TanGeneratorListeAnzeigen
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.sepa.SepaBankTransferBase
|
import net.codinux.banking.fints.messages.segmente.implementierte.tan.TanGeneratorTanMediumAnOderUmmelden
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.tan.TanGeneratorListeAnzeigen
|
import net.codinux.banking.fints.messages.segmente.implementierte.umsaetze.*
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.tan.TanGeneratorTanMediumAnOderUmmelden
|
import net.codinux.banking.fints.model.*
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.umsaetze.*
|
import net.codinux.banking.fints.response.InstituteSegmentId
|
||||||
import net.dankito.banking.fints.model.*
|
import net.codinux.banking.fints.response.segments.*
|
||||||
import net.dankito.banking.fints.response.InstituteSegmentId
|
import net.codinux.banking.fints.util.FinTsUtils
|
||||||
import net.dankito.banking.fints.response.segments.*
|
|
||||||
import net.dankito.banking.fints.util.FinTsUtils
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +39,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
|
|
||||||
private const val SignatureHeaderSegmentNumber = MessageHeaderSegmentNumber + 1
|
private const val SignatureHeaderSegmentNumber = MessageHeaderSegmentNumber + 1
|
||||||
|
|
||||||
private const val SignedMessagePayloadFirstSegmentNumber = SignatureHeaderSegmentNumber + 1
|
const val SignedMessagePayloadFirstSegmentNumber = SignatureHeaderSegmentNumber + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,7 +131,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.bank.customerSystemId == KundensystemID.Anonymous) {
|
if (context.bank.customerSystemId == KundensystemID.Anonymous) {
|
||||||
segments.add(Synchronisierung(segments.size + 3, Synchronisierungsmodus.NeueKundensystemIdZurueckmelden))
|
segments.add(Synchronisierung(SignedMessagePayloadFirstSegmentNumber + segments.size, Synchronisierungsmodus.NeueKundensystemIdZurueckmelden))
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSignedMessageBuilderResult(context, MessageType.DialogInit, segments)
|
return createSignedMessageBuilderResult(context, MessageType.DialogInit, segments)
|
||||||
|
@ -242,17 +240,38 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
return createSignedMessageBuilderResult(context, MessageType.GetBalance, segments)
|
return createSignedMessageBuilderResult(context, MessageType.GetBalance, segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val securitiesAccountResult = supportsGetSecuritiesAccountBalance(account)
|
||||||
|
|
||||||
|
if (securitiesAccountResult.isJobVersionSupported) {
|
||||||
|
return createGetSecuritiesAccountBalanceMessage(context, result, account)
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun createGetSecuritiesAccountBalanceMessage(context: JobContext, result: MessageBuilderResult,
|
||||||
|
account: AccountData): MessageBuilderResult {
|
||||||
|
|
||||||
|
val segments = mutableListOf<Segment>(Depotaufstellung(SignedMessagePayloadFirstSegmentNumber, account))
|
||||||
|
|
||||||
|
addTanSegmentIfRequired(context, CustomerSegmentId.SecuritiesAccountBalance, segments, SignedMessagePayloadFirstSegmentNumber + 1)
|
||||||
|
|
||||||
|
return createSignedMessageBuilderResult(context, MessageType.GetSecuritiesAccountBalance, segments)
|
||||||
|
}
|
||||||
|
|
||||||
open fun supportsGetBalance(account: AccountData): Boolean {
|
open fun supportsGetBalance(account: AccountData): Boolean {
|
||||||
return supportsGetBalanceMessage(account).isJobVersionSupported
|
return supportsGetBalanceMessage(account).isJobVersionSupported
|
||||||
|
|| supportsGetSecuritiesAccountBalance(account).isJobVersionSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun supportsGetBalanceMessage(account: AccountData): MessageBuilderResult {
|
protected open fun supportsGetBalanceMessage(account: AccountData): MessageBuilderResult {
|
||||||
return getSupportedVersionsOfJobForAccount(CustomerSegmentId.Balance, account, listOf(5, 6, 7, 8))
|
return getSupportedVersionsOfJobForAccount(CustomerSegmentId.Balance, account, listOf(5, 6, 7, 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun supportsGetSecuritiesAccountBalance(account: AccountData): MessageBuilderResult {
|
||||||
|
return getSupportedVersionsOfJobForAccount(CustomerSegmentId.SecuritiesAccountBalance, account, listOf(6))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun createGetTanMediaListMessage(context: JobContext,
|
open fun createGetTanMediaListMessage(context: JobContext,
|
||||||
tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
||||||
|
@ -272,7 +291,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: no HKTAN needed?
|
// TODO: no HKTAN needed?
|
||||||
open fun createChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium,
|
open fun createChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanMedium,
|
||||||
tan: String? = null, atc: Int? = null): MessageBuilderResult {
|
tan: String? = null, atc: Int? = null): MessageBuilderResult {
|
||||||
|
|
||||||
val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.ChangeTanMedium, context.bank, listOf(1, 2, 3))
|
val result = getSupportedVersionsOfJobForBank(CustomerSegmentId.ChangeTanMedium, context.bank, listOf(1, 2, 3))
|
||||||
|
@ -295,12 +314,22 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
|
|
||||||
val segments = listOf(
|
val segments = listOf(
|
||||||
ZweiSchrittTanEinreichung(SignedMessagePayloadFirstSegmentNumber, tanProcess, null,
|
ZweiSchrittTanEinreichung(SignedMessagePayloadFirstSegmentNumber, tanProcess, null,
|
||||||
tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier)
|
tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier, tanResponse.segmentVersion)
|
||||||
)
|
)
|
||||||
|
|
||||||
return createSignedMessageBuilderResult(context, MessageType.Tan, createSignedMessage(context, enteredTan, segments), segments)
|
return createSignedMessageBuilderResult(context, MessageType.Tan, createSignedMessage(context, enteredTan, segments), segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun createDecoupledTanStatusMessage(context: JobContext, tanResponse: TanResponse): MessageBuilderResult {
|
||||||
|
|
||||||
|
val segments = listOf(
|
||||||
|
ZweiSchrittTanEinreichung(SignedMessagePayloadFirstSegmentNumber, TanProcess.AppTan,
|
||||||
|
jobReference = tanResponse.jobReference, furtherTanFollows = false, segmentVersion = 7, tanMediaIdentifier = tanResponse.tanMediaIdentifier)
|
||||||
|
)
|
||||||
|
|
||||||
|
return createSignedMessageBuilderResult(context, MessageType.CheckDecoupledTanStatus, createSignedMessage(context, null, segments), segments)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun createBankTransferMessage(context: JobContext, data: BankTransferData, account: AccountData): MessageBuilderResult {
|
open fun createBankTransferMessage(context: JobContext, data: BankTransferData, account: AccountData): MessageBuilderResult {
|
||||||
|
|
||||||
|
@ -394,7 +423,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
return createSignedMessage(context, null, payloadSegments)
|
return createSignedMessage(context, null, payloadSegments)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun createSignedMessage(context: JobContext, tan: String? = null, payloadSegments: List<Segment>): String {
|
protected open fun createSignedMessage(context: JobContext, tan: String? = null, payloadSegments: List<Segment>): String {
|
||||||
|
|
||||||
val date = utils.formatDateTodayAsInt()
|
val date = utils.formatDateTodayAsInt()
|
||||||
val time = utils.formatTimeNowAsInt()
|
val time = utils.formatTimeNowAsInt()
|
||||||
|
@ -406,7 +435,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
return createMessage(context, encryptedPayload, payloadSegments.size)
|
return createMessage(context, encryptedPayload, payloadSegments.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun createMessage(context: JobContext, payloadSegments: List<Segment>, countWrappedSegments: Int = 0): String {
|
protected open fun createMessage(context: JobContext, payloadSegments: List<Segment>, countWrappedSegments: Int = 0): String {
|
||||||
|
|
||||||
val dialog = context.dialog
|
val dialog = context.dialog
|
||||||
dialog.increaseMessageNumber()
|
dialog.increaseMessageNumber()
|
||||||
|
@ -521,8 +550,10 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun createTwoStepTanSegment(context: JobContext, segmentId: CustomerSegmentId, segmentNumber: Int): ZweiSchrittTanEinreichung {
|
protected open fun createTwoStepTanSegment(context: JobContext, segmentId: CustomerSegmentId, segmentNumber: Int): ZweiSchrittTanEinreichung {
|
||||||
|
val bank = context.bank
|
||||||
|
|
||||||
return ZweiSchrittTanEinreichung(segmentNumber, TanProcess.TanProcess4, segmentId,
|
return ZweiSchrittTanEinreichung(segmentNumber, TanProcess.TanProcess4, segmentId,
|
||||||
tanMediaIdentifier = getTanMediaIdentifierIfRequired(context))
|
tanMediaIdentifier = getTanMediaIdentifierIfRequired(context), segmentVersion = bank.selectedTanMethod.hktanVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getTanMediaIdentifierIfRequired(context: JobContext): String? {
|
protected open fun getTanMediaIdentifierIfRequired(context: JobContext): String? {
|
||||||
|
@ -536,8 +567,7 @@ open class MessageBuilder(protected val utils: FinTsUtils = FinTsUtils()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun isTanRequiredForJob(context: JobContext, segmentId: CustomerSegmentId): Boolean {
|
protected open fun isTanRequiredForJob(context: JobContext, segmentId: CustomerSegmentId): Boolean {
|
||||||
return context.bank.pinInfo?.jobTanConfiguration?.first { it.segmentId == segmentId.id }?.tanRequired
|
return context.bank.doesJobRequireTan(segmentId)
|
||||||
?: false // TODO: actually in this case it's not allowed to execute job via PIN/TAN at all
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getSepaUrnFor(segmentId: CustomerSegmentId, bank: BankData, sepaDataFormat: String): String? {
|
protected open fun getSepaUrnFor(segmentId: CustomerSegmentId, bank: BankData, sepaDataFormat: String): String? {
|
|
@ -1,7 +1,9 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.segmente.Segment
|
import net.codinux.banking.fints.messages.datenelementgruppen.implementierte.Segmentkopf
|
||||||
import net.dankito.banking.fints.messages.segmente.implementierte.ZweiSchrittTanEinreichung
|
import net.codinux.banking.fints.messages.segmente.Segment
|
||||||
|
import net.codinux.banking.fints.messages.segmente.implementierte.Verarbeitungsvorbereitung
|
||||||
|
import net.codinux.banking.fints.messages.segmente.implementierte.ZweiSchrittTanEinreichung
|
||||||
|
|
||||||
|
|
||||||
open class MessageBuilderResult(
|
open class MessageBuilderResult(
|
||||||
|
@ -32,4 +34,10 @@ open class MessageBuilderResult(
|
||||||
&& messageBodySegments.first() is ZweiSchrittTanEinreichung
|
&& messageBodySegments.first() is ZweiSchrittTanEinreichung
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun isDialogInitMessage(): Boolean =
|
||||||
|
messageBodySegments.any { it is Verarbeitungsvorbereitung }
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString() = "${messageBodySegments.joinToString { (it.dataElementsAndGroups.firstOrNull() as? Segmentkopf)?.let { "${it.identifier}:${it.segmentVersion}" } ?: "<No Segment header>" } }}"
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
import net.dankito.banking.fints.util.MessageUtils
|
import net.codinux.banking.fints.util.MessageUtils
|
||||||
|
|
||||||
|
|
||||||
abstract class Nachrichtenteil(protected val messageUtils: MessageUtils = MessageUtils()) {
|
abstract class Nachrichtenteil(protected val messageUtils: MessageUtils = MessageUtils()) {
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages
|
package net.codinux.banking.fints.messages
|
||||||
|
|
||||||
|
|
||||||
class Separators {
|
class Separators {
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente
|
package net.codinux.banking.fints.messages.datenelemente
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
abstract class Datenelement(existenzstatus: Existenzstatus): DatenelementBase(existenzstatus) {
|
abstract class Datenelement(existenzstatus: Existenzstatus): DatenelementBase(existenzstatus) {
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
import net.codinux.banking.fints.messages.Nachrichtenteil
|
||||||
|
|
||||||
|
|
||||||
|
abstract class DatenelementBase(val existenzstatus: Existenzstatus) : Nachrichtenteil()
|
|
@ -1,8 +1,8 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,10 +1,10 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import net.dankito.banking.fints.extensions.toStringWithMinDigits
|
import net.codinux.banking.fints.extensions.toStringWithMinDigits
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,10 +1,10 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
package net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||||
|
|
||||||
import kotlinx.datetime.LocalTime
|
import kotlinx.datetime.LocalTime
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import net.dankito.banking.fints.extensions.toStringWithMinDigits
|
import net.codinux.banking.fints.extensions.toStringWithMinDigits
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.basisformate
|
package net.codinux.banking.fints.messages.datenelemente.basisformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.basisformate
|
package net.codinux.banking.fints.messages.datenelemente.basisformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.basisformate
|
package net.codinux.banking.fints.messages.datenelemente.basisformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,11 +1,11 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.basisformate
|
package net.codinux.banking.fints.messages.datenelemente.basisformate
|
||||||
|
|
||||||
import io.ktor.utils.io.charsets.encode
|
import io.ktor.utils.io.charsets.encode
|
||||||
import io.ktor.utils.io.charsets.name
|
import io.ktor.utils.io.charsets.name
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.HbciCharset
|
import net.codinux.banking.fints.messages.HbciCharset
|
||||||
import net.dankito.banking.fints.messages.Separators
|
import net.codinux.banking.fints.messages.Separators
|
||||||
import net.dankito.banking.fints.messages.datenelemente.Datenelement
|
import net.codinux.banking.fints.messages.datenelemente.Datenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.basisformate
|
package net.codinux.banking.fints.messages.datenelemente.basisformate
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
open class BezugssegmentInKreditinstitutsnachricht(segmentNumber: Int) : Bezugssegment(segmentNumber, Existenzstatus.Optional)
|
open class BezugssegmentInKreditinstitutsnachricht(segmentNumber: Int) : Bezugssegment(segmentNumber, Existenzstatus.Optional)
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
|
||||||
|
|
||||||
open class BezugssegmentInKundennachricht(segmentNumber: Int) : Bezugssegment(segmentNumber, Existenzstatus.NotAllowed)
|
open class BezugssegmentInKundennachricht(segmentNumber: Int) : Bezugssegment(segmentNumber, Existenzstatus.NotAllowed)
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -0,0 +1,21 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
|
||||||
|
enum class Dialogsprache(override val code: String) : ICodeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der Kunde darf lediglich ein Sprachkennzeichen einstellen, das im Rahmen
|
||||||
|
* der BPD vom Kreditinstitut an das Kundensystem übermittelt wurde.
|
||||||
|
* Wenn noch keine BPD vorliegen, sollte das Kundenprodukt mit Hilfe eines
|
||||||
|
* anonymen Dialogs die aktuelle BPD des Instituts ermitteln und die Standardsprache des Instituts einstellen, die in den Bankparameterdaten mitgeteilt
|
||||||
|
* wird. Falls die BPD nicht abgerufen werden kann, ist der Wert „0“ einzustellen. Das Kreditinstitut antwortet in diesem Fall in seiner Standardsprache.
|
||||||
|
*/
|
||||||
|
Default("0"),
|
||||||
|
|
||||||
|
German("1"),
|
||||||
|
|
||||||
|
English("2"),
|
||||||
|
|
||||||
|
French("3")
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.TextDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.TextDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
|
||||||
enum class HbciVersion(val versionNumber: Int, override val code: String) : ICodeEnum {
|
enum class HbciVersion(val versionNumber: Int, override val code: String) : ICodeEnum {
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified T : Enum<T>> allCodes(): List<String> {
|
inline fun <reified T : Enum<T>> allCodes(): List<String> {
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -0,0 +1,21 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information darüber, ob die Kundensystem-ID erforderlich ist.
|
||||||
|
*/
|
||||||
|
enum class KundensystemStatusWerte(override val code: String) : ICodeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kundensystem-ID wird nicht benötigt (HBCI DDV-Verfahren und
|
||||||
|
* chipkartenbasierte Verfahren ab Sicherheitsprofil-Version 3)
|
||||||
|
* und PinTan bis über HKSYN eine Kundensystem-ID vom Bankserver abgerufen wurde.
|
||||||
|
*/
|
||||||
|
NichtBenoetigt("0"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kundensystem-ID wird benötigt (sonstige HBCI RAH- /
|
||||||
|
* RDH- und PIN/TAN-Verfahren)
|
||||||
|
*/
|
||||||
|
Benoetigt("1")
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.TextDatenelement
|
||||||
|
|
||||||
|
|
||||||
|
open class NotAllowedDatenelement : TextDatenelement("", Existenzstatus.NotAllowed)
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,4 +1,4 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
|
|
||||||
enum class Synchronisierungsmodus(override val code: String) : ICodeEnum {
|
enum class Synchronisierungsmodus(override val code: String) : ICodeEnum {
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte
|
package net.codinux.banking.fints.messages.datenelemente.implementierte
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.account
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.JaNein
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.JaNein
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
|
open class BIC(bic: String, existenzstatus: Existenzstatus) : AlphanumerischesDatenelement(bic, existenzstatus, 11)
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
|
||||||
|
|
||||||
|
|
||||||
|
open class IBAN(iban: String, existenzstatus: Existenzstatus) : AlphanumerischesDatenelement(iban, existenzstatus, 34)
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.account
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,8 +1,8 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.account
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||||
import net.dankito.banking.fints.model.GetAccountTransactionsParameter
|
import net.codinux.banking.fints.model.GetAccountTransactionsParameter
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.account
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.account
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Identifikation
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
||||||
|
|
||||||
|
|
||||||
enum class BezeichnerFuerAlgorithmusparameterIV(override val code: String) : ICodeEnum {
|
enum class BezeichnerFuerAlgorithmusparameterIV(override val code: String) : ICodeEnum {
|
|
@ -1,8 +1,8 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.allCodes
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.allCodes
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
||||||
|
|
||||||
|
|
||||||
enum class BezeichnerFuerAlgorithmusparameterSchluessel(override val code: String) : ICodeEnum {
|
enum class BezeichnerFuerAlgorithmusparameterSchluessel(override val code: String) : ICodeEnum {
|
|
@ -1,8 +1,8 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.allCodes
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.allCodes
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
||||||
|
|
||||||
|
|
||||||
enum class Komprimierungsfunktion(val abbreviation: String, override val code: String) : ICodeEnum {
|
enum class Komprimierungsfunktion(val abbreviation: String, override val code: String) : ICodeEnum {
|
|
@ -1,8 +1,8 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
import net.codinux.banking.fints.messages.datenelemente.abgeleiteteformate.Code
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.allCodes
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.allCodes
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,7 +1,7 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.Existenzstatus
|
import net.codinux.banking.fints.messages.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.BinaerDatenelement
|
import net.codinux.banking.fints.messages.datenelemente.basisformate.BinaerDatenelement
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
package net.dankito.banking.fints.messages.datenelemente.implementierte.encryption
|
package net.codinux.banking.fints.messages.datenelemente.implementierte.encryption
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.ICodeEnum
|
||||||
|
|
||||||
|
|
||||||
enum class Verschluesselungsalgorithmus(override val code: String) : ICodeEnum {
|
enum class Verschluesselungsalgorithmus(override val code: String) : ICodeEnum {
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue