Created BicFinder with BankFinder so that we can determine a German BIC from bank code or a German IBAN

This commit is contained in:
dankito 2022-02-23 02:59:10 +01:00
parent 8671bf058d
commit ad826941dd
5 changed files with 3556 additions and 13 deletions

View File

@ -23,6 +23,8 @@ enum class ErrorCode {
DidNotRetrieveAllAccountData,
CanNotDetermineBicForIban,
NoAccountSupportsMoneyTransfer,
MoreThanOneAccountSupportsMoneyTransfer

View File

@ -15,6 +15,7 @@ import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.fints.response.client.GetAccountInfoResponse
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
import net.dankito.banking.fints.response.segments.AccountType
import net.dankito.banking.fints.util.BicFinder
import net.dankito.banking.fints.util.FinTsServerAddressFinder
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.utils.multiplatform.extensions.minusDays
@ -39,6 +40,8 @@ open class FinTsClient @JvmOverloads constructor(
protected open val mapper = FinTsModelMapper()
protected open val bicFinder = BicFinder()
open suspend fun getAccountDataAsync(bankCode: String, loginName: String, password: String): GetAccountDataResponse {
return getAccountDataAsync(GetAccountDataParameter(bankCode, loginName, password))
@ -50,7 +53,7 @@ open class FinTsClient @JvmOverloads constructor(
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 bank = mapper.mapToBankData(param, finTsServerAddress)
val accounts = param.accounts
if (accounts.isNullOrEmpty() || param.retrieveOnlyAccountInfo) { // then first retrieve customer's bank accounts
@ -118,8 +121,14 @@ open class FinTsClient @JvmOverloads constructor(
if (finTsServerAddress.isNullOrBlank()) {
return TransferMoneyResponse(ErrorCode.BankDoesNotSupportFinTs3, "Either bank does not FinTS 3.0 or we don't know its FinTS server address", listOf(), null)
}
val recipientBankIdentifier = getRecipientBankCode(param)
if (recipientBankIdentifier == null) {
return TransferMoneyResponse(ErrorCode.CanNotDetermineBicForIban, "We can only determine recipient's BIC automatically for German IBANs. If it's a German IBAN, either we " +
"cannot extract the bank code from IBAN ${param.recipientAccountIdentifier} (fourth to twelfth position) or don't know the BIC to this bank code. Please specify recipient's IBAN explicitly.", listOf())
}
val bank = BankData(param.bankCode, param.loginName, param.password, finTsServerAddress, "")
val bank = mapper.mapToBankData(param, finTsServerAddress)
val remittanceAccount = param.remittanceAccount
if (remittanceAccount == null) { // then first retrieve customer's bank accounts
@ -129,14 +138,14 @@ open class FinTsClient @JvmOverloads constructor(
return TransferMoneyResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
getAccountInfoResponse.messageLogWithoutSensitiveData, bank)
} else {
return transferMoneyAsync(param, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
return transferMoneyAsync(param, recipientBankIdentifier, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
}
} else {
return transferMoneyAsync(param, bank, listOf(mapper.mapToAccountData(remittanceAccount, param)), null)
return transferMoneyAsync(param, recipientBankIdentifier, bank, listOf(mapper.mapToAccountData(remittanceAccount, param)), null)
}
}
protected open suspend fun transferMoneyAsync(param: TransferMoneyParameter, bank: BankData, accounts: List<AccountData>, previousJobResponse: FinTsClientResponse?): TransferMoneyResponse {
protected open suspend fun transferMoneyAsync(param: TransferMoneyParameter, recipientBankIdentifier: String, bank: BankData, accounts: List<AccountData>, previousJobResponse: FinTsClientResponse?): TransferMoneyResponse {
val accountsSupportingTransfer = accounts.filter { it.supportsTransferringMoney }
if (accountsSupportingTransfer.isEmpty()) {
return TransferMoneyResponse(ErrorCode.NoAccountSupportsMoneyTransfer, "None of the accounts $accounts supports money transfer", previousJobResponse?.messageLogWithoutSensitiveData ?: listOf(), bank)
@ -144,14 +153,27 @@ open class FinTsClient @JvmOverloads constructor(
return TransferMoneyResponse(ErrorCode.MoreThanOneAccountSupportsMoneyTransfer, "More than one of the accounts $accountsSupportingTransfer supports money transfer, so we cannot clearly determine which one to use for this transfer", previousJobResponse?.messageLogWithoutSensitiveData ?: listOf(), bank)
}
val recipientBankIdentifier = param.recipientBankIdentifier ?: "" // TODO: determine BIC from recipientBankCode if it's a German bank
val context = JobContext(JobContextType.TransferMoney, this.callback, product, bank, accountsSupportingTransfer.first())
val response = jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier, param.amount, param.reference, param.instantPayment))
val response = jobExecutor.transferMoneyAsync(context, BankTransferData(param.recipientName, param.recipientAccountIdentifier, recipientBankIdentifier,
param.amount, param.reference, param.instantPayment))
return TransferMoneyResponse(mapper.mapErrorCode(response), mapper.mapErrorMessages(response), mapper.mergeMessageLog(previousJobResponse, response), bank)
}
private fun getRecipientBankCode(param: TransferMoneyParameter): String? {
param.recipientBankIdentifier?.let { return it }
val probablyIban = param.recipientAccountIdentifier.replace(" ", "")
if (probablyIban.length > 12) {
val bankCode = probablyIban.substring(4, 4 + 8) // extract bank code from IBAN. For German IBAN bank code starts at fourth position and has 8 digits
bicFinder.findBic(bankCode)?.let { return it }
}
return null
}
protected open suspend fun getAccountInfo(param: FinTsClientParameter, bank: BankData): GetAccountInfoResponse {
param.finTsModel?.let {
// TODO: implement

View File

@ -10,10 +10,18 @@ import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.fints.response.client.GetAccountTransactionsResponse
import net.dankito.banking.fints.response.segments.AccountType
import net.dankito.banking.fints.util.BicFinder
open class FinTsModelMapper {
protected open val bicFinder = BicFinder()
open fun mapToBankData(param: FinTsClientParameter, finTsServerAddress: String): BankData {
return BankData(param.bankCode, param.loginName, param.password, finTsServerAddress, bicFinder.findBic(param.bankCode) ?: "")
}
open fun mapToAccountData(credentials: BankAccountIdentifier, param: FinTsClientParameter): AccountData {
val accountData = AccountData(credentials.identifier, credentials.subAccountNumber, Laenderkennzeichen.Germany, param.bankCode,
credentials.iban, param.loginName, null, null, "", null, null, listOf(), listOf())

View File

@ -55,12 +55,6 @@ open class ModelMapper(
}
}
response.getFirstSegmentById<SepaAccountInfo>(InstituteSegmentId.SepaAccountInfo)?.let { sepaAccountInfo ->
sepaAccountInfo.account.bic?.let {
bank.bic = it // TODO: really set BIC on bank then?
}
}
response.getFirstSegmentById<ChangeTanMediaParameters>(InstituteSegmentId.ChangeTanMediaParameters)?.let { parameters ->
bank.changeTanMediumParameters = parameters
}

File diff suppressed because it is too large Load Diff