Created FinTsServerAddressFinder with BankListCreator to easily retrieve a FinTS server address to a bank code

This commit is contained in:
dankito 2022-02-21 01:07:00 +01:00
parent 73a98eea67
commit 3ed9e10c22
13 changed files with 1776 additions and 26 deletions

View File

@ -47,7 +47,7 @@ class FirstFragment : Fragment() {
} }
// TODO: set your credentials here // TODO: set your credentials here
presenter.retrieveAccountData("", "", "", "") { response -> presenter.retrieveAccountData("", "", "") { response ->
response.customerAccount?.let { customer -> response.customerAccount?.let { customer ->
accountTransactionsAdapter.items = customer.accounts.flatMap { it.bookedTransactions } accountTransactionsAdapter.items = customer.accounts.flatMap { it.bookedTransactions }
} }

View File

@ -34,9 +34,9 @@ open class Presenter {
open fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String, retrievedResult: (GetAccountDataResponse) -> Unit) { open fun retrieveAccountData(bankCode: String, loginName: String, password: String, retrievedResult: (GetAccountDataResponse) -> Unit) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
val response = fintsClient.getAccountData(GetAccountDataParameter(bankCode, customerId, pin, finTs3ServerAddress)) val response = fintsClient.getAccountData(GetAccountDataParameter(bankCode, loginName, password))
log.info("Retrieved response from ${response.customerAccount?.bankName} for ${response.customerAccount?.customerName}") log.info("Retrieved response from ${response.customerAccount?.bankName} for ${response.customerAccount?.customerName}")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {

View File

@ -27,7 +27,7 @@ class AccountTransactionsView(props: AccountTransactionsViewProps) : RComponent<
// due to CORS your bank's servers can not be requested directly from browser -> set a CORS proxy url in main.kt // due to CORS your bank's servers can not be requested directly from browser -> set a CORS proxy url in main.kt
// TODO: set your credentials here // TODO: set your credentials here
GlobalScope.launch { GlobalScope.launch {
props.presenter.retrieveAccountData("", "", "", "") { response -> props.presenter.retrieveAccountData("", "", "") { response ->
response.customerAccount?.let { customer -> response.customerAccount?.let { customer ->
val balance = customer.accounts.sumOf { it.balance?.amount?.string?.replace(',', '.')?.toDoubleOrNull() ?: 0.0 } // i know, double is not an appropriate data type for amounts val balance = customer.accounts.sumOf { it.balance?.amount?.string?.replace(',', '.')?.toDoubleOrNull() ?: 0.0 } // i know, double is not an appropriate data type for amounts

View File

@ -32,9 +32,9 @@ open class Presenter {
} }
open fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String, retrievedResult: (GetAccountDataResponse) -> Unit) { open fun retrieveAccountData(bankCode: String, loginName: String, password: String, retrievedResult: (GetAccountDataResponse) -> Unit) {
GlobalScope.launch(Dispatchers.Unconfined) { GlobalScope.launch(Dispatchers.Unconfined) {
val response = fintsClient.getAccountData(GetAccountDataParameter(bankCode, customerId, pin, finTs3ServerAddress)) val response = fintsClient.getAccountData(GetAccountDataParameter(bankCode, loginName, password))
log.info("Retrieved response from ${response.customerAccount?.bankName} for ${response.customerAccount?.customerName}") log.info("Retrieved response from ${response.customerAccount?.bankName} for ${response.customerAccount?.customerName}")

View File

@ -51,7 +51,7 @@ struct ContentView: View {
private func retrieveTransactions() { private func retrieveTransactions() {
// TODO: set your credentials here // TODO: set your credentials here
self.presenter.getAccountData("", "", "", "", self.handleGetAccountDataResponse) self.presenter.getAccountData("", "", "", self.handleGetAccountDataResponse)
} }
private func handleGetAccountDataResponse(_ response: GetAccountDataResponse) { private func handleGetAccountDataResponse(_ response: GetAccountDataResponse) {

View File

@ -11,7 +11,7 @@ open class CustomerAccount(
override var bankCode: String, override var bankCode: String,
override var loginName: String, override var loginName: String,
override var password: String, override var password: String,
override var finTsServerAddress: String, open var finTsServerAddress: String,
open var bankName: String, open var bankName: String,
open var bic: String, open var bic: String,
@ -25,7 +25,7 @@ open class CustomerAccount(
open var selectedTanMethod: TanMethod? = null, open var selectedTanMethod: TanMethod? = null,
open var tanMedia: List<TanMedium> = listOf(), open var tanMedia: List<TanMedium> = listOf(),
open var selectedTanMedium: TanMedium? = null, open var selectedTanMedium: TanMedium? = null,
) : CustomerCredentials(bankCode, loginName, password, finTsServerAddress) { ) : CustomerCredentials(bankCode, loginName, password) {
override fun toString(): String { override fun toString(): String {

View File

@ -4,10 +4,9 @@ package net.dankito.banking.client.model
open class CustomerCredentials( open class CustomerCredentials(
open val bankCode: String, open val bankCode: String,
open val loginName: String, open val loginName: String,
open val password: String, open val password: String
open val finTsServerAddress: String // TODO: get rid of this
) { ) {
internal constructor() : this("", "", "", "") // for object deserializers internal constructor() : this("", "", "") // for object deserializers
} }

View File

@ -10,10 +10,9 @@ open class FinTsClientParameter(
bankCode: String, bankCode: String,
loginName: String, loginName: String,
password: String, password: String,
finTsServerAddress: String, // TODO: get rid of this
open val preferredTanMethods: List<TanMethodType>? = null, open val preferredTanMethods: List<TanMethodType>? = null,
open val preferredTanMedium: String? = null, // the ID of the medium open val preferredTanMedium: String? = null, // the ID of the medium
open val abortIfTanIsRequired: Boolean = false, open val abortIfTanIsRequired: Boolean = false,
open val finTsModel: BankData? = null open val finTsModel: BankData? = null
) : CustomerCredentials(bankCode, loginName, password, finTsServerAddress) ) : CustomerCredentials(bankCode, loginName, password)

View File

@ -10,7 +10,6 @@ open class GetAccountDataParameter(
bankCode: String, bankCode: String,
loginName: String, loginName: String,
password: String, password: String,
finTsServerAddress: String, // TODO: get rid of this
/** /**
* Optionally specify for which bank account to retrieve the account data. * Optionally specify for which bank account to retrieve the account data.
* If not set the data for all bank accounts of this account will be retrieved. * If not set the data for all bank accounts of this account will be retrieved.
@ -25,7 +24,7 @@ open class GetAccountDataParameter(
preferredTanMedium: String? = null, preferredTanMedium: String? = null,
abortIfTanIsRequired: Boolean = false, abortIfTanIsRequired: Boolean = false,
finTsModel: BankData? = null finTsModel: BankData? = null
) : FinTsClientParameter(bankCode, loginName, password, finTsServerAddress, preferredTanMethods, preferredTanMedium, abortIfTanIsRequired, finTsModel) { ) : FinTsClientParameter(bankCode, loginName, password, preferredTanMethods, preferredTanMedium, abortIfTanIsRequired, finTsModel) {
open val retrieveOnlyAccountInfo: Boolean open val retrieveOnlyAccountInfo: Boolean
get() = retrieveBalance == false && retrieveTransactions == RetrieveTransactions.No get() = retrieveBalance == false && retrieveTransactions == RetrieveTransactions.No

View File

@ -3,6 +3,8 @@ package net.dankito.banking.client.model.response
enum class ErrorCode { enum class ErrorCode {
BankDoesNotSupportFinTs3,
InternalError, InternalError,
BankReturnedError, BankReturnedError,

View File

@ -12,6 +12,7 @@ import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.fints.response.client.GetAccountInfoResponse import net.dankito.banking.fints.response.client.GetAccountInfoResponse
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
import net.dankito.banking.fints.response.segments.AccountType import net.dankito.banking.fints.response.segments.AccountType
import net.dankito.banking.fints.util.FinTsServerAddressFinder
import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.utils.multiplatform.extensions.minusDays import net.dankito.utils.multiplatform.extensions.minusDays
import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin
@ -21,6 +22,7 @@ import kotlin.jvm.JvmOverloads
open class FinTsClient @JvmOverloads constructor( open class FinTsClient @JvmOverloads constructor(
open var callback: FinTsClientCallback, open var callback: FinTsClientCallback,
protected open val jobExecutor: FinTsJobExecutor = FinTsJobExecutor(), protected open val jobExecutor: FinTsJobExecutor = FinTsJobExecutor(),
protected open val finTsServerAddressFinder: FinTsServerAddressFinder = FinTsServerAddressFinder(),
protected open val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically protected open val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically
) { ) {
@ -29,16 +31,19 @@ open class FinTsClient @JvmOverloads constructor(
} }
constructor(callback: FinTsClientCallback) : this(callback, FinTsJobExecutor()) // Swift does not support default parameter values -> create constructor overloads constructor(callback: FinTsClientCallback, webClient: IWebClient) : this(callback, FinTsJobExecutor(RequestExecutor(webClient = webClient))) // Swift does not support default parameter values -> create constructor overloads
constructor(callback: FinTsClientCallback, webClient: IWebClient) : this(callback, FinTsJobExecutor(RequestExecutor(webClient = webClient)))
protected open val mapper = FinTsModelMapper() protected open val mapper = FinTsModelMapper()
open suspend fun getAccountData(param: GetAccountDataParameter): GetAccountDataResponse { open suspend fun getAccountData(param: GetAccountDataParameter): GetAccountDataResponse {
val bank = BankData(param.bankCode, param.loginName, param.password, param.finTsServerAddress, "") val finTsServerAddress = finTsServerAddressFinder.findFinTsServerAddress(param.bankCode)
if (finTsServerAddress.isNullOrBlank()) {
return GetAccountDataResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not FinTS 3.0 or we don't know its FinTS server address", null, listOf())
}
val bank = BankData(param.bankCode, param.loginName, param.password, finTsServerAddress, "")
val accounts = param.accounts val accounts = param.accounts
if (accounts.isNullOrEmpty() || param.retrieveOnlyAccountInfo) { // then first retrieve customer's bank accounts if (accounts.isNullOrEmpty() || param.retrieveOnlyAccountInfo) { // then first retrieve customer's bank accounts

View File

@ -10,22 +10,22 @@ import net.dankito.utils.multiplatform.extensions.*
import platform.posix.exit import platform.posix.exit
fun main(args: Array<String>) { fun main(args: Array<String>) {
if (args.size < 4) { if (args.size < 3) {
println("Bitte geben Sie Ihre Bankzugangsdaten ein in der Reihenfolge: <Bankleitzahl> <Login name> <Password> <FinTS Serveradresse der Bank>\r\n" + println("Bitte geben Sie Ihre Bankzugangsdaten ein in der Reihenfolge: <Bankleitzahl> <Login name> <Password>\r\n" +
"Z. B.: ./fints4k.kexe 10050000 \"Mein Loginname\" GeheimesPasswort \"https://banking-be3.s-fints-pt-be.de/fints30\"") "Z. B.: ./fints4k.kexe 10050000 \"Mein Loginname\" GeheimesPasswort")
exit(0) exit(0)
} }
Application().retrieveAccountData(args[0], args[1], args[2], args[3]) Application().retrieveAccountData(args[0], args[1], args[2])
} }
class Application { class Application {
fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String) { fun retrieveAccountData(bankCode: String, loginName: String, password: String) {
runBlocking { runBlocking {
val client = FinTsClient(SimpleFinTsClientCallback { tanChallenge -> enterTan(tanChallenge) }) val client = FinTsClient(SimpleFinTsClientCallback { tanChallenge -> enterTan(tanChallenge) })
val response = client.getAccountData(GetAccountDataParameter(bankCode, customerId, pin, finTs3ServerAddress)) val response = client.getAccountData(GetAccountDataParameter(bankCode, loginName, password))
if (response.error != null) { if (response.error != null) {
println("An error occurred: ${response.error}${response.errorMessage?.let { " $it" }}") println("An error occurred: ${response.error}${response.errorMessage?.let { " $it" }}")