Merged CustomerData and BankData
This commit is contained in:
parent
49d34f1733
commit
63259ff404
|
@ -56,9 +56,9 @@ open class FinTsClient(
|
||||||
|
|
||||||
protected val messageLogField = ArrayList<MessageLogEntry>() // TODO: make thread safe like with CopyOnWriteArrayList
|
protected val messageLogField = ArrayList<MessageLogEntry>() // TODO: make thread safe like with CopyOnWriteArrayList
|
||||||
|
|
||||||
// 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 CustomerData
|
// 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 messageLogWithoutSensitiveData: List<MessageLogEntry>
|
open val messageLogWithoutSensitiveData: List<MessageLogEntry>
|
||||||
get() = messageLogField.map { MessageLogEntry(removeSensitiveDataFromMessage(it.message, it.customer), it.time, it.customer) }
|
get() = messageLogField.map { MessageLogEntry(removeSensitiveDataFromMessage(it.message, it.bank), it.time, it.bank) }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +81,7 @@ open class FinTsClient(
|
||||||
* On success [bank] parameter is updated afterwards.
|
* On success [bank] parameter is updated afterwards.
|
||||||
*/
|
*/
|
||||||
open fun getAnonymousBankInfo(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
open fun getAnonymousBankInfo(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
val dialogContext = DialogContext(bank, CustomerData.Anonymous, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
|
|
||||||
val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext)
|
val message = messageBuilder.createAnonymousDialogInitMessage(dialogContext)
|
||||||
|
|
||||||
|
@ -109,21 +109,21 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun getUsersTanProcedures(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
|
open fun getUsersTanProcedures(bank: BankData, callback: (AddAccountResponse) -> Unit) {
|
||||||
// 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),
|
||||||
// user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure())
|
// user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure())
|
||||||
bank.resetBpdVersion()
|
bank.resetBpdVersion()
|
||||||
customer.resetUpdVersion()
|
bank.resetUpdVersion()
|
||||||
/**
|
/**
|
||||||
* Sind dem Kundenprodukt die konkreten, für den Benutzer zugelassenen Sicherheitsverfahren nicht bekannt, so können
|
* Sind dem Kundenprodukt die konkreten, für den Benutzer zugelassenen Sicherheitsverfahren nicht bekannt, so können
|
||||||
* diese über eine Dialoginitialisierung mit Sicherheitsfunktion=999 angefordert werden. Die konkreten Verfahren
|
* diese über eine Dialoginitialisierung mit Sicherheitsfunktion=999 angefordert werden. Die konkreten Verfahren
|
||||||
* werden dann über den Rückmeldungscode=3920 zurückgemeldet. Im Rahmen dieses Prozesses darf keine UPD
|
* werden dann über den Rückmeldungscode=3920 zurückgemeldet. Im Rahmen dieses Prozesses darf keine UPD
|
||||||
* zurückgeliefert werden und die Durchführung anderer Geschäftsvorfälle ist in einem solchen Dialog nicht erlaubt.
|
* zurückgeliefert werden und die Durchführung anderer Geschäftsvorfälle ist in einem solchen Dialog nicht erlaubt.
|
||||||
*/
|
*/
|
||||||
customer.resetSelectedTanProcedure()
|
bank.resetSelectedTanProcedure()
|
||||||
|
|
||||||
// this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN procedures
|
// this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN procedures
|
||||||
val dialogContext = DialogContext(bank, customer, product, versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1)
|
val dialogContext = DialogContext(bank, product, versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1)
|
||||||
|
|
||||||
val message = messageBuilder.createInitDialogMessage(dialogContext)
|
val message = messageBuilder.createInitDialogMessage(dialogContext)
|
||||||
|
|
||||||
|
@ -139,15 +139,15 @@ open class FinTsClient(
|
||||||
|
|
||||||
if (getUsersTanProceduresResponse.successful) { // TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part
|
if (getUsersTanProceduresResponse.successful) { // TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part
|
||||||
updateBankData(dialogContext.bank, getUsersTanProceduresResponse)
|
updateBankData(dialogContext.bank, getUsersTanProceduresResponse)
|
||||||
updateCustomerData(dialogContext.customer, dialogContext.bank, getUsersTanProceduresResponse)
|
updateCustomerData(dialogContext.bank, getUsersTanProceduresResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999'
|
// even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999'
|
||||||
if (bankDoesNotSupportRetrievingUsersTanProcedures(getUsersTanProceduresResponse)) {
|
if (bankDoesNotSupportRetrievingUsersTanProcedures(getUsersTanProceduresResponse)) {
|
||||||
getBankAndCustomerInfoForNewUserViaAnonymousDialog(dialogContext.bank, dialogContext.customer, callback) // TODO: should not be necessary anymore
|
getBankAndCustomerInfoForNewUserViaAnonymousDialog(dialogContext.bank, callback) // TODO: should not be necessary anymore
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
callback(AddAccountResponse(getUsersTanProceduresResponse, dialogContext.bank, dialogContext.customer))
|
callback(AddAccountResponse(getUsersTanProceduresResponse, dialogContext.bank))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,44 +158,44 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is only a quick fix. Find a better and general solution
|
// TODO: this is only a quick fix. Find a better and general solution
|
||||||
protected open fun getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
|
protected open fun getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank: BankData, callback: (AddAccountResponse) -> Unit) {
|
||||||
getAnonymousBankInfo(bank) { anonymousBankInfoResponse ->
|
getAnonymousBankInfo(bank) { anonymousBankInfoResponse ->
|
||||||
if (anonymousBankInfoResponse.isSuccessful == false) {
|
if (anonymousBankInfoResponse.isSuccessful == false) {
|
||||||
callback(AddAccountResponse(anonymousBankInfoResponse.toResponse(), bank, customer))
|
callback(AddAccountResponse(anonymousBankInfoResponse.toResponse(), bank))
|
||||||
}
|
}
|
||||||
else if (bank.supportedTanProcedures.isEmpty()) { // should only be a theoretical error
|
else if (bank.tanProceduresSupportedByBank.isEmpty()) { // should only be a theoretical error
|
||||||
callback(AddAccountResponse(Response(true,
|
callback(AddAccountResponse(Response(true,
|
||||||
errorMessage = "Die TAN Verfahren der Bank konnten nicht ermittelt werden"), bank, customer)) // TODO: translate
|
errorMessage = "Die TAN Verfahren der Bank konnten nicht ermittelt werden"), bank)) // TODO: translate
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
customer.supportedTanProcedures = bank.supportedTanProcedures
|
bank.tanProceduresAvailableForUser = bank.tanProceduresSupportedByBank
|
||||||
getUsersTanProcedure(customer)
|
getUsersTanProcedure(bank)
|
||||||
|
|
||||||
val dialogContext = DialogContext(bank, customer, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
|
|
||||||
initDialogAfterSuccessfulChecks(dialogContext) { initDialogResponse ->
|
initDialogAfterSuccessfulChecks(dialogContext) { initDialogResponse ->
|
||||||
closeDialog(dialogContext)
|
closeDialog(dialogContext)
|
||||||
|
|
||||||
callback(AddAccountResponse(initDialogResponse, bank, customer))
|
callback(AddAccountResponse(initDialogResponse, bank))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun getAccounts(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) {
|
protected open fun getAccounts(bank: BankData, callback: (AddAccountResponse) -> Unit) {
|
||||||
|
|
||||||
val dialogContext = DialogContext(bank, customer, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
|
|
||||||
initDialogAfterSuccessfulChecks(dialogContext) { response ->
|
initDialogAfterSuccessfulChecks(dialogContext) { response ->
|
||||||
closeDialog(dialogContext)
|
closeDialog(dialogContext)
|
||||||
|
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
updateBankData(bank, response)
|
updateBankData(bank, response)
|
||||||
updateCustomerData(customer, bank, response)
|
updateCustomerData(bank, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(AddAccountResponse(response, bank, customer))
|
callback(AddAccountResponse(response, bank))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,15 +211,15 @@ open class FinTsClient(
|
||||||
*
|
*
|
||||||
* If you change customer system id during a dialog your messages get rejected by bank institute.
|
* If you change customer system id during a dialog your messages get rejected by bank institute.
|
||||||
*/
|
*/
|
||||||
protected open fun synchronizeCustomerSystemId(bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) {
|
protected open fun synchronizeCustomerSystemId(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
|
|
||||||
val dialogContext = DialogContext(bank, customer, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext)
|
val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(dialogContext)
|
||||||
|
|
||||||
getAndHandleResponseForMessage(message, dialogContext) { response ->
|
getAndHandleResponseForMessage(message, dialogContext) { response ->
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
updateBankData(bank, response)
|
updateBankData(bank, response)
|
||||||
updateCustomerData(customer, bank, response)
|
updateCustomerData(bank, response)
|
||||||
|
|
||||||
closeDialog(dialogContext)
|
closeDialog(dialogContext)
|
||||||
}
|
}
|
||||||
|
@ -229,15 +229,14 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun addAccountAsync(bank: BankData, customer: CustomerData,
|
open fun addAccountAsync(bank: BankData, callback: (AddAccountResponse) -> Unit) {
|
||||||
callback: (AddAccountResponse) -> Unit) {
|
|
||||||
|
|
||||||
val originalAreWeThatGentleToCloseDialogs = areWeThatGentleToCloseDialogs
|
val originalAreWeThatGentleToCloseDialogs = areWeThatGentleToCloseDialogs
|
||||||
areWeThatGentleToCloseDialogs = false
|
areWeThatGentleToCloseDialogs = false
|
||||||
|
|
||||||
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN procedures */
|
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN procedures */
|
||||||
|
|
||||||
getUsersTanProcedures(bank, customer) { newUserInfoResponse ->
|
getUsersTanProcedures(bank) { newUserInfoResponse ->
|
||||||
|
|
||||||
if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
||||||
callback(newUserInfoResponse)
|
callback(newUserInfoResponse)
|
||||||
|
@ -247,25 +246,25 @@ open class FinTsClient(
|
||||||
|
|
||||||
// do not ask user for tan at this stage
|
// do not ask user for tan at this stage
|
||||||
var didOverwriteUserUnselectedTanProcedure = false
|
var didOverwriteUserUnselectedTanProcedure = false
|
||||||
if (customer.isTanProcedureSelected == false && customer.supportedTanProcedures.isNotEmpty()) {
|
if (bank.isTanProcedureSelected == false && bank.tanProceduresAvailableForUser.isNotEmpty()) {
|
||||||
|
|
||||||
if (customer.supportedTanProcedures.size == 1) { // user has only one TAN procedure -> set it and we're done
|
if (bank.tanProceduresAvailableForUser.size == 1) { // user has only one TAN procedure -> set it and we're done
|
||||||
customer.selectedTanProcedure = customer.supportedTanProcedures.first()
|
bank.selectedTanProcedure = bank.tanProceduresAvailableForUser.first()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
didOverwriteUserUnselectedTanProcedure = true
|
didOverwriteUserUnselectedTanProcedure = true
|
||||||
customer.selectedTanProcedure = selectSuggestedTanProcedure(customer) ?: customer.supportedTanProcedures.first()
|
bank.selectedTanProcedure = selectSuggestedTanProcedure(bank) ?: bank.tanProceduresAvailableForUser.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Second dialgo: some banks require that in order to initialize a dialog with strong customer authorization TAN media is required */
|
/* Second dialgo: some banks require that in order to initialize a dialog with strong customer authorization TAN media is required */
|
||||||
|
|
||||||
getTanMediaList(bank, customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) {
|
getTanMediaList(bank, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) {
|
||||||
|
|
||||||
/* Third dialog: Now we can initialize our first dialog with strong customer authorization. Use it to get UPD and customer's accounts */
|
/* Third dialog: Now we can initialize our first dialog with strong customer authorization. Use it to get UPD and customer's accounts */
|
||||||
|
|
||||||
getAccounts(bank, customer) { getAccountsResponse ->
|
getAccounts(bank) { getAccountsResponse ->
|
||||||
|
|
||||||
if (getAccountsResponse.isSuccessful == false) {
|
if (getAccountsResponse.isSuccessful == false) {
|
||||||
callback(getAccountsResponse)
|
callback(getAccountsResponse)
|
||||||
|
@ -274,48 +273,48 @@ open class FinTsClient(
|
||||||
|
|
||||||
/* Fourth dialog: Try to retrieve account balances and transactions of last 90 days without TAN */
|
/* Fourth dialog: Try to retrieve account balances and transactions of last 90 days without TAN */
|
||||||
|
|
||||||
addAccountGetAccountBalancesAndTransactions(customer, bank, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure,
|
addAccountGetAccountBalancesAndTransactions(bank, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure,
|
||||||
originalAreWeThatGentleToCloseDialogs, callback)
|
originalAreWeThatGentleToCloseDialogs, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun addAccountGetAccountBalancesAndTransactions(customer: CustomerData, bank: BankData, newUserInfoResponse: AddAccountResponse,
|
protected open fun addAccountGetAccountBalancesAndTransactions(bank: BankData, newUserInfoResponse: AddAccountResponse,
|
||||||
didOverwriteUserUnselectedTanProcedure: Boolean, originalAreWeThatGentleToCloseDialogs: Boolean,
|
didOverwriteUserUnselectedTanProcedure: Boolean, originalAreWeThatGentleToCloseDialogs: Boolean,
|
||||||
callback: (AddAccountResponse) -> Unit) {
|
callback: (AddAccountResponse) -> Unit) {
|
||||||
val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>()
|
val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>()
|
||||||
val balances = mutableMapOf<AccountData, Money>()
|
val balances = mutableMapOf<AccountData, Money>()
|
||||||
|
|
||||||
val accountSupportingRetrievingTransactions = customer.accounts.filter { it.supportsFeature(AccountFeature.RetrieveBalance) || it.supportsFeature(AccountFeature.RetrieveAccountTransactions) }
|
val accountSupportingRetrievingTransactions = bank.accounts.filter { it.supportsFeature(AccountFeature.RetrieveBalance) || it.supportsFeature(AccountFeature.RetrieveAccountTransactions) }
|
||||||
val countAccountSupportingRetrievingTransactions = accountSupportingRetrievingTransactions.size
|
val countAccountSupportingRetrievingTransactions = accountSupportingRetrievingTransactions.size
|
||||||
var countRetrievedAccounts = 0
|
var countRetrievedAccounts = 0
|
||||||
|
|
||||||
if (countAccountSupportingRetrievingTransactions == 0) {
|
if (countAccountSupportingRetrievingTransactions == 0) {
|
||||||
addAccountAfterRetrievingTransactions(bank, customer, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure,
|
addAccountAfterRetrievingTransactions(bank, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure,
|
||||||
originalAreWeThatGentleToCloseDialogs, transactionsOfLast90DaysResponses, balances, callback)
|
originalAreWeThatGentleToCloseDialogs, transactionsOfLast90DaysResponses, balances, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountSupportingRetrievingTransactions.forEach { account ->
|
accountSupportingRetrievingTransactions.forEach { account ->
|
||||||
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account) { response ->
|
tryGetTransactionsOfLast90DaysWithoutTan(bank, account) { response ->
|
||||||
transactionsOfLast90DaysResponses.add(response)
|
transactionsOfLast90DaysResponses.add(response)
|
||||||
response.balance?.let { balances.put(account, it) }
|
response.balance?.let { balances.put(account, it) }
|
||||||
|
|
||||||
countRetrievedAccounts++
|
countRetrievedAccounts++
|
||||||
if (countRetrievedAccounts == countAccountSupportingRetrievingTransactions) {
|
if (countRetrievedAccounts == countAccountSupportingRetrievingTransactions) {
|
||||||
addAccountAfterRetrievingTransactions(bank, customer, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure, originalAreWeThatGentleToCloseDialogs,
|
addAccountAfterRetrievingTransactions(bank, newUserInfoResponse, didOverwriteUserUnselectedTanProcedure, originalAreWeThatGentleToCloseDialogs,
|
||||||
transactionsOfLast90DaysResponses, balances, callback)
|
transactionsOfLast90DaysResponses, balances, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun addAccountAfterRetrievingTransactions(bank: BankData, customer: CustomerData, newUserInfoResponse: AddAccountResponse,
|
protected open fun addAccountAfterRetrievingTransactions(bank: BankData, newUserInfoResponse: AddAccountResponse,
|
||||||
didOverwriteUserUnselectedTanProcedure: Boolean, originalAreWeThatGentleToCloseDialogs: Boolean,
|
didOverwriteUserUnselectedTanProcedure: Boolean, originalAreWeThatGentleToCloseDialogs: Boolean,
|
||||||
transactionsOfLast90DaysResponses: MutableList<GetTransactionsResponse>,
|
transactionsOfLast90DaysResponses: MutableList<GetTransactionsResponse>,
|
||||||
balances: MutableMap<AccountData, Money>, callback: (AddAccountResponse) -> Unit) {
|
balances: MutableMap<AccountData, Money>, callback: (AddAccountResponse) -> Unit) {
|
||||||
if (didOverwriteUserUnselectedTanProcedure) {
|
if (didOverwriteUserUnselectedTanProcedure) {
|
||||||
customer.resetSelectedTanProcedure()
|
bank.resetSelectedTanProcedure()
|
||||||
}
|
}
|
||||||
|
|
||||||
areWeThatGentleToCloseDialogs = originalAreWeThatGentleToCloseDialogs
|
areWeThatGentleToCloseDialogs = originalAreWeThatGentleToCloseDialogs
|
||||||
|
@ -324,8 +323,9 @@ open class FinTsClient(
|
||||||
val unbookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.unbookedTransactions }
|
val unbookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.unbookedTransactions }
|
||||||
val bookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.bookedTransactions }
|
val bookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.bookedTransactions }
|
||||||
|
|
||||||
callback(AddAccountResponse(newUserInfoResponse.toResponse(), bank, customer,
|
// TODO: to evaluate if adding account has been successful also check if count accounts > 0
|
||||||
supportsRetrievingTransactionsOfLast90DaysWithoutTan, bookedTransactions, unbookedTransactions, balances))
|
callback(AddAccountResponse(newUserInfoResponse.toResponse(), bank, supportsRetrievingTransactionsOfLast90DaysWithoutTan,
|
||||||
|
bookedTransactions, unbookedTransactions, balances))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -335,20 +335,20 @@ open class FinTsClient(
|
||||||
*
|
*
|
||||||
* Check if bank supports this.
|
* Check if bank supports this.
|
||||||
*/
|
*/
|
||||||
open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
open fun tryGetTransactionsOfLast90DaysWithoutTan(bank: BankData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
||||||
|
|
||||||
val now = Date()
|
val now = Date()
|
||||||
val ninetyDaysAgo = Date(now.millisSinceEpoch - NinetyDaysMillis)
|
val ninetyDaysAgo = Date(now.millisSinceEpoch - NinetyDaysMillis)
|
||||||
|
|
||||||
getTransactionsAsync(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo, abortIfTanIsRequired = true), bank, customer, account) { response ->
|
getTransactionsAsync(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo, abortIfTanIsRequired = true), bank, account) { response ->
|
||||||
callback(response)
|
callback(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData,
|
open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData,
|
||||||
customer: CustomerData, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
||||||
|
|
||||||
val dialogContext = DialogContext(bank, customer, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
|
|
||||||
initDialog(dialogContext) { initDialogResponse ->
|
initDialog(dialogContext) { initDialogResponse ->
|
||||||
|
|
||||||
|
@ -417,61 +417,60 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun getTanMediaListAsync(bank: BankData, customer: CustomerData,
|
open fun getTanMediaListAsync(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
||||||
tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
|
||||||
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien,
|
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien,
|
||||||
callback: (GetTanMediaListResponse) -> Unit) {
|
callback: (GetTanMediaListResponse) -> Unit) {
|
||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
getTanMediaList(bank, customer, tanMediaKind, tanMediumClass, callback)
|
getTanMediaList(bank, tanMediaKind, tanMediumClass, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getTanMediaList(bank: BankData, customer: CustomerData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
open fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
||||||
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) {
|
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) {
|
||||||
|
|
||||||
sendMessageAndHandleResponse(bank, customer, true, CustomerSegmentId.TanMediaList, { dialogContext ->
|
sendMessageAndHandleResponse(bank, true, CustomerSegmentId.TanMediaList, { dialogContext ->
|
||||||
messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass)
|
messageBuilder.createGetTanMediaListMessage(dialogContext, tanMediaKind, tanMediumClass)
|
||||||
}) { response ->
|
}) { response ->
|
||||||
handleGetTanMediaListResponse(response, customer, callback)
|
handleGetTanMediaListResponse(response, bank, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleGetTanMediaListResponse(response: Response, customer: CustomerData, callback: (GetTanMediaListResponse) -> Unit) {
|
private fun handleGetTanMediaListResponse(response: Response, bank: BankData, callback: (GetTanMediaListResponse) -> Unit) {
|
||||||
// TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures
|
// TAN media list (= TAN generator list) is only returned for users with chipTAN TAN procedures
|
||||||
val tanMediaList = if (response.successful == false) null
|
val tanMediaList = if (response.successful == false) null
|
||||||
else response.getFirstSegmentById<TanMediaList>(InstituteSegmentId.TanMediaList)
|
else response.getFirstSegmentById<TanMediaList>(InstituteSegmentId.TanMediaList)
|
||||||
|
|
||||||
tanMediaList?.let {
|
tanMediaList?.let {
|
||||||
customer.tanMedia = it.tanMedia
|
bank.tanMedia = it.tanMedia
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(GetTanMediaListResponse(response, tanMediaList))
|
callback(GetTanMediaListResponse(response, tanMediaList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) {
|
open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
|
|
||||||
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
|
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
|
||||||
this.callback.enterTanGeneratorAtc(customer, newActiveTanMedium) { enteredAtc ->
|
this.callback.enterTanGeneratorAtc(bank, newActiveTanMedium) { enteredAtc ->
|
||||||
if (enteredAtc.hasAtcBeenEntered == false) {
|
if (enteredAtc.hasAtcBeenEntered == false) {
|
||||||
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
|
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
|
||||||
callback(FinTsClientResponse(Response(false, errorMessage = message)))
|
callback(FinTsClientResponse(Response(false, errorMessage = message)))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sendChangeTanMediumMessage(bank, customer, newActiveTanMedium, enteredAtc, callback)
|
sendChangeTanMediumMessage(bank, newActiveTanMedium, enteredAtc, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sendChangeTanMediumMessage(bank, customer, newActiveTanMedium, null, callback)
|
sendChangeTanMediumMessage(bank, newActiveTanMedium, null, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun sendChangeTanMediumMessage(bank: BankData, customer: CustomerData, newActiveTanMedium: TanGeneratorTanMedium,
|
protected open fun sendChangeTanMediumMessage(bank: BankData, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?,
|
||||||
enteredAtc: EnterTanGeneratorAtcResult?, callback: (FinTsClientResponse) -> Unit) {
|
callback: (FinTsClientResponse) -> Unit) {
|
||||||
|
|
||||||
sendMessageAndHandleResponse(bank, customer, false, null, { dialogContext ->
|
sendMessageAndHandleResponse(bank, false, null, { dialogContext ->
|
||||||
messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc)
|
messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc)
|
||||||
}) { response ->
|
}) { response ->
|
||||||
callback(FinTsClientResponse(response))
|
callback(FinTsClientResponse(response))
|
||||||
|
@ -479,10 +478,9 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData,
|
open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
customer: CustomerData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
|
||||||
|
|
||||||
sendMessageAndHandleResponse(bank, customer, true, null, { dialogContext ->
|
sendMessageAndHandleResponse(bank, true, null, { dialogContext ->
|
||||||
messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext)
|
messageBuilder.createBankTransferMessage(bankTransferData, account, dialogContext)
|
||||||
}) { response ->
|
}) { response ->
|
||||||
callback(FinTsClientResponse(response))
|
callback(FinTsClientResponse(response))
|
||||||
|
@ -490,11 +488,10 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun sendMessageAndHandleResponse(bank: BankData, customer: CustomerData, messageMayRequiresTan: Boolean = true,
|
protected open fun sendMessageAndHandleResponse(bank: BankData, messageMayRequiresTan: Boolean = true, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null,
|
||||||
segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null,
|
|
||||||
createMessage: (DialogContext) -> MessageBuilderResult, callback: (Response) -> Unit) {
|
createMessage: (DialogContext) -> MessageBuilderResult, callback: (Response) -> Unit) {
|
||||||
|
|
||||||
val dialogContext = DialogContext(bank, customer, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
|
|
||||||
if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) {
|
if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) {
|
||||||
initDialog(dialogContext) { initDialogResponse ->
|
initDialog(dialogContext) { initDialogResponse ->
|
||||||
|
@ -527,13 +524,13 @@ open class FinTsClient(
|
||||||
protected open fun initDialog(dialogContext: DialogContext, callback: (Response) -> Unit) {
|
protected open fun initDialog(dialogContext: DialogContext, callback: (Response) -> Unit) {
|
||||||
|
|
||||||
// we first need to retrieve supported tan procedures and jobs before we can do anything
|
// we first need to retrieve supported tan procedures and jobs before we can do anything
|
||||||
ensureBasicBankDataRetrieved(dialogContext.bank, dialogContext.customer) { retrieveBasicBankDataResponse ->
|
ensureBasicBankDataRetrieved(dialogContext.bank) { retrieveBasicBankDataResponse ->
|
||||||
if (retrieveBasicBankDataResponse.successful == false) {
|
if (retrieveBasicBankDataResponse.successful == false) {
|
||||||
callback(retrieveBasicBankDataResponse)
|
callback(retrieveBasicBankDataResponse)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// as in the next step we have to supply user's tan procedure, ensure user selected his or her
|
// as in the next step we have to supply user's tan procedure, ensure user selected his or her
|
||||||
ensureTanProcedureIsSelected(dialogContext.bank, dialogContext.customer) { tanProcedureSelectedResponse ->
|
ensureTanProcedureIsSelected(dialogContext.bank) { tanProcedureSelectedResponse ->
|
||||||
if (tanProcedureSelectedResponse.successful == false) {
|
if (tanProcedureSelectedResponse.successful == false) {
|
||||||
callback(tanProcedureSelectedResponse)
|
callback(tanProcedureSelectedResponse)
|
||||||
}
|
}
|
||||||
|
@ -553,7 +550,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
updateBankData(dialogContext.bank, response)
|
updateBankData(dialogContext.bank, response)
|
||||||
updateCustomerData(dialogContext.customer, dialogContext.bank, response)
|
updateCustomerData(dialogContext.bank, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(response)
|
callback(response)
|
||||||
|
@ -568,7 +565,7 @@ open class FinTsClient(
|
||||||
getAndHandleResponseForMessage(message, dialogContext) { response ->
|
getAndHandleResponseForMessage(message, dialogContext) { response ->
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
updateBankData(dialogContext.bank, response)
|
updateBankData(dialogContext.bank, response)
|
||||||
updateCustomerData(dialogContext.customer, dialogContext.bank, response)
|
updateCustomerData(dialogContext.bank, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(response)
|
callback(response)
|
||||||
|
@ -588,10 +585,10 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun ensureBasicBankDataRetrieved(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) {
|
protected open fun ensureBasicBankDataRetrieved(bank: BankData, callback: (Response) -> Unit) {
|
||||||
if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) {
|
if (bank.tanProceduresSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) {
|
||||||
getUsersTanProcedures(bank, customer) { getBankInfoResponse ->
|
getUsersTanProcedures(bank) { getBankInfoResponse ->
|
||||||
if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty()
|
if (getBankInfoResponse.isSuccessful == false || bank.tanProceduresSupportedByBank.isEmpty()
|
||||||
|| bank.supportedJobs.isEmpty()) {
|
|| bank.supportedJobs.isEmpty()) {
|
||||||
|
|
||||||
callback(Response(false, errorMessage =
|
callback(Response(false, errorMessage =
|
||||||
|
@ -607,48 +604,48 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) {
|
protected open fun ensureTanProcedureIsSelected(bank: BankData, callback: (Response) -> Unit) {
|
||||||
if (customer.isTanProcedureSelected == false) {
|
if (bank.isTanProcedureSelected == false) {
|
||||||
if (customer.supportedTanProcedures.isEmpty()) {
|
if (bank.tanProceduresAvailableForUser.isEmpty()) {
|
||||||
getUsersTanProcedures(bank, customer) {
|
getUsersTanProcedures(bank) {
|
||||||
if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user
|
if (bank.tanProceduresAvailableForUser.isEmpty()) { // could not retrieve supported tan procedures for user
|
||||||
callback(Response(false, noTanProcedureSelected = true))
|
callback(Response(false, noTanProcedureSelected = true))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getUsersTanProcedure(customer)
|
getUsersTanProcedure(bank)
|
||||||
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
|
callback(Response(bank.isTanProcedureSelected, noTanProcedureSelected = !!!bank.isTanProcedureSelected))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getUsersTanProcedure(customer)
|
getUsersTanProcedure(bank)
|
||||||
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
|
callback(Response(bank.isTanProcedureSelected, noTanProcedureSelected = !!!bank.isTanProcedureSelected))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
callback(Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected))
|
callback(Response(bank.isTanProcedureSelected, noTanProcedureSelected = !!!bank.isTanProcedureSelected))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getUsersTanProcedure(customer: CustomerData) {
|
protected open fun getUsersTanProcedure(bank: BankData) {
|
||||||
if (customer.supportedTanProcedures.size == 1) { // user has only one TAN procedure -> set it and we're done
|
if (bank.tanProceduresAvailableForUser.size == 1) { // user has only one TAN procedure -> set it and we're done
|
||||||
customer.selectedTanProcedure = customer.supportedTanProcedures.first()
|
bank.selectedTanProcedure = bank.tanProceduresAvailableForUser.first()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// we know user's supported tan procedures, now ask user which one to select
|
// we know user's supported tan procedures, now ask user which one to select
|
||||||
callback.askUserForTanProcedure(customer.supportedTanProcedures, selectSuggestedTanProcedure(customer)) { selectedTanProcedure ->
|
callback.askUserForTanProcedure(bank.tanProceduresAvailableForUser, selectSuggestedTanProcedure(bank)) { selectedTanProcedure ->
|
||||||
selectedTanProcedure?.let {
|
selectedTanProcedure?.let {
|
||||||
customer.selectedTanProcedure = selectedTanProcedure
|
bank.selectedTanProcedure = selectedTanProcedure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun selectSuggestedTanProcedure(customer: CustomerData): TanProcedure? {
|
protected open fun selectSuggestedTanProcedure(bank: BankData): TanProcedure? {
|
||||||
return customer.supportedTanProcedures.firstOrNull { it.type != TanProcedureType.ChipTanUsb && it.type != TanProcedureType.SmsTan && it.type != TanProcedureType.ChipTanManuell }
|
return bank.tanProceduresAvailableForUser.firstOrNull { it.type != TanProcedureType.ChipTanUsb && it.type != TanProcedureType.SmsTan && it.type != TanProcedureType.ChipTanManuell }
|
||||||
?: customer.supportedTanProcedures.firstOrNull { it.type != TanProcedureType.ChipTanUsb && it.type != TanProcedureType.SmsTan }
|
?: bank.tanProceduresAvailableForUser.firstOrNull { it.type != TanProcedureType.ChipTanUsb && it.type != TanProcedureType.SmsTan }
|
||||||
?: customer.supportedTanProcedures.firstOrNull { it.type != TanProcedureType.ChipTanUsb }
|
?: bank.tanProceduresAvailableForUser.firstOrNull { it.type != TanProcedureType.ChipTanUsb }
|
||||||
?: customer.supportedTanProcedures.firstOrNull()
|
?: bank.tanProceduresAvailableForUser.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -766,24 +763,24 @@ open class FinTsClient(
|
||||||
|
|
||||||
log.debug { prettyPrintMessageWithPrefix }
|
log.debug { prettyPrintMessageWithPrefix }
|
||||||
|
|
||||||
messageLogField.add(MessageLogEntry(prettyPrintMessageWithPrefix, timeStamp, dialogContext.customer))
|
messageLogField.add(MessageLogEntry(prettyPrintMessageWithPrefix, timeStamp, dialogContext.bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun prettyPrintHbciMessage(message: String): String {
|
protected fun prettyPrintHbciMessage(message: String): String {
|
||||||
return message.replace("'", "'\r\n")
|
return message.replace("'", "'\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun removeSensitiveDataFromMessage(message: String, customer: CustomerData): String {
|
protected open fun removeSensitiveDataFromMessage(message: String, bank: BankData): String {
|
||||||
var prettyPrintMessageWithoutSensitiveData = message
|
var prettyPrintMessageWithoutSensitiveData = message
|
||||||
.replace(customer.customerId, "<customer_id>")
|
.replace(bank.customerId, "<customer_id>")
|
||||||
.replace("+" + customer.pin, "+<pin>")
|
.replace("+" + bank.pin, "+<pin>")
|
||||||
|
|
||||||
if (customer.name.isNotBlank()) {
|
if (bank.customerName.isNotBlank()) {
|
||||||
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
|
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
|
||||||
.replace(customer.name, "<customer_name>", true)
|
.replace(bank.customerName, "<customer_name>", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
customer.accounts.forEach { account ->
|
bank.accounts.forEach { account ->
|
||||||
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
|
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
|
||||||
.replace(account.accountIdentifier, "<account_identifier>")
|
.replace(account.accountIdentifier, "<account_identifier>")
|
||||||
|
|
||||||
|
@ -836,19 +833,19 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun handleEnteringTanRequired(tanResponse: TanResponse, response: Response, dialogContext: DialogContext, callback: (Response) -> Unit) {
|
protected open fun handleEnteringTanRequired(tanResponse: TanResponse, response: Response, dialogContext: DialogContext, callback: (Response) -> Unit) {
|
||||||
val customer = dialogContext.customer // TODO: copy required data to TanChallenge
|
val bank = dialogContext.bank // TODO: copy required data to TanChallenge
|
||||||
val tanChallenge = createTanChallenge(tanResponse, customer)
|
val tanChallenge = createTanChallenge(tanResponse, bank)
|
||||||
|
|
||||||
this.callback.enterTan(customer, tanChallenge) { enteredTanResult ->
|
this.callback.enterTan(bank, tanChallenge) { enteredTanResult ->
|
||||||
handleEnterTanResult(enteredTanResult, tanResponse, response, dialogContext, callback)
|
handleEnterTanResult(enteredTanResult, tanResponse, response, dialogContext, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun createTanChallenge(tanResponse: TanResponse, customer: CustomerData): TanChallenge {
|
protected open fun createTanChallenge(tanResponse: TanResponse, bank: BankData): TanChallenge {
|
||||||
// TODO: is this true for all tan procedures?
|
// TODO: is this true for all tan procedures?
|
||||||
val messageToShowToUser = tanResponse.challenge ?: ""
|
val messageToShowToUser = tanResponse.challenge ?: ""
|
||||||
val challenge = tanResponse.challengeHHD_UC ?: ""
|
val challenge = tanResponse.challengeHHD_UC ?: ""
|
||||||
val tanProcedure = customer.selectedTanProcedure
|
val tanProcedure = bank.selectedTanProcedure
|
||||||
|
|
||||||
return when (tanProcedure.type) {
|
return when (tanProcedure.type) {
|
||||||
TanProcedureType.ChipTanFlickercode ->
|
TanProcedureType.ChipTanFlickercode ->
|
||||||
|
@ -894,7 +891,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, dialogContext: DialogContext, callback: (Response) -> Unit) {
|
protected open fun handleUserAsksToChangeTanProcedureAndResendLastMessage(changeTanProcedureTo: TanProcedure, dialogContext: DialogContext, callback: (Response) -> Unit) {
|
||||||
|
|
||||||
dialogContext.customer.selectedTanProcedure = changeTanProcedureTo
|
dialogContext.bank.selectedTanProcedure = changeTanProcedureTo
|
||||||
|
|
||||||
|
|
||||||
val lastCreatedMessage = dialogContext.currentMessage
|
val lastCreatedMessage = dialogContext.currentMessage
|
||||||
|
@ -914,7 +911,7 @@ open class FinTsClient(
|
||||||
lastCreatedMessage?.let { closeDialog(dialogContext) }
|
lastCreatedMessage?.let { closeDialog(dialogContext) }
|
||||||
|
|
||||||
|
|
||||||
changeTanMedium(changeTanMediumTo, dialogContext.bank, dialogContext.customer) { changeTanMediumResponse ->
|
changeTanMedium(changeTanMediumTo, dialogContext.bank) { changeTanMediumResponse ->
|
||||||
changeTanMediumResultCallback?.invoke(changeTanMediumResponse)
|
changeTanMediumResultCallback?.invoke(changeTanMediumResponse)
|
||||||
|
|
||||||
if (changeTanMediumResponse.isSuccessful == false || lastCreatedMessage == null) {
|
if (changeTanMediumResponse.isSuccessful == false || lastCreatedMessage == null) {
|
||||||
|
@ -930,7 +927,7 @@ open class FinTsClient(
|
||||||
protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext, callback: (Response) -> Unit) {
|
protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext, callback: (Response) -> Unit) {
|
||||||
|
|
||||||
if (lastCreatedMessage != null) { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message
|
if (lastCreatedMessage != null) { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message
|
||||||
val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler)
|
val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler)
|
||||||
|
|
||||||
initDialog(newDialogContext) { initDialogResponse ->
|
initDialog(newDialogContext) { initDialogResponse ->
|
||||||
if (initDialogResponse.successful == false) {
|
if (initDialogResponse.successful == false) {
|
||||||
|
@ -957,7 +954,7 @@ open class FinTsClient(
|
||||||
protected open fun updateBankData(bank: BankData, response: Response) {
|
protected open fun updateBankData(bank: BankData, response: Response) {
|
||||||
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
|
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
|
||||||
bank.bpdVersion = bankParameters.bpdVersion
|
bank.bpdVersion = bankParameters.bpdVersion
|
||||||
bank.name = adjustBankName(bankParameters.bankName)
|
bank.bankName = adjustBankName(bankParameters.bankName)
|
||||||
bank.bankCode = bankParameters.bankCode
|
bank.bankCode = bankParameters.bankCode
|
||||||
bank.countryCode = bankParameters.bankCountryCode
|
bank.countryCode = bankParameters.bankCountryCode
|
||||||
bank.countMaxJobsPerMessage = bankParameters.countMaxJobsPerMessage
|
bank.countMaxJobsPerMessage = bankParameters.countMaxJobsPerMessage
|
||||||
|
@ -972,7 +969,7 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { tanInfo ->
|
response.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { tanInfo ->
|
||||||
bank.supportedTanProcedures = mapToTanProcedures(tanInfo)
|
bank.tanProceduresSupportedByBank = mapToTanProcedures(tanInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<CommunicationInfo>(InstituteSegmentId.CommunicationInfo)?.let { communicationInfo ->
|
response.getFirstSegmentById<CommunicationInfo>(InstituteSegmentId.CommunicationInfo)?.let { communicationInfo ->
|
||||||
|
@ -1000,19 +997,19 @@ open class FinTsClient(
|
||||||
return bankName.replace("DB24-Filiale", "Deutsche Bank") // set a better name for Deutsche Bank's self title 'DB24-Filiale'
|
return bankName.replace("DB24-Filiale", "Deutsche Bank") // set a better name for Deutsche Bank's self title 'DB24-Filiale'
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun updateCustomerData(customer: CustomerData, bank: BankData, response: Response) {
|
protected open fun updateCustomerData(bank: BankData, response: Response) {
|
||||||
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
|
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
|
||||||
// TODO: ask user if there is more than one supported language? But it seems that almost all banks only support German.
|
// TODO: ask user if there is more than one supported language? But it seems that almost all banks only support German.
|
||||||
if (customer.selectedLanguage == Dialogsprache.Default && bankParameters.supportedLanguages.isNotEmpty()) {
|
if (bank.selectedLanguage == Dialogsprache.Default && bankParameters.supportedLanguages.isNotEmpty()) {
|
||||||
customer.selectedLanguage = bankParameters.supportedLanguages.first()
|
bank.selectedLanguage = bankParameters.supportedLanguages.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<ReceivedSynchronization>(InstituteSegmentId.Synchronization)?.let { synchronization ->
|
response.getFirstSegmentById<ReceivedSynchronization>(InstituteSegmentId.Synchronization)?.let { synchronization ->
|
||||||
synchronization.customerSystemId?.let {
|
synchronization.customerSystemId?.let {
|
||||||
customer.customerSystemId = it
|
bank.customerSystemId = it
|
||||||
|
|
||||||
customer.customerSystemStatus = KundensystemStatusWerte.Benoetigt // TODO: didn't find out for sure yet, but i think i read somewhere, that this has to be set when customerSystemId is set
|
bank.customerSystemStatus = KundensystemStatusWerte.Benoetigt // TODO: didn't find out for sure yet, but i think i read somewhere, that this has to be set when customerSystemId is set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,9 +1018,9 @@ open class FinTsClient(
|
||||||
accountInfo.accountHolderName2?.let {
|
accountInfo.accountHolderName2?.let {
|
||||||
accountHolderName += it // TODO: add a whitespace in between?
|
accountHolderName += it // TODO: add a whitespace in between?
|
||||||
}
|
}
|
||||||
customer.name = accountHolderName
|
bank.customerName = accountHolderName
|
||||||
|
|
||||||
findExistingAccount(customer, accountInfo)?.let { account ->
|
findExistingAccount(bank, accountInfo)?.let { account ->
|
||||||
// TODO: update AccountData. But can this ever happen that an account changes?
|
// TODO: update AccountData. But can this ever happen that an account changes?
|
||||||
}
|
}
|
||||||
?: run {
|
?: run {
|
||||||
|
@ -1032,7 +1029,7 @@ open class FinTsClient(
|
||||||
accountInfo.accountType, accountInfo.currency, accountHolderName, accountInfo.productName,
|
accountInfo.accountType, accountInfo.currency, accountHolderName, accountInfo.productName,
|
||||||
accountInfo.accountLimit, accountInfo.allowedJobNames)
|
accountInfo.accountLimit, accountInfo.allowedJobNames)
|
||||||
|
|
||||||
customer.addAccount(newAccount)
|
bank.addAccount(newAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: may also make use of other info
|
// TODO: may also make use of other info
|
||||||
|
@ -1046,11 +1043,11 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<UserParameters>(InstituteSegmentId.UserParameters)?.let { userParameters ->
|
response.getFirstSegmentById<UserParameters>(InstituteSegmentId.UserParameters)?.let { userParameters ->
|
||||||
customer.updVersion = userParameters.updVersion
|
bank.updVersion = userParameters.updVersion
|
||||||
|
|
||||||
if (customer.name.isEmpty()) {
|
if (bank.customerName.isEmpty()) {
|
||||||
userParameters.username?.let {
|
userParameters.username?.let {
|
||||||
customer.name = it
|
bank.customerName = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,19 +1055,19 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<CommunicationInfo>(InstituteSegmentId.CommunicationInfo)?.let { communicationInfo ->
|
response.getFirstSegmentById<CommunicationInfo>(InstituteSegmentId.CommunicationInfo)?.let { communicationInfo ->
|
||||||
if (customer.selectedLanguage != communicationInfo.defaultLanguage) {
|
if (bank.selectedLanguage != communicationInfo.defaultLanguage) {
|
||||||
customer.selectedLanguage = communicationInfo.defaultLanguage
|
bank.selectedLanguage = communicationInfo.defaultLanguage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val supportedJobs = response.supportedJobs
|
val supportedJobs = response.supportedJobs
|
||||||
if (supportedJobs.isNotEmpty()) { // if allowedJobsForBank is empty than bank didn't send any allowed job
|
if (supportedJobs.isNotEmpty()) { // if allowedJobsForBank is empty than bank didn't send any allowed job
|
||||||
for (account in customer.accounts) {
|
for (account in bank.accounts) {
|
||||||
setAllowedJobsForAccount(bank, account, supportedJobs)
|
setAllowedJobsForAccount(bank, account, supportedJobs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bank.supportedJobs.isNotEmpty()) {
|
else if (bank.supportedJobs.isNotEmpty()) {
|
||||||
for (account in customer.accounts) {
|
for (account in bank.accounts) {
|
||||||
if (account.allowedJobs.isEmpty()) {
|
if (account.allowedJobs.isEmpty()) {
|
||||||
setAllowedJobsForAccount(bank, account, bank.supportedJobs)
|
setAllowedJobsForAccount(bank, account, bank.supportedJobs)
|
||||||
}
|
}
|
||||||
|
@ -1078,16 +1075,16 @@ open class FinTsClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.supportedTanProceduresForUser.isNotEmpty()) {
|
if (response.supportedTanProceduresForUser.isNotEmpty()) {
|
||||||
customer.supportedTanProcedures = response.supportedTanProceduresForUser.mapNotNull { findTanProcedure(it, bank) }
|
bank.tanProceduresAvailableForUser = response.supportedTanProceduresForUser.mapNotNull { findTanProcedure(it, bank) }
|
||||||
|
|
||||||
if (customer.supportedTanProcedures.firstOrNull { it.securityFunction == customer.selectedTanProcedure.securityFunction } == null) { // supportedTanProcedures don't contain selectedTanProcedure anymore
|
if (bank.tanProceduresAvailableForUser.firstOrNull { it.securityFunction == bank.selectedTanProcedure.securityFunction } == null) { // supportedTanProcedures don't contain selectedTanProcedure anymore
|
||||||
customer.resetSelectedTanProcedure()
|
bank.resetSelectedTanProcedure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun findTanProcedure(securityFunction: Sicherheitsfunktion, bank: BankData): TanProcedure? {
|
protected open fun findTanProcedure(securityFunction: Sicherheitsfunktion, bank: BankData): TanProcedure? {
|
||||||
return bank.supportedTanProcedures.firstOrNull { it.securityFunction == securityFunction }
|
return bank.tanProceduresSupportedByBank.firstOrNull { it.securityFunction == securityFunction }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun setAllowedJobsForAccount(bank: BankData, account: AccountData, supportedJobs: List<JobParameters>) {
|
protected open fun setAllowedJobsForAccount(bank: BankData, account: AccountData, supportedJobs: List<JobParameters>) {
|
||||||
|
@ -1214,8 +1211,8 @@ open class FinTsClient(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun findExistingAccount(customer: CustomerData, accountInfo: AccountInfo): AccountData? {
|
protected open fun findExistingAccount(bank: BankData, accountInfo: AccountInfo): AccountData? {
|
||||||
customer.accounts.forEach { account ->
|
bank.accounts.forEach { account ->
|
||||||
if (account.accountIdentifier == accountInfo.accountIdentifier
|
if (account.accountIdentifier == accountInfo.accountIdentifier
|
||||||
&& account.productName == accountInfo.productName
|
&& account.productName == accountInfo.productName
|
||||||
&& account.accountType == accountInfo.accountType) {
|
&& account.accountType == accountInfo.accountType) {
|
||||||
|
|
|
@ -17,14 +17,13 @@ import net.dankito.banking.fints.webclient.KtorWebClient
|
||||||
|
|
||||||
open class FinTsClientForCustomer(
|
open class FinTsClientForCustomer(
|
||||||
val bank: BankData,
|
val bank: BankData,
|
||||||
val customer: CustomerData,
|
|
||||||
callback: FinTsClientCallback,
|
callback: FinTsClientCallback,
|
||||||
webClient: IWebClient = KtorWebClient(),
|
webClient: IWebClient = KtorWebClient(),
|
||||||
base64Service: IBase64Service = PureKotlinBase64Service(),
|
base64Service: IBase64Service = PureKotlinBase64Service(),
|
||||||
messageBuilder: MessageBuilder = MessageBuilder(),
|
messageBuilder: MessageBuilder = MessageBuilder(),
|
||||||
responseParser: ResponseParser = ResponseParser(),
|
responseParser: ResponseParser = ResponseParser(),
|
||||||
mt940Parser: IAccountTransactionsParser = Mt940AccountTransactionsParser(),
|
mt940Parser: IAccountTransactionsParser = Mt940AccountTransactionsParser(),
|
||||||
product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically
|
product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically){}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
protected val client = FinTsClient(callback, webClient, base64Service, messageBuilder, responseParser, mt940Parser, product)
|
protected val client = FinTsClient(callback, webClient, base64Service, messageBuilder, responseParser, mt940Parser, product)
|
||||||
|
@ -35,17 +34,17 @@ open class FinTsClientForCustomer(
|
||||||
|
|
||||||
|
|
||||||
open fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
|
open fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
|
||||||
client.addAccountAsync(bank, customer, callback)
|
client.addAccountAsync(bank, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun getTransactionsAsync(parameter: GetTransactionsParameter, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
open fun getTransactionsAsync(parameter: GetTransactionsParameter, account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
|
||||||
client.getTransactionsAsync(parameter, bank, customer, account, callback)
|
client.getTransactionsAsync(parameter, bank, account, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun doBankTransferAsync(bankTransferData: BankTransferData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
open fun doBankTransferAsync(bankTransferData: BankTransferData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
client.doBankTransferAsync(bankTransferData, bank, customer, account, callback)
|
client.doBankTransferAsync(bankTransferData, bank, account, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,13 +18,13 @@ interface FinTsClientCallback {
|
||||||
*/
|
*/
|
||||||
fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit)
|
fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit)
|
||||||
|
|
||||||
fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
|
fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.
|
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.
|
||||||
*
|
*
|
||||||
* If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc]
|
* If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc]
|
||||||
*/
|
*/
|
||||||
fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
|
fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,11 +12,11 @@ open class NoOpFinTsClientCallback : FinTsClientCallback {
|
||||||
callback(suggestedTanProcedure)
|
callback(suggestedTanProcedure)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||||
callback(EnterTanResult.userDidNotEnterTan())
|
callback(EnterTanResult.userDidNotEnterTan())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||||
callback(EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
callback(EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ import net.dankito.banking.fints.model.*
|
||||||
|
|
||||||
|
|
||||||
open class SimpleFinTsClientCallback(
|
open class SimpleFinTsClientCallback(
|
||||||
protected val enterTan: ((customer: CustomerData, tanChallenge: TanChallenge) -> EnterTanResult)? = null,
|
protected val enterTan: ((bank: BankData, tanChallenge: TanChallenge) -> EnterTanResult)? = null,
|
||||||
protected val enterTanGeneratorAtc: ((customer: CustomerData, tanMedium: TanGeneratorTanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
protected val enterTanGeneratorAtc: ((bank: BankData, tanMedium: TanGeneratorTanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
||||||
protected val askUserForTanProcedure: ((supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?) -> TanProcedure?)? = null
|
protected val askUserForTanProcedure: ((supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?) -> TanProcedure?)? = null
|
||||||
) : FinTsClientCallback {
|
) : FinTsClientCallback {
|
||||||
|
|
||||||
|
@ -16,12 +16,12 @@ open class SimpleFinTsClientCallback(
|
||||||
callback(askUserForTanProcedure?.invoke(supportedTanProcedures, suggestedTanProcedure) ?: suggestedTanProcedure)
|
callback(askUserForTanProcedure?.invoke(supportedTanProcedures, suggestedTanProcedure) ?: suggestedTanProcedure)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||||
callback(enterTan?.invoke(customer, tanChallenge) ?: EnterTanResult.userDidNotEnterTan())
|
callback(enterTan?.invoke(bank, tanChallenge) ?: EnterTanResult.userDidNotEnterTan())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||||
callback(enterTanGeneratorAtc?.invoke(customer, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
callback(enterTanGeneratorAtc?.invoke(bank, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -117,11 +117,11 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
if (segmentIdForTwoStepTanProcess != null) {
|
if (segmentIdForTwoStepTanProcess != null) {
|
||||||
segments.add(createTwoStepTanSegment(segmentIdForTwoStepTanProcess, dialogContext))
|
segments.add(createTwoStepTanSegment(segmentIdForTwoStepTanProcess, dialogContext))
|
||||||
}
|
}
|
||||||
else if (dialogContext.customer.isTanProcedureSelected) {
|
else if (dialogContext.bank.isTanProcedureSelected) {
|
||||||
segments.add(createTwoStepTanSegment(CustomerSegmentId.Identification, dialogContext))
|
segments.add(createTwoStepTanSegment(CustomerSegmentId.Identification, dialogContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialogContext.customer.customerSystemId == KundensystemID.Anonymous) {
|
if (dialogContext.bank.customerSystemId == KundensystemID.Anonymous) {
|
||||||
segments.add(Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden))
|
segments.add(Synchronisierung(generator.getNextSegmentNumber(), Synchronisierungsmodus.NeueKundensystemIdZurueckmelden))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
if (result.isJobVersionSupported) {
|
if (result.isJobVersionSupported) {
|
||||||
val segments = listOf(
|
val segments = listOf(
|
||||||
TanGeneratorTanMediumAnOderUmmelden(result.getHighestAllowedVersion!!, generator.resetSegmentNumber(2),
|
TanGeneratorTanMediumAnOderUmmelden(result.getHighestAllowedVersion!!, generator.resetSegmentNumber(2),
|
||||||
dialogContext.bank, dialogContext.customer, newActiveTanMedium, tan, atc)
|
dialogContext.bank, newActiveTanMedium, tan, atc)
|
||||||
)
|
)
|
||||||
|
|
||||||
return createSignedMessageBuilderResult(dialogContext, segments)
|
return createSignedMessageBuilderResult(dialogContext, segments)
|
||||||
|
@ -261,7 +261,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
|
|
||||||
if (result.isJobVersionSupported && urn != null) {
|
if (result.isJobVersionSupported && urn != null) {
|
||||||
val segments = mutableListOf<Segment>(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2),
|
val segments = mutableListOf<Segment>(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2),
|
||||||
urn, dialogContext.customer, account, dialogContext.bank.bic, data))
|
urn, dialogContext.bank.customerName, account, dialogContext.bank.bic, data))
|
||||||
|
|
||||||
addTanSegmentIfRequired(segmentId, dialogContext, segments)
|
addTanSegmentIfRequired(segmentId, dialogContext, segments)
|
||||||
|
|
||||||
|
@ -395,7 +395,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
val signatureEnding = Signaturabschluss(
|
val signatureEnding = Signaturabschluss(
|
||||||
generator.getNextSegmentNumber(),
|
generator.getNextSegmentNumber(),
|
||||||
controlReference,
|
controlReference,
|
||||||
dialogContext.customer.pin,
|
dialogContext.bank.pin,
|
||||||
tan
|
tan
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -469,10 +469,10 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getTanMediaIdentifierIfRequired(dialogContext: DialogContext): String? {
|
protected open fun getTanMediaIdentifierIfRequired(dialogContext: DialogContext): String? {
|
||||||
val customer = dialogContext.customer
|
val bank = dialogContext.bank
|
||||||
|
|
||||||
if (customer.isTanProcedureSelected && customer.selectedTanProcedure.nameOfTanMediaRequired) {
|
if (bank.isTanProcedureSelected && bank.selectedTanProcedure.nameOfTanMediaRequired) {
|
||||||
return customer.tanMedia.firstOrNull { it.mediumName != null }?.mediumName
|
return bank.tanMedia.firstOrNull { it.mediumName != null }?.mediumName
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -6,6 +6,10 @@ open class JobTanConfiguration(
|
||||||
val tanRequired: Boolean
|
val tanRequired: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this("", false) // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$segmentId requires TAN? $tanRequired"
|
return "$segmentId requires TAN? $tanRequired"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ open class IdentifikationsSegment(
|
||||||
) : Segment(listOf(
|
) : Segment(listOf(
|
||||||
Segmentkopf(CustomerSegmentId.Identification, 2, segmentNumber),
|
Segmentkopf(CustomerSegmentId.Identification, 2, segmentNumber),
|
||||||
Kreditinstitutskennung(baseData.bank.countryCode, baseData.bank.bankCode),
|
Kreditinstitutskennung(baseData.bank.countryCode, baseData.bank.bankCode),
|
||||||
KundenID(baseData.customer.customerId),
|
KundenID(baseData.bank.customerId),
|
||||||
KundensystemID(baseData.customer.customerSystemId),
|
KundensystemID(baseData.bank.customerSystemId),
|
||||||
KundensystemStatus(baseData.customer.customerSystemStatus, Existenzstatus.Mandatory)
|
KundensystemStatus(baseData.bank.customerSystemStatus, Existenzstatus.Mandatory)
|
||||||
))
|
))
|
|
@ -14,7 +14,6 @@ open class PinTanSignaturkopf(
|
||||||
) : Signaturkopf(
|
) : Signaturkopf(
|
||||||
segmentNumber,
|
segmentNumber,
|
||||||
baseData.bank,
|
baseData.bank,
|
||||||
baseData.customer,
|
|
||||||
baseData.versionOfSecurityProcedure,
|
baseData.versionOfSecurityProcedure,
|
||||||
securityControlReference,
|
securityControlReference,
|
||||||
date,
|
date,
|
||||||
|
|
|
@ -13,7 +13,6 @@ open class PinTanVerschluesselungskopf(
|
||||||
|
|
||||||
) : Verschluesselungskopf(
|
) : Verschluesselungskopf(
|
||||||
baseData.bank,
|
baseData.bank,
|
||||||
baseData.customer,
|
|
||||||
baseData.versionOfSecurityProcedure,
|
baseData.versionOfSecurityProcedure,
|
||||||
date,
|
date,
|
||||||
time,
|
time,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.sig
|
||||||
import net.dankito.banking.fints.messages.segmente.Segment
|
import net.dankito.banking.fints.messages.segmente.Segment
|
||||||
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
||||||
import net.dankito.banking.fints.model.BankData
|
import net.dankito.banking.fints.model.BankData
|
||||||
import net.dankito.banking.fints.model.CustomerData
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +24,6 @@ import net.dankito.banking.fints.model.CustomerData
|
||||||
open class Signaturkopf(
|
open class Signaturkopf(
|
||||||
segmentNumber: Int,
|
segmentNumber: Int,
|
||||||
bank: BankData,
|
bank: BankData,
|
||||||
customer: CustomerData,
|
|
||||||
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens,
|
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens,
|
||||||
securityControlReference: String,
|
securityControlReference: String,
|
||||||
date: Int,
|
date: Int,
|
||||||
|
@ -41,15 +39,15 @@ open class Signaturkopf(
|
||||||
Sicherheitsverfahren.PIN_TAN_Verfahren,
|
Sicherheitsverfahren.PIN_TAN_Verfahren,
|
||||||
versionOfSecurityProcedure
|
versionOfSecurityProcedure
|
||||||
), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet)
|
), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet)
|
||||||
SicherheitsfunktionKodiert(customer.selectedTanProcedure.securityFunction),
|
SicherheitsfunktionKodiert(bank.selectedTanProcedure.securityFunction),
|
||||||
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
|
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
|
||||||
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
|
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
|
||||||
RolleDesSicherheitslieferantenKodiert(), // allowed: 1
|
RolleDesSicherheitslieferantenKodiert(), // allowed: 1
|
||||||
SicherheitsidentifikationDetails(customer.customerSystemId),
|
SicherheitsidentifikationDetails(bank.customerSystemId),
|
||||||
// "Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet.
|
// "Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet.
|
||||||
Sicherheitsreferenznummer(1), // TODO: is this always 1?
|
Sicherheitsreferenznummer(1), // TODO: is this always 1?
|
||||||
SicherheitsdatumUndUhrzeit(date, time),
|
SicherheitsdatumUndUhrzeit(date, time),
|
||||||
HashalgorithmusDatenelementgruppe(),
|
HashalgorithmusDatenelementgruppe(),
|
||||||
SignaturalgorithmusDatenelementgruppe(algorithm, mode),
|
SignaturalgorithmusDatenelementgruppe(algorithm, mode),
|
||||||
Schluesselname(bank.countryCode, bank.bankCode, customer.customerId, Schluesselart.Signierschluessel, keyNumber, keyVersion)
|
Schluesselname(bank.countryCode, bank.bankCode, bank.customerId, Schluesselart.Signierschluessel, keyNumber, keyVersion)
|
||||||
))
|
))
|
|
@ -14,8 +14,8 @@ open class Verarbeitungsvorbereitung(
|
||||||
) : Segment(listOf(
|
) : Segment(listOf(
|
||||||
Segmentkopf(CustomerSegmentId.ProcessingPreparation, 3, segmentNumber),
|
Segmentkopf(CustomerSegmentId.ProcessingPreparation, 3, segmentNumber),
|
||||||
BPDVersion(baseData.bank.bpdVersion, Existenzstatus.Mandatory),
|
BPDVersion(baseData.bank.bpdVersion, Existenzstatus.Mandatory),
|
||||||
UPDVersion(baseData.customer.updVersion, Existenzstatus.Mandatory),
|
UPDVersion(baseData.bank.updVersion, Existenzstatus.Mandatory),
|
||||||
DialogspracheDatenelement(baseData.customer.selectedLanguage, Existenzstatus.Mandatory),
|
DialogspracheDatenelement(baseData.bank.selectedLanguage, Existenzstatus.Mandatory),
|
||||||
Produktbezeichnung(baseData.product.name, Existenzstatus.Mandatory),
|
Produktbezeichnung(baseData.product.name, Existenzstatus.Mandatory),
|
||||||
Produktversion(baseData.product.version, Existenzstatus.Mandatory)
|
Produktversion(baseData.product.version, Existenzstatus.Mandatory)
|
||||||
))
|
))
|
|
@ -14,7 +14,6 @@ import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.sig
|
||||||
import net.dankito.banking.fints.messages.segmente.Segment
|
import net.dankito.banking.fints.messages.segmente.Segment
|
||||||
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
||||||
import net.dankito.banking.fints.model.BankData
|
import net.dankito.banking.fints.model.BankData
|
||||||
import net.dankito.banking.fints.model.CustomerData
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +34,6 @@ import net.dankito.banking.fints.model.CustomerData
|
||||||
*/
|
*/
|
||||||
open class Verschluesselungskopf(
|
open class Verschluesselungskopf(
|
||||||
bank: BankData,
|
bank: BankData,
|
||||||
customer: CustomerData,
|
|
||||||
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens,
|
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens,
|
||||||
date: Int,
|
date: Int,
|
||||||
time: Int,
|
time: Int,
|
||||||
|
@ -51,10 +49,10 @@ open class Verschluesselungskopf(
|
||||||
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, versionOfSecurityProcedure), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet)
|
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, versionOfSecurityProcedure), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet)
|
||||||
SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext),
|
SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext),
|
||||||
RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4
|
RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4
|
||||||
SicherheitsidentifikationDetails(customer.customerSystemId),
|
SicherheitsidentifikationDetails(bank.customerSystemId),
|
||||||
SicherheitsdatumUndUhrzeit(date, time),
|
SicherheitsdatumUndUhrzeit(date, time),
|
||||||
VerschluesselungsalgorithmusDatenelementgruppe(mode, encryptionAlgorithm),
|
VerschluesselungsalgorithmusDatenelementgruppe(mode, encryptionAlgorithm),
|
||||||
Schluesselname(bank.countryCode, bank.bankCode, customer.customerId, key, keyNumber, keyVersion),
|
Schluesselname(bank.countryCode, bank.bankCode, bank.customerId, key, keyNumber, keyVersion),
|
||||||
KomprimierungsfunktionDatenelement(algorithm),
|
KomprimierungsfunktionDatenelement(algorithm),
|
||||||
NotAllowedDatenelement() // Certificate not applicapable for PIN/TAN
|
NotAllowedDatenelement() // Certificate not applicapable for PIN/TAN
|
||||||
))
|
))
|
|
@ -3,14 +3,13 @@ package net.dankito.banking.fints.messages.segmente.implementierte.sepa
|
||||||
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||||
import net.dankito.banking.fints.model.AccountData
|
import net.dankito.banking.fints.model.AccountData
|
||||||
import net.dankito.banking.fints.model.BankTransferData
|
import net.dankito.banking.fints.model.BankTransferData
|
||||||
import net.dankito.banking.fints.model.CustomerData
|
|
||||||
|
|
||||||
|
|
||||||
open class SepaBankTransferBase(
|
open class SepaBankTransferBase(
|
||||||
segmentId: CustomerSegmentId,
|
segmentId: CustomerSegmentId,
|
||||||
segmentNumber: Int,
|
segmentNumber: Int,
|
||||||
sepaDescriptorUrn: String,
|
sepaDescriptorUrn: String,
|
||||||
debitor: CustomerData,
|
debitorName: String,
|
||||||
account: AccountData,
|
account: AccountData,
|
||||||
debitorBic: String,
|
debitorBic: String,
|
||||||
data: BankTransferData,
|
data: BankTransferData,
|
||||||
|
@ -26,7 +25,7 @@ open class SepaBankTransferBase(
|
||||||
debitorBic,
|
debitorBic,
|
||||||
mapOf(
|
mapOf(
|
||||||
SepaMessageCreator.NumberOfTransactionsKey to "1", // TODO: may someday support more then one transaction per file
|
SepaMessageCreator.NumberOfTransactionsKey to "1", // TODO: may someday support more then one transaction per file
|
||||||
"DebitorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(debitor.name),
|
"DebitorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(debitorName),
|
||||||
"DebitorIban" to account.iban!!,
|
"DebitorIban" to account.iban!!,
|
||||||
"DebitorBic" to debitorBic,
|
"DebitorBic" to debitorBic,
|
||||||
"CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName),
|
"CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName),
|
||||||
|
|
|
@ -16,7 +16,6 @@ import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.acc
|
||||||
import net.dankito.banking.fints.messages.segmente.Segment
|
import net.dankito.banking.fints.messages.segmente.Segment
|
||||||
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||||
import net.dankito.banking.fints.model.BankData
|
import net.dankito.banking.fints.model.BankData
|
||||||
import net.dankito.banking.fints.model.CustomerData
|
|
||||||
import net.dankito.banking.fints.response.segments.ChangeTanMediaParameters
|
import net.dankito.banking.fints.response.segments.ChangeTanMediaParameters
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +40,6 @@ open class TanGeneratorTanMediumAnOderUmmelden(
|
||||||
segmentVersion: Int,
|
segmentVersion: Int,
|
||||||
segmentNumber: Int,
|
segmentNumber: Int,
|
||||||
bank: BankData,
|
bank: BankData,
|
||||||
customer: CustomerData,
|
|
||||||
newActiveTanMedium: TanGeneratorTanMedium,
|
newActiveTanMedium: TanGeneratorTanMedium,
|
||||||
/**
|
/**
|
||||||
* Has to be set if „Eingabe von ATC und TAN erforderlich“ (BPD)=“J“
|
* Has to be set if „Eingabe von ATC und TAN erforderlich“ (BPD)=“J“
|
||||||
|
@ -55,7 +53,6 @@ open class TanGeneratorTanMediumAnOderUmmelden(
|
||||||
* An optional field and only used in version 3
|
* An optional field and only used in version 3
|
||||||
*/
|
*/
|
||||||
iccsn: String? = null,
|
iccsn: String? = null,
|
||||||
|
|
||||||
parameters: ChangeTanMediaParameters = bank.changeTanMediumParameters!!
|
parameters: ChangeTanMediaParameters = bank.changeTanMediumParameters!!
|
||||||
)
|
)
|
||||||
: Segment(listOf(
|
: Segment(listOf(
|
||||||
|
@ -64,8 +61,8 @@ open class TanGeneratorTanMediumAnOderUmmelden(
|
||||||
AlphanumerischesDatenelement(newActiveTanMedium.cardNumber, Existenzstatus.Mandatory),
|
AlphanumerischesDatenelement(newActiveTanMedium.cardNumber, Existenzstatus.Mandatory),
|
||||||
AlphanumerischesDatenelement(newActiveTanMedium.cardSequenceNumber, if (parameters.enteringCardSequenceNumberRequired) Existenzstatus.Mandatory else Existenzstatus.NotAllowed),
|
AlphanumerischesDatenelement(newActiveTanMedium.cardSequenceNumber, if (parameters.enteringCardSequenceNumberRequired) Existenzstatus.Mandatory else Existenzstatus.NotAllowed),
|
||||||
if (segmentVersion > 1) NumerischesDatenelement(newActiveTanMedium.cardType, 2, if (parameters.enteringCardTypeAllowed) Existenzstatus.Optional else Existenzstatus.NotAllowed) else DoNotPrintDatenelement(),
|
if (segmentVersion > 1) NumerischesDatenelement(newActiveTanMedium.cardType, 2, if (parameters.enteringCardTypeAllowed) Existenzstatus.Optional else Existenzstatus.NotAllowed) else DoNotPrintDatenelement(),
|
||||||
if (segmentVersion == 2) Kontoverbindung(customer.accounts.first()) else DoNotPrintDatenelement(),
|
if (segmentVersion == 2) Kontoverbindung(bank.accounts.first()) else DoNotPrintDatenelement(),
|
||||||
if (segmentVersion >= 3 && parameters.accountInfoRequired) KontoverbindungInternational(customer.accounts.first(), bank) else DoNotPrintDatenelement(),
|
if (segmentVersion >= 3 && parameters.accountInfoRequired) KontoverbindungInternational(bank.accounts.first(), bank) else DoNotPrintDatenelement(),
|
||||||
if (segmentVersion >= 2) Datum(newActiveTanMedium.validFrom, Existenzstatus.Optional) else DoNotPrintDatenelement(),
|
if (segmentVersion >= 2) Datum(newActiveTanMedium.validFrom, Existenzstatus.Optional) else DoNotPrintDatenelement(),
|
||||||
if (segmentVersion >= 2) Datum(newActiveTanMedium.validTo, Existenzstatus.Optional) else DoNotPrintDatenelement(),
|
if (segmentVersion >= 2) Datum(newActiveTanMedium.validTo, Existenzstatus.Optional) else DoNotPrintDatenelement(),
|
||||||
if (segmentVersion >= 3) AlphanumerischesDatenelement(iccsn, Existenzstatus.Optional, 19) else DoNotPrintDatenelement(),
|
if (segmentVersion >= 3) AlphanumerischesDatenelement(iccsn, Existenzstatus.Optional, 19) else DoNotPrintDatenelement(),
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package net.dankito.banking.fints.model
|
package net.dankito.banking.fints.model
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.BPDVersion
|
import net.dankito.banking.fints.messages.datenelemente.implementierte.*
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache
|
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.HbciVersion
|
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.dankito.banking.fints.response.segments.ChangeTanMediaParameters
|
import net.dankito.banking.fints.response.segments.ChangeTanMediaParameters
|
||||||
import net.dankito.banking.fints.response.segments.JobParameters
|
import net.dankito.banking.fints.response.segments.JobParameters
|
||||||
import net.dankito.banking.fints.response.segments.PinInfo
|
import net.dankito.banking.fints.response.segments.PinInfo
|
||||||
|
@ -11,12 +11,31 @@ import net.dankito.banking.fints.response.segments.PinInfo
|
||||||
|
|
||||||
open class BankData(
|
open class BankData(
|
||||||
var bankCode: String,
|
var bankCode: String,
|
||||||
|
var customerId: String,
|
||||||
|
var pin: String,
|
||||||
var finTs3ServerAddress: String,
|
var finTs3ServerAddress: String,
|
||||||
var bic: String,
|
var bic: String,
|
||||||
var name: String = "",
|
|
||||||
|
var bankName: String = "",
|
||||||
var countryCode: Int = Laenderkennzeichen.Germany, // TODO: currently there are only German banks. But change this if ever other countries get supported
|
var countryCode: Int = Laenderkennzeichen.Germany, // TODO: currently there are only German banks. But change this if ever other countries get supported
|
||||||
var bpdVersion: Int = BPDVersion.VersionNotReceivedYet,
|
var bpdVersion: Int = BPDVersion.VersionNotReceivedYet,
|
||||||
|
|
||||||
|
var userId: String = customerId,
|
||||||
|
var customerName: String = "",
|
||||||
|
var updVersion: Int = UPDVersion.VersionNotReceivedYet,
|
||||||
|
|
||||||
|
var tanProceduresSupportedByBank: List<TanProcedure> = listOf(),
|
||||||
|
var tanProceduresAvailableForUser: List<TanProcedure> = listOf(),
|
||||||
|
var selectedTanProcedure: TanProcedure = TanProcedureNotSelected,
|
||||||
|
var tanMedia: List<TanMedium> = listOf(),
|
||||||
|
var changeTanMediumParameters: ChangeTanMediaParameters? = null,
|
||||||
|
var pinInfo: PinInfo? = null,
|
||||||
|
|
||||||
|
var supportedLanguages: List<Dialogsprache> = listOf(),
|
||||||
|
var selectedLanguage: Dialogsprache = Dialogsprache.Default,
|
||||||
|
var customerSystemId: String = KundensystemID.Anonymous,
|
||||||
|
var customerSystemStatus: KundensystemStatusWerte = KundensystemStatus.SynchronizingCustomerSystemId,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximale Anzahl an Geschäftsvorfallsarten, die pro Nachricht zulässig ist.
|
* Maximale Anzahl an Geschäftsvorfallsarten, die pro Nachricht zulässig ist.
|
||||||
* Der Wert ‚0’ gibt an, dass keine Restriktionen bzgl. der Anzahl an Geschäftsvorfallsarten bestehen.
|
* Der Wert ‚0’ gibt an, dass keine Restriktionen bzgl. der Anzahl an Geschäftsvorfallsarten bestehen.
|
||||||
|
@ -24,32 +43,66 @@ open class BankData(
|
||||||
var countMaxJobsPerMessage: Int = 0,
|
var countMaxJobsPerMessage: Int = 0,
|
||||||
|
|
||||||
var supportedHbciVersions: List<HbciVersion> = listOf(),
|
var supportedHbciVersions: List<HbciVersion> = listOf(),
|
||||||
var supportedTanProcedures: List<TanProcedure> = listOf(),
|
|
||||||
var changeTanMediumParameters: ChangeTanMediaParameters? = null,
|
|
||||||
var pinInfo: PinInfo? = null,
|
|
||||||
var supportedLanguages: List<Dialogsprache> = listOf(),
|
|
||||||
var supportedJobs: List<JobParameters> = listOf()
|
var supportedJobs: List<JobParameters> = listOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val SecurityFunctionNotSelected = Sicherheitsfunktion.Einschritt_Verfahren
|
||||||
|
|
||||||
internal constructor() : this("", "", "") // for object deserializers
|
val TanProcedureNotSelected = TanProcedure("NOT_SELECTED", SecurityFunctionNotSelected, TanProcedureType.EnterTan)
|
||||||
|
|
||||||
|
// TODO: is the BIC really needed at anonymous dialog init?
|
||||||
|
fun anonymous(bankCode: String, finTs3ServerAddress: String, bic: String): BankData {
|
||||||
|
return BankData(bankCode, KundenID.Anonymous, "", finTs3ServerAddress, bic, customerSystemStatus = KundensystemStatusWerte.NichtBenoetigt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this("", "", "", "", "") // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// for UniCredit / HypVereinsbank for online banking '70020270' has to be used as bank code
|
// for UniCredit / HypVereinsbank for online banking '70020270' has to be used as bank code
|
||||||
if (name.contains("unicredit", true)) {
|
if (bankName.contains("unicredit", true)) {
|
||||||
bankCode = "70020270"
|
bankCode = "70020270"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected val _accounts = mutableListOf<AccountData>()
|
||||||
|
|
||||||
|
open val accounts: List<AccountData>
|
||||||
|
get() = ArrayList(_accounts)
|
||||||
|
|
||||||
|
|
||||||
|
open val isTanProcedureSelected: Boolean
|
||||||
|
get() = selectedTanProcedure != TanProcedureNotSelected
|
||||||
|
|
||||||
|
|
||||||
|
open fun addAccount(account: AccountData) {
|
||||||
|
_accounts.add(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun removeAccount(account: AccountData) {
|
||||||
|
_accounts.remove(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun resetBpdVersion() {
|
open fun resetBpdVersion() {
|
||||||
bpdVersion = BPDVersion.VersionNotReceivedYet
|
bpdVersion = BPDVersion.VersionNotReceivedYet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun resetUpdVersion() {
|
||||||
|
updVersion = UPDVersion.VersionNotReceivedYet
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun resetSelectedTanProcedure() {
|
||||||
|
selectedTanProcedure = TanProcedureNotSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$name ($bankCode)"
|
return "$bankCode $customerId"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
package net.dankito.banking.fints.model
|
|
||||||
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.*
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
|
||||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
|
||||||
|
|
||||||
|
|
||||||
open class CustomerData(
|
|
||||||
var customerId: String,
|
|
||||||
var pin: String,
|
|
||||||
var userId: String = customerId,
|
|
||||||
var name: String = "",
|
|
||||||
var updVersion: Int = UPDVersion.VersionNotReceivedYet,
|
|
||||||
var supportedTanProcedures: List<TanProcedure> = listOf(),
|
|
||||||
var selectedTanProcedure: TanProcedure = TanProcedureNotSelected,
|
|
||||||
var tanMedia: List<TanMedium> = listOf(),
|
|
||||||
var selectedLanguage: Dialogsprache = Dialogsprache.Default,
|
|
||||||
var customerSystemId: String = KundensystemID.Anonymous,
|
|
||||||
var customerSystemStatus: KundensystemStatusWerte = KundensystemStatus.SynchronizingCustomerSystemId
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val SecurityFunctionNotSelected = Sicherheitsfunktion.Einschritt_Verfahren
|
|
||||||
|
|
||||||
val TanProcedureNotSelected = TanProcedure("NOT_SELECTED", SecurityFunctionNotSelected, TanProcedureType.EnterTan)
|
|
||||||
|
|
||||||
val Anonymous = CustomerData(KundenID.Anonymous, "", customerSystemStatus = KundensystemStatusWerte.NichtBenoetigt)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// for Java
|
|
||||||
constructor(customerId: String, pin: String) : this(customerId, pin, customerId)
|
|
||||||
|
|
||||||
internal constructor() : this("", "") // for object deserializers
|
|
||||||
|
|
||||||
|
|
||||||
protected val accountsField = mutableListOf<AccountData>()
|
|
||||||
|
|
||||||
open val accounts: List<AccountData>
|
|
||||||
get() = ArrayList(accountsField)
|
|
||||||
|
|
||||||
|
|
||||||
open val isTanProcedureSelected: Boolean
|
|
||||||
get() = selectedTanProcedure != TanProcedureNotSelected
|
|
||||||
|
|
||||||
|
|
||||||
open fun resetSelectedTanProcedure() {
|
|
||||||
selectedTanProcedure = TanProcedureNotSelected
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun resetUpdVersion() {
|
|
||||||
updVersion = UPDVersion.VersionNotReceivedYet
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open fun addAccount(account: AccountData) {
|
|
||||||
accountsField.add(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun removeAccount(account: AccountData) {
|
|
||||||
accountsField.remove(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return customerId
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -7,17 +7,16 @@ import net.dankito.banking.fints.response.Response
|
||||||
|
|
||||||
open class DialogContext(
|
open class DialogContext(
|
||||||
bank: BankData,
|
bank: BankData,
|
||||||
customer: CustomerData,
|
|
||||||
product: ProductData,
|
product: ProductData,
|
||||||
var abortIfTanIsRequired: Boolean = false,
|
var abortIfTanIsRequired: Boolean = false,
|
||||||
var currentMessage: MessageBuilderResult? = null,
|
var currentMessage: MessageBuilderResult? = null,
|
||||||
var dialogId: String = InitialDialogId,
|
var dialogId: String = InitialDialogId,
|
||||||
var response: Response? = null,
|
var response: Response? = null,
|
||||||
var didBankCloseDialog: Boolean = false,
|
var didBankCloseDialog: Boolean = false,
|
||||||
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2, // for PinTan almost always the case except for getting a user's TAN procedures
|
versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2,
|
||||||
var previousMessageInDialog: MessageBuilderResult? = null,
|
var previousMessageInDialog: MessageBuilderResult? = null, // for PinTan almost always the case except for getting a user's TAN procedures
|
||||||
var chunkedResponseHandler: ((Response) -> Unit)? = null
|
var chunkedResponseHandler: ((Response) -> Unit)? = null
|
||||||
) : MessageBaseData(bank, customer, product, versionOfSecurityProcedure) {
|
) : MessageBaseData(bank, product, versionOfSecurityProcedure) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val InitialDialogId = "0"
|
const val InitialDialogId = "0"
|
||||||
|
|
|
@ -5,7 +5,6 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.
|
||||||
|
|
||||||
open class MessageBaseData(
|
open class MessageBaseData(
|
||||||
val bank: BankData,
|
val bank: BankData,
|
||||||
val customer: CustomerData,
|
|
||||||
val product: ProductData,
|
val product: ProductData,
|
||||||
val versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PinTanDefaultVersion
|
val versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PinTanDefaultVersion
|
||||||
)
|
)
|
|
@ -6,7 +6,7 @@ import net.dankito.utils.multiplatform.Date
|
||||||
open class MessageLogEntry(
|
open class MessageLogEntry(
|
||||||
val message: String,
|
val message: String,
|
||||||
val time: Date,
|
val time: Date,
|
||||||
val customer: CustomerData
|
val bank: BankData
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import net.dankito.banking.fints.response.Response
|
||||||
open class AddAccountResponse(
|
open class AddAccountResponse(
|
||||||
response: Response,
|
response: Response,
|
||||||
val bank: BankData,
|
val bank: BankData,
|
||||||
val customer: CustomerData,
|
|
||||||
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
|
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
|
||||||
bookedTransactionsOfLast90Days: List<AccountTransaction> = listOf(),
|
bookedTransactionsOfLast90Days: List<AccountTransaction> = listOf(),
|
||||||
unbookedTransactionsOfLast90Days: List<Any> = listOf(),
|
unbookedTransactionsOfLast90Days: List<Any> = listOf(),
|
||||||
|
|
|
@ -10,4 +10,7 @@ open class ChangeTanMediaParameters(
|
||||||
val accountInfoRequired: Boolean,
|
val accountInfoRequired: Boolean,
|
||||||
val allowedCardTypes: List<Int>
|
val allowedCardTypes: List<Int>
|
||||||
)
|
)
|
||||||
: JobParameters(parameters)
|
: JobParameters(parameters) {
|
||||||
|
|
||||||
|
internal constructor() : this(JobParameters(), false, false, false, false, false, listOf()) // for object deserializers
|
||||||
|
}
|
|
@ -12,4 +12,8 @@ open class PinInfo(
|
||||||
val customerIdHint: String?,
|
val customerIdHint: String?,
|
||||||
val jobTanConfiguration: List<JobTanConfiguration>
|
val jobTanConfiguration: List<JobTanConfiguration>
|
||||||
)
|
)
|
||||||
: JobParameters(parameters)
|
: JobParameters(parameters) {
|
||||||
|
|
||||||
|
internal constructor() : this(JobParameters(), null, null, null, null, null, listOf()) // for object deserializers
|
||||||
|
|
||||||
|
}
|
|
@ -30,15 +30,13 @@ abstract class FinTsTestBase {
|
||||||
|
|
||||||
const val Bic = "ABCDDEMM123"
|
const val Bic = "ABCDDEMM123"
|
||||||
|
|
||||||
val Bank = BankData(BankCode, BankFinTsServerAddress, Bic, "", BankCountryCode)
|
|
||||||
|
|
||||||
val Language = Dialogsprache.German
|
val Language = Dialogsprache.German
|
||||||
|
|
||||||
val SecurityFunction = Sicherheitsfunktion.PIN_TAN_910
|
val SecurityFunction = Sicherheitsfunktion.PIN_TAN_910
|
||||||
|
|
||||||
const val ControlReference = "4477"
|
const val ControlReference = "4477"
|
||||||
|
|
||||||
val Customer = CustomerData(CustomerId, Pin, selectedTanProcedure = TanProcedure("chipTAN-optisch", SecurityFunction, TanProcedureType.ChipTanFlickercode), selectedLanguage = Language)
|
val Bank = BankData(BankCode, CustomerId, Pin, BankFinTsServerAddress, Bic, "", BankCountryCode, selectedTanProcedure = TanProcedure("chipTAN-optisch", SecurityFunction, TanProcedureType.ChipTanFlickercode), selectedLanguage = Language)
|
||||||
|
|
||||||
val Currency = "EUR"
|
val Currency = "EUR"
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
fun createAnonymousDialogInitMessage() {
|
fun createAnonymousDialogInitMessage() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val dialogContext = DialogContext(Bank, CustomerData.Anonymous, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createAnonymousDialogInitMessage(dialogContext).createdMessage
|
val result = underTest.createAnonymousDialogInitMessage(dialogContext).createdMessage
|
||||||
|
@ -62,7 +62,7 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val dialogId = createDialogId()
|
val dialogId = createDialogId()
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product, false, null, dialogId)
|
val dialogContext = DialogContext(Bank, Product, false, null, dialogId)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createAnonymousDialogEndMessage(dialogContext).createdMessage ?: ""
|
val result = underTest.createAnonymousDialogEndMessage(dialogContext).createdMessage ?: ""
|
||||||
|
@ -80,7 +80,7 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
fun createDialogInitMessage() {
|
fun createDialogInitMessage() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createSynchronizeCustomerSystemIdMessage(dialogContext).createdMessage ?: ""
|
val result = underTest.createSynchronizeCustomerSystemIdMessage(dialogContext).createdMessage ?: ""
|
||||||
|
@ -104,7 +104,7 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val dialogId = createDialogId()
|
val dialogId = createDialogId()
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product, false, null, dialogId)
|
val dialogContext = DialogContext(Bank, Product, false, null, dialogId)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createDialogEndMessage(dialogContext).createdMessage ?: ""
|
val result = underTest.createDialogEndMessage(dialogContext).createdMessage ?: ""
|
||||||
|
@ -125,7 +125,7 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
fun createGetTransactionsMessage_JobIsNotAllowed() {
|
fun createGetTransactionsMessage_JobIsNotAllowed() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Account, dialogContext)
|
val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Account, dialogContext)
|
||||||
|
@ -142,8 +142,8 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
val getTransactionsJobWithPreviousVersion = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:72:4")
|
val getTransactionsJobWithPreviousVersion = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:72:4")
|
||||||
Bank.supportedJobs = listOf(getTransactionsJob)
|
Bank.supportedJobs = listOf(getTransactionsJob)
|
||||||
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJobWithPreviousVersion))
|
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJobWithPreviousVersion))
|
||||||
Customer.addAccount(account)
|
Bank.addAccount(account)
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), account, dialogContext)
|
val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), account, dialogContext)
|
||||||
|
@ -160,8 +160,8 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5")
|
val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5")
|
||||||
Bank.supportedJobs = listOf(getTransactionsJob)
|
Bank.supportedJobs = listOf(getTransactionsJob)
|
||||||
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob))
|
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob))
|
||||||
Customer.addAccount(account)
|
Bank.addAccount(account)
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
val fromDate = Date(2019, Month.August, 6)
|
val fromDate = Date(2019, Month.August, 6)
|
||||||
val toDate = Date(2019, Month.October, 21)
|
val toDate = Date(2019, Month.October, 21)
|
||||||
|
@ -191,8 +191,8 @@ class MessageBuilderTest : FinTsTestBase() {
|
||||||
val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5")
|
val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5")
|
||||||
Bank.supportedJobs = listOf(getTransactionsJob)
|
Bank.supportedJobs = listOf(getTransactionsJob)
|
||||||
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob))
|
val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob))
|
||||||
Customer.addAccount(account)
|
Bank.addAccount(account)
|
||||||
val dialogContext = DialogContext(Bank, Customer, Product)
|
val dialogContext = DialogContext(Bank, Product)
|
||||||
|
|
||||||
val fromDate = Date(2019, Month.August, 6)
|
val fromDate = Date(2019, Month.August, 6)
|
||||||
val toDate = Date(2019, Month.October, 21)
|
val toDate = Date(2019, Month.October, 21)
|
||||||
|
|
|
@ -13,7 +13,7 @@ class IdentifikationsSegmentTest : FinTsTestBase() {
|
||||||
fun format() {
|
fun format() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val underTest = IdentifikationsSegment(2, MessageBaseData(Bank, Customer, Product))
|
val underTest = IdentifikationsSegment(2, MessageBaseData(Bank, Product))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.format()
|
val result = underTest.format()
|
||||||
|
|
|
@ -15,7 +15,7 @@ class SignaturkopfTest : FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val controlReference = "1902675680"
|
val controlReference = "1902675680"
|
||||||
|
|
||||||
val underTest = PinTanSignaturkopf(2, MessageBaseData(Bank, Customer, Product),
|
val underTest = PinTanSignaturkopf(2, MessageBaseData(Bank, Product),
|
||||||
controlReference, Date, Time)
|
controlReference, Date, Time)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
|
@ -14,7 +14,7 @@ class VerschluesselungskopfTest : FinTsTestBase() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
|
|
||||||
val underTest = PinTanVerschluesselungskopf(MessageBaseData(Bank, Customer, Product), Date, Time)
|
val underTest = PinTanVerschluesselungskopf(MessageBaseData(Bank, Product), Date, Time)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = underTest.format()
|
val result = underTest.format()
|
||||||
|
|
|
@ -30,7 +30,7 @@ class SepaBankTransferBaseTest {
|
||||||
// given
|
// given
|
||||||
val underTest = SepaBankTransferBase(CustomerSegmentId.SepaBankTransfer, segmentNumber,
|
val underTest = SepaBankTransferBase(CustomerSegmentId.SepaBankTransfer, segmentNumber,
|
||||||
"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
|
||||||
CustomerData("", "", "", debitorName),
|
debitorName,
|
||||||
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
||||||
debitorBic,
|
debitorBic,
|
||||||
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
||||||
|
@ -52,7 +52,7 @@ class SepaBankTransferBaseTest {
|
||||||
// given
|
// given
|
||||||
val underTest = SepaBankTransferBase(CustomerSegmentId.SepaBankTransfer, segmentNumber,
|
val underTest = SepaBankTransferBase(CustomerSegmentId.SepaBankTransfer, segmentNumber,
|
||||||
"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03",
|
"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03",
|
||||||
CustomerData("", "", "", debitorName),
|
debitorName,
|
||||||
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
||||||
debitorBic,
|
debitorBic,
|
||||||
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
||||||
|
|
|
@ -32,7 +32,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Customer.addAccount(Account)
|
Bank.addAccount(Account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -59,7 +59,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -76,7 +76,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -93,7 +93,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(1, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -111,7 +111,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -128,7 +128,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, true, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -145,7 +145,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, false, false, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -162,7 +162,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, true, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, false, false, true, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -179,7 +179,7 @@ class TanGeneratorTanMediumAnOderUmmeldenTest: FinTsTestBase() {
|
||||||
// given
|
// given
|
||||||
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, true, false, listOf())
|
val parameters = ChangeTanMediaParameters(createEmptyJobParameters(), false, true, true, true, false, listOf())
|
||||||
|
|
||||||
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, Customer, NewActiveTanMedium, TAN, ATC, null, parameters)
|
val underTest = TanGeneratorTanMediumAnOderUmmelden(2, SegmentNumber, Bank, NewActiveTanMedium, TAN, ATC, null, parameters)
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
|
@ -60,13 +60,13 @@ open class FinTsClientTestBase {
|
||||||
callback(suggestedTanProcedure) // simply return suggestedTanProcedure as in most cases it's the best fitting one
|
callback(suggestedTanProcedure) // simply return suggestedTanProcedure as in most cases it's the best fitting one
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||||
didAskUserToEnterTan = true
|
didAskUserToEnterTan = true
|
||||||
|
|
||||||
callback(EnterTanResult.userDidNotEnterTan())
|
callback(EnterTanResult.userDidNotEnterTan())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||||
fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " +
|
fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " +
|
||||||
"(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.")
|
"(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.")
|
||||||
}
|
}
|
||||||
|
@ -77,11 +77,10 @@ open class FinTsClientTestBase {
|
||||||
private val underTest = FinTsClient(callback, KtorWebClient(), PureKotlinBase64Service())
|
private val underTest = FinTsClient(callback, KtorWebClient(), PureKotlinBase64Service())
|
||||||
|
|
||||||
|
|
||||||
private val BankDataAnonymous = BankData("10070000","https://fints.deutsche-bank.de/", "DEUTDEBBXXX")
|
private val BankDataAnonymous = BankData.anonymous("10070000", "https://fints.deutsche-bank.de/", "DEUTDEBBXXX")
|
||||||
|
|
||||||
private val bankInfo = InMemoryBankFinder().findBankByBankCode(BankCode).first()
|
private val bankInfo = InMemoryBankFinder().findBankByBankCode(BankCode).first()
|
||||||
private val Bank = BankData(bankInfo.bankCode, bankInfo.pinTanAddress ?: "", bankInfo.bic, bankInfo.name)
|
private val Bank = BankData(bankInfo.bankCode, CustomerId, Password, bankInfo.pinTanAddress ?: "", bankInfo.bic, bankInfo.name)
|
||||||
private val Customer = CustomerData(CustomerId, Password)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,10 +93,10 @@ open class FinTsClientTestBase {
|
||||||
// then
|
// then
|
||||||
expect(result.isSuccessful).isTrue()
|
expect(result.isSuccessful).isTrue()
|
||||||
expect(BankDataAnonymous.supportedHbciVersions).isNotEmpty()
|
expect(BankDataAnonymous.supportedHbciVersions).isNotEmpty()
|
||||||
expect(BankDataAnonymous.supportedTanProcedures).isNotEmpty()
|
expect(BankDataAnonymous.tanProceduresSupportedByBank).isNotEmpty()
|
||||||
expect(BankDataAnonymous.supportedJobs).isNotEmpty()
|
expect(BankDataAnonymous.supportedJobs).isNotEmpty()
|
||||||
expect(BankDataAnonymous.supportedLanguages).isNotEmpty()
|
expect(BankDataAnonymous.supportedLanguages).isNotEmpty()
|
||||||
expect(BankDataAnonymous.name).isNotEmpty()
|
expect(BankDataAnonymous.bankName).isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +110,7 @@ open class FinTsClientTestBase {
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
underTest.addAccountAsync(Bank, Customer) {
|
underTest.addAccountAsync(Bank) {
|
||||||
response.set(it)
|
response.set(it)
|
||||||
countDownLatch.countDown()
|
countDownLatch.countDown()
|
||||||
}
|
}
|
||||||
|
@ -125,19 +124,19 @@ open class FinTsClientTestBase {
|
||||||
|
|
||||||
expect(didAskUserForTanProcedure).isFalse()
|
expect(didAskUserForTanProcedure).isFalse()
|
||||||
|
|
||||||
expect(Bank.name).isNotEmpty()
|
expect(Bank.bankName).isNotEmpty()
|
||||||
expect(Bank.supportedJobs).isNotEmpty() // supported jobs are now known
|
expect(Bank.supportedJobs).isNotEmpty() // supported jobs are now known
|
||||||
expect(Bank.supportedTanProcedures).isNotEmpty() // supported tan procedures are now known
|
expect(Bank.tanProceduresSupportedByBank).isNotEmpty() // supported tan procedures are now known
|
||||||
expect(Bank.supportedHbciVersions).isNotEmpty() // supported HBIC versions are now known
|
expect(Bank.supportedHbciVersions).isNotEmpty() // supported HBIC versions are now known
|
||||||
expect(Bank.supportedLanguages).isNotEmpty() // supported languages are now known
|
expect(Bank.supportedLanguages).isNotEmpty() // supported languages are now known
|
||||||
|
|
||||||
expect(Customer.name).isNotEmpty()
|
expect(Bank.customerName).isNotEmpty()
|
||||||
expect(Customer.supportedTanProcedures).isNotEmpty()
|
expect(Bank.tanProceduresAvailableForUser).isNotEmpty()
|
||||||
expect(Customer.selectedLanguage).notToBe(Dialogsprache.Default) // language is set now
|
expect(Bank.selectedLanguage).notToBe(Dialogsprache.Default) // language is set now
|
||||||
expect(Customer.customerSystemId).notToBe(KundensystemStatus.SynchronizingCustomerSystemId.code) // customer system id is now set
|
expect(Bank.customerSystemId).notToBe(KundensystemStatus.SynchronizingCustomerSystemId.code) // customer system id is now set
|
||||||
expect(Customer.customerSystemStatus).toBe(KundensystemStatusWerte.Benoetigt) // customerSystemStatus is set now
|
expect(Bank.customerSystemStatus).toBe(KundensystemStatusWerte.Benoetigt) // customerSystemStatus is set now
|
||||||
expect(Customer.accounts).isNotEmpty() // accounts are now known
|
expect(Bank.accounts).isNotEmpty() // accounts are now known
|
||||||
expect(Customer.accounts.first().allowedJobs).isNotEmpty() // allowed jobs are now known
|
expect(Bank.accounts.first().allowedJobs).isNotEmpty() // allowed jobs are now known
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,14 +148,14 @@ open class FinTsClientTestBase {
|
||||||
val response = AtomicReference<GetTransactionsResponse>()
|
val response = AtomicReference<GetTransactionsResponse>()
|
||||||
val countDownLatch = CountDownLatch(1)
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
|
||||||
underTest.addAccountAsync(Bank, Customer) { // retrieve basic data, e.g. accounts
|
underTest.addAccountAsync(Bank) { // retrieve basic data, e.g. accounts
|
||||||
val account = Customer.accounts.firstOrNull { it.supportsFeature(AccountFeature.RetrieveAccountTransactions) }
|
val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.RetrieveAccountTransactions) }
|
||||||
expect(account).withRepresentation("We need at least one account that supports retrieving account transactions (${CustomerSegmentId.AccountTransactionsMt940.id})").notToBeNull()
|
expect(account).withRepresentation("We need at least one account that supports retrieving account transactions (${CustomerSegmentId.AccountTransactionsMt940.id})").notToBeNull()
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
||||||
// some banks support retrieving account transactions of last 90 days without TAN
|
// some banks support retrieving account transactions of last 90 days without TAN
|
||||||
underTest.tryGetTransactionsOfLast90DaysWithoutTan(Bank, Customer, account!!) {
|
underTest.tryGetTransactionsOfLast90DaysWithoutTan(Bank, account!!) {
|
||||||
response.set(it)
|
response.set(it)
|
||||||
countDownLatch.countDown()
|
countDownLatch.countDown()
|
||||||
}
|
}
|
||||||
|
@ -185,11 +184,11 @@ open class FinTsClientTestBase {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(Customer.tanMedia).isEmpty()
|
expect(Bank.tanMedia).isEmpty()
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) { result ->
|
underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) { result ->
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result.isSuccessful).isTrue()
|
expect(result.isSuccessful).isTrue()
|
||||||
|
@ -198,7 +197,7 @@ open class FinTsClientTestBase {
|
||||||
expect(result.tanMediaList!!.usageOption).toBe(TanEinsatzOption.KundeKannGenauEinMediumZuEinerZeitNutzen) // TODO: may adjust to your value
|
expect(result.tanMediaList!!.usageOption).toBe(TanEinsatzOption.KundeKannGenauEinMediumZuEinerZeitNutzen) // TODO: may adjust to your value
|
||||||
expect(result.tanMediaList!!.tanMedia).isNotEmpty()
|
expect(result.tanMediaList!!.tanMedia).isNotEmpty()
|
||||||
|
|
||||||
expect(Customer.tanMedia).isNotEmpty()
|
expect(Bank.tanMedia).isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +207,7 @@ open class FinTsClientTestBase {
|
||||||
|
|
||||||
// when
|
// when
|
||||||
expect {
|
expect {
|
||||||
underTest.getTanMediaList(Bank, Customer, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) { }
|
underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) { }
|
||||||
}.toThrow<UnsupportedOperationException>()
|
}.toThrow<UnsupportedOperationException>()
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,21 +224,21 @@ open class FinTsClientTestBase {
|
||||||
val response = AtomicReference<FinTsClientResponse>()
|
val response = AtomicReference<FinTsClientResponse>()
|
||||||
val countDownLatch = CountDownLatch(1)
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
|
||||||
underTest.addAccountAsync(Bank, Customer) { // retrieve basic data, e.g. accounts
|
underTest.addAccountAsync(Bank) { // retrieve basic data, e.g. accounts
|
||||||
// we need at least one account that supports cash transfer
|
// we need at least one account that supports cash transfer
|
||||||
val account = Customer.accounts.firstOrNull { it.supportsFeature(AccountFeature.TransferMoney) }
|
val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.TransferMoney) }
|
||||||
expect(account).withRepresentation("We need at least one account that supports cash transfer (${CustomerSegmentId.SepaBankTransfer.id})").notToBeNull()
|
expect(account).withRepresentation("We need at least one account that supports cash transfer (${CustomerSegmentId.SepaBankTransfer.id})").notToBeNull()
|
||||||
|
|
||||||
// IBAN should be set
|
// IBAN should be set
|
||||||
expect(account?.iban).withRepresentation("Account IBAN must be set").notToBeNull()
|
expect(account?.iban).withRepresentation("Account IBAN must be set").notToBeNull()
|
||||||
|
|
||||||
// transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2
|
// transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2
|
||||||
val BankTransferData = BankTransferData(Customer.name, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"),
|
val BankTransferData = BankTransferData(Bank.customerName, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"),
|
||||||
"${DateTimeFormatForUniqueBankTransferUsage.format(Date())} Test transaction ${UUID.random()}")
|
"${DateTimeFormatForUniqueBankTransferUsage.format(Date())} Test transaction ${UUID.random()}")
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
underTest.doBankTransferAsync(BankTransferData, Bank, Customer, account) { result ->
|
underTest.doBankTransferAsync(BankTransferData, Bank, account) { result ->
|
||||||
response.set(result)
|
response.set(result)
|
||||||
countDownLatch.countDown()
|
countDownLatch.countDown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ class BanksFinTsDetailsRetriever {
|
||||||
|
|
||||||
|
|
||||||
private fun getAnonymousBankInfo(bank: BankData): Response {
|
private fun getAnonymousBankInfo(bank: BankData): Response {
|
||||||
val dialogContext = DialogContext(bank, CustomerData.Anonymous, product)
|
val dialogContext = DialogContext(bank, product)
|
||||||
val requestBody = messageBuilder.createAnonymousDialogInitMessage(dialogContext)
|
val requestBody = messageBuilder.createAnonymousDialogInitMessage(dialogContext)
|
||||||
|
|
||||||
val anonymousBankInfoResponse = AtomicReference<Response>()
|
val anonymousBankInfoResponse = AtomicReference<Response>()
|
||||||
|
@ -145,7 +145,8 @@ class BanksFinTsDetailsRetriever {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAndSaveBankDetails(bankInfo: BankInfo, responsesFolder: File, csvPrinter: CSVPrinter) = runBlocking {
|
private fun getAndSaveBankDetails(bankInfo: BankInfo, responsesFolder: File, csvPrinter: CSVPrinter) = runBlocking {
|
||||||
val bank = BankData(bankInfo.bankCode, bankInfo.pinTanAddress ?: "", bankInfo.bic, bankInfo.name)
|
val bank = BankData.anonymous(bankInfo.bankCode, bankInfo.pinTanAddress ?: "", bankInfo.bic)
|
||||||
|
bank.bankName = bankInfo.name
|
||||||
|
|
||||||
val anonymousBankInfoResponse = getAnonymousBankInfo(bank)
|
val anonymousBankInfoResponse = getAnonymousBankInfo(bank)
|
||||||
|
|
||||||
|
@ -183,7 +184,7 @@ class BanksFinTsDetailsRetriever {
|
||||||
|
|
||||||
csvPrinter.printRecord(bankInfo.bankCode, bankInfo.name, bankInfo.city,
|
csvPrinter.printRecord(bankInfo.bankCode, bankInfo.name, bankInfo.city,
|
||||||
bank.bpdVersion,
|
bank.bpdVersion,
|
||||||
bank.supportedTanProcedures.joinToString(", ") { it.securityFunction.code + ": " + it.displayName + " (" + it.type + ")" },
|
bank.tanProceduresSupportedByBank.joinToString(", ") { it.securityFunction.code + ": " + it.displayName + " (" + it.type + ")" },
|
||||||
supportedTanProcedures.joinToString(", "),
|
supportedTanProcedures.joinToString(", "),
|
||||||
hhd13Supported,
|
hhd13Supported,
|
||||||
hhd14Supported,
|
hhd14Supported,
|
||||||
|
|
|
@ -47,12 +47,10 @@ open class fints4kBankingClient(
|
||||||
protected var didTryToGetAccountDataFromBank = false
|
protected var didTryToGetAccountDataFromBank = false
|
||||||
|
|
||||||
|
|
||||||
protected val bank = BankData(customer.bankCode, customer.finTsServerAddress, customer.bic, customer.bankName)
|
protected val bank = BankData(customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, customer.bic, customer.bankName)
|
||||||
|
|
||||||
protected val fints4kCustomer = CustomerData(customer.customerId, customer.password)
|
|
||||||
|
|
||||||
|
|
||||||
protected open val client = FinTsClientForCustomer(bank, fints4kCustomer, createFinTsClientCallback(callback), webClient, base64Service)
|
protected open val client = FinTsClientForCustomer(bank, createFinTsClientCallback(callback), webClient, base64Service)
|
||||||
|
|
||||||
|
|
||||||
override val messageLogWithoutSensitiveData: List<MessageLogEntry>
|
override val messageLogWithoutSensitiveData: List<MessageLogEntry>
|
||||||
|
@ -67,7 +65,7 @@ open class fints4kBankingClient(
|
||||||
|
|
||||||
protected open fun handleAddAccountResponse(response: net.dankito.banking.fints.response.client.AddAccountResponse,
|
protected open fun handleAddAccountResponse(response: net.dankito.banking.fints.response.client.AddAccountResponse,
|
||||||
callback: (AddAccountResponse) -> Unit) {
|
callback: (AddAccountResponse) -> Unit) {
|
||||||
mapper.mapCustomer(customer, fints4kCustomer, bank)
|
mapper.mapCustomer(customer, bank)
|
||||||
val mappedResponse = mapper.mapResponse(customer, response)
|
val mappedResponse = mapper.mapResponse(customer, response)
|
||||||
|
|
||||||
saveData()
|
saveData()
|
||||||
|
@ -135,7 +133,7 @@ open class fints4kBankingClient(
|
||||||
|
|
||||||
|
|
||||||
protected open fun findAccountForBankAccount(bankAccount: BankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) {
|
protected open fun findAccountForBankAccount(bankAccount: BankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) {
|
||||||
val mappedAccount = mapper.findAccountForBankAccount(fints4kCustomer, bankAccount)
|
val mappedAccount = mapper.findAccountForBankAccount(bank, bankAccount)
|
||||||
|
|
||||||
if (mappedAccount != null) {
|
if (mappedAccount != null) {
|
||||||
findAccountResult(mappedAccount, null)
|
findAccountResult(mappedAccount, null)
|
||||||
|
@ -144,7 +142,7 @@ open class fints4kBankingClient(
|
||||||
addAccountAsync { response ->
|
addAccountAsync { response ->
|
||||||
didTryToGetAccountDataFromBank = !!! response.isSuccessful
|
didTryToGetAccountDataFromBank = !!! response.isSuccessful
|
||||||
|
|
||||||
findAccountResult(mapper.findAccountForBankAccount(fints4kCustomer, bankAccount),
|
findAccountResult(mapper.findAccountForBankAccount(bank, bankAccount),
|
||||||
response.errorToShowToUser)
|
response.errorToShowToUser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,12 +153,12 @@ open class fints4kBankingClient(
|
||||||
|
|
||||||
|
|
||||||
override fun restoreData() {
|
override fun restoreData() {
|
||||||
val deserializedCustomer = serializer.deserializeObject(getFints4kClientDataFile(), CustomerData::class)
|
val deserializedBank = serializer.deserializeObject(getFints4kClientDataFile(), BankData::class)
|
||||||
|
|
||||||
deserializedCustomer?.let {
|
deserializedBank?.let {
|
||||||
mapper.updateCustomer(fints4kCustomer, deserializedCustomer)
|
mapper.updateCustomer(bank, deserializedBank)
|
||||||
|
|
||||||
mapper.mapCustomer(customer, fints4kCustomer, bank) // TODO: necessary?
|
mapper.mapCustomer(customer, bank) // TODO: necessary?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,9 +166,9 @@ open class fints4kBankingClient(
|
||||||
try {
|
try {
|
||||||
val clientDataFile = getFints4kClientDataFile()
|
val clientDataFile = getFints4kClientDataFile()
|
||||||
|
|
||||||
serializer.serializeObject(fints4kCustomer, clientDataFile)
|
serializer.serializeObject(bank, clientDataFile)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.error("Could not save customer data for $fints4kCustomer", e)
|
log.error("Could not save customer data for $bank", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +177,7 @@ open class fints4kBankingClient(
|
||||||
|
|
||||||
folder.mkdirs()
|
folder.mkdirs()
|
||||||
|
|
||||||
return File(folder, "${bank.bankCode}_${fints4kCustomer.customerId}_$fints4kClientDataFilename")
|
return File(folder, "${bank.bankCode}_${bank.customerId}_$fints4kClientDataFilename")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -190,12 +188,12 @@ open class fints4kBankingClient(
|
||||||
handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure, callback)
|
handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||||
handleEnterTan(customer, tanChallenge, callback, clientCallback)
|
handleEnterTan(bank, tanChallenge, callback, clientCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||||
handleEnterTanGeneratorAtc(customer, tanMedium, callback, clientCallback)
|
handleEnterTanGeneratorAtc(bank, tanMedium, callback, clientCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -206,16 +204,16 @@ open class fints4kBankingClient(
|
||||||
callback(suggestedTanProcedure)
|
callback(suggestedTanProcedure)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun handleEnterTan(customer: CustomerData, tanChallenge: TanChallenge, enterTanCallback: (EnterTanResult) -> Unit, clientCallback: BankingClientCallback) {
|
protected open fun handleEnterTan(bank: BankData, tanChallenge: TanChallenge, enterTanCallback: (EnterTanResult) -> Unit, clientCallback: BankingClientCallback) {
|
||||||
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer)
|
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, bank)
|
||||||
|
|
||||||
clientCallback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) { result ->
|
clientCallback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) { result ->
|
||||||
enterTanCallback(mapper.mapEnterTanResult(result, customer))
|
enterTanCallback(mapper.mapEnterTanResult(result, bank))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun handleEnterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, enterAtcCallback: (EnterTanGeneratorAtcResult) -> Unit, clientCallback: BankingClientCallback) {
|
protected open fun handleEnterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, enterAtcCallback: (EnterTanGeneratorAtcResult) -> Unit, clientCallback: BankingClientCallback) {
|
||||||
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer)
|
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, bank)
|
||||||
|
|
||||||
clientCallback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) { result ->
|
clientCallback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) { result ->
|
||||||
enterAtcCallback(mapper.mapEnterTanGeneratorAtcResult(result))
|
enterAtcCallback(mapper.mapEnterTanGeneratorAtcResult(result))
|
||||||
|
|
|
@ -11,7 +11,6 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.
|
||||||
import net.dankito.banking.fints.model.AccountData
|
import net.dankito.banking.fints.model.AccountData
|
||||||
import net.dankito.banking.fints.model.AccountFeature
|
import net.dankito.banking.fints.model.AccountFeature
|
||||||
import net.dankito.banking.fints.model.BankData
|
import net.dankito.banking.fints.model.BankData
|
||||||
import net.dankito.banking.fints.model.CustomerData
|
|
||||||
import net.dankito.banking.fints.model.Money
|
import net.dankito.banking.fints.model.Money
|
||||||
import net.dankito.banking.fints.response.client.FinTsClientResponse
|
import net.dankito.banking.fints.response.client.FinTsClientResponse
|
||||||
import net.dankito.banking.fints.response.segments.AccountType
|
import net.dankito.banking.fints.response.segments.AccountType
|
||||||
|
@ -61,36 +60,36 @@ open class fints4kModelMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun mapCustomer(customer: Customer, customerData: CustomerData, bank: BankData) {
|
open fun mapCustomer(customer: Customer, bank: BankData) {
|
||||||
customer.bankCode = bank.bankCode
|
customer.bankCode = bank.bankCode
|
||||||
customer.customerId = customerData.customerId
|
customer.customerId = bank.customerId
|
||||||
customer.password = customerData.pin
|
customer.password = bank.pin
|
||||||
customer.finTsServerAddress = bank.finTs3ServerAddress
|
customer.finTsServerAddress = bank.finTs3ServerAddress
|
||||||
customer.bankName = bank.name
|
customer.bankName = bank.bankName
|
||||||
customer.bic = bank.bic
|
customer.bic = bank.bic
|
||||||
customer.customerName = customerData.name
|
customer.customerName = bank.customerName
|
||||||
customer.userId = customerData.userId
|
customer.userId = bank.userId
|
||||||
|
|
||||||
customer.accounts = mapBankAccounts(customer, customerData.accounts)
|
customer.accounts = mapBankAccounts(customer, bank.accounts)
|
||||||
|
|
||||||
updateTanMediaAndProcedures(customer, customerData)
|
updateTanMediaAndProcedures(customer, bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to a fints4k internal mapper
|
// TODO: move to a fints4k internal mapper
|
||||||
open fun updateCustomer(customer: CustomerData, updatedCustomer: CustomerData) {
|
open fun updateCustomer(bank: BankData, updatedBank: BankData) {
|
||||||
customer.pin = updatedCustomer.pin
|
bank.pin = updatedBank.pin
|
||||||
customer.name = updatedCustomer.name
|
bank.customerName = updatedBank.customerName
|
||||||
|
|
||||||
customer.supportedTanProcedures = updatedCustomer.supportedTanProcedures
|
bank.tanProceduresAvailableForUser = updatedBank.tanProceduresAvailableForUser
|
||||||
customer.selectedTanProcedure = updatedCustomer.selectedTanProcedure
|
bank.selectedTanProcedure = updatedBank.selectedTanProcedure
|
||||||
customer.tanMedia = updatedCustomer.tanMedia
|
bank.tanMedia = updatedBank.tanMedia
|
||||||
|
|
||||||
customer.updVersion = updatedCustomer.updVersion
|
bank.updVersion = updatedBank.updVersion
|
||||||
customer.selectedLanguage = updatedCustomer.selectedLanguage
|
bank.selectedLanguage = updatedBank.selectedLanguage
|
||||||
customer.customerSystemId = updatedCustomer.customerSystemId
|
bank.customerSystemId = updatedBank.customerSystemId
|
||||||
customer.customerSystemStatus = updatedCustomer.customerSystemStatus
|
bank.customerSystemStatus = updatedBank.customerSystemStatus
|
||||||
|
|
||||||
updateBankAccounts(customer, updatedCustomer.accounts)
|
updateBankAccounts(bank, updatedBank.accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,25 +139,25 @@ open class fints4kModelMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to a fints4k internal mapper
|
// TODO: move to a fints4k internal mapper
|
||||||
open fun updateBankAccounts(customer: CustomerData, updatedAccounts: List<AccountData>) {
|
open fun updateBankAccounts(bank: BankData, updatedAccounts: List<AccountData>) {
|
||||||
val accounts = customer.accounts
|
val accounts = bank.accounts
|
||||||
|
|
||||||
updatedAccounts.forEach { updatedAccount ->
|
updatedAccounts.forEach { updatedAccount ->
|
||||||
val matchingExistingAccount = findMatchingBankAccount(accounts, updatedAccount)
|
val matchingExistingAccount = findMatchingBankAccount(accounts, updatedAccount)
|
||||||
|
|
||||||
if (matchingExistingAccount == null) {
|
if (matchingExistingAccount == null) {
|
||||||
customer.addAccount(updatedAccount)
|
bank.addAccount(updatedAccount)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
updateBankAccount(matchingExistingAccount, updatedAccount)
|
updateBankAccount(matchingExistingAccount, updatedAccount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customer.accounts.forEach { account ->
|
bank.accounts.forEach { account ->
|
||||||
val updatedAccount = findMatchingBankAccount(updatedAccounts, account)
|
val updatedAccount = findMatchingBankAccount(updatedAccounts, account)
|
||||||
|
|
||||||
if (updatedAccount == null) {
|
if (updatedAccount == null) {
|
||||||
customer.removeAccount(account)
|
bank.removeAccount(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,8 +170,8 @@ open class fints4kModelMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun findAccountForBankAccount(customer: CustomerData, bankAccount: BankAccount): AccountData? {
|
open fun findAccountForBankAccount(bank: BankData, bankAccount: BankAccount): AccountData? {
|
||||||
return customer.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier }
|
return bank.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier }
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun findMatchingBankAccount(customer: Customer, accountData: AccountData): BankAccount? {
|
open fun findMatchingBankAccount(customer: Customer, accountData: AccountData): BankAccount? {
|
||||||
|
@ -231,17 +230,17 @@ open class fints4kModelMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun updateTanMediaAndProcedures(account: Customer, customer: CustomerData) {
|
open fun updateTanMediaAndProcedures(account: Customer, bank: BankData) {
|
||||||
account.supportedTanProcedures = mapTanProcedures(customer.supportedTanProcedures)
|
account.supportedTanProcedures = mapTanProcedures(bank.tanProceduresAvailableForUser)
|
||||||
|
|
||||||
if (customer.isTanProcedureSelected) {
|
if (bank.isTanProcedureSelected) {
|
||||||
account.selectedTanProcedure = findMappedTanProcedure(account, customer.selectedTanProcedure)
|
account.selectedTanProcedure = findMappedTanProcedure(account, bank.selectedTanProcedure)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
account.selectedTanProcedure = null
|
account.selectedTanProcedure = null
|
||||||
}
|
}
|
||||||
|
|
||||||
account.tanMedia = mapTanMediums(customer.tanMedia)
|
account.tanMedia = mapTanMediums(bank.tanMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,19 +351,19 @@ open class fints4kModelMapper {
|
||||||
return if (tanMedium.status.name.contains("aktiv", true)) TanMediumStatus.Used else TanMediumStatus.Available
|
return if (tanMedium.status.name.contains("aktiv", true)) TanMediumStatus.Used else TanMediumStatus.Available
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapTanMedium(tanMedium: TanMedium, customer: CustomerData): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium {
|
open fun mapTanMedium(tanMedium: TanMedium, bank: BankData): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium {
|
||||||
if (tanMedium is TanGeneratorTanMedium) {
|
if (tanMedium is TanGeneratorTanMedium) {
|
||||||
return mapTanMedium(tanMedium, customer)
|
return mapTanMedium(tanMedium, bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
val statusToHave = if (tanMedium.status == TanMediumStatus.Used) listOf(net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Aktiv, net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.AktivFolgekarte)
|
val statusToHave = if (tanMedium.status == TanMediumStatus.Used) listOf(net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Aktiv, net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.AktivFolgekarte)
|
||||||
else listOf(net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Verfuegbar, net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.VerfuegbarFolgekarte)
|
else listOf(net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Verfuegbar, net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.VerfuegbarFolgekarte)
|
||||||
|
|
||||||
return customer.tanMedia.first { tanMedium.displayName == it.mediumClass.name && statusToHave.contains(it.status) }
|
return bank.tanMedia.first { tanMedium.displayName == it.mediumClass.name && statusToHave.contains(it.status) }
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapTanMedium(tanMedium: TanGeneratorTanMedium, customer: CustomerData): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium {
|
open fun mapTanMedium(tanMedium: TanGeneratorTanMedium, bank: BankData): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium {
|
||||||
return customer.tanMedia.mapNotNull { it as? net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium }
|
return bank.tanMedia.mapNotNull { it as? net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium }
|
||||||
.first { it.cardNumber == tanMedium.cardNumber
|
.first { it.cardNumber == tanMedium.cardNumber
|
||||||
&& (it.cardSequenceNumber == null || tanMedium.displayName.contains(it.cardSequenceNumber!!)) }
|
&& (it.cardSequenceNumber == null || tanMedium.displayName.contains(it.cardSequenceNumber!!)) }
|
||||||
}
|
}
|
||||||
|
@ -403,7 +402,7 @@ open class fints4kModelMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapEnterTanResult(result: EnterTanResult, customer: CustomerData): net.dankito.banking.fints.model.EnterTanResult {
|
open fun mapEnterTanResult(result: EnterTanResult, bank: BankData): net.dankito.banking.fints.model.EnterTanResult {
|
||||||
result.changeTanProcedureTo?.let { changeTanProcedureTo ->
|
result.changeTanProcedureTo?.let { changeTanProcedureTo ->
|
||||||
return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanProcedure(mapTanProcedure(changeTanProcedureTo))
|
return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanProcedure(mapTanProcedure(changeTanProcedureTo))
|
||||||
}
|
}
|
||||||
|
@ -411,7 +410,7 @@ open class fints4kModelMapper {
|
||||||
result.changeTanMediumTo?.let { changeTanMediumTo ->
|
result.changeTanMediumTo?.let { changeTanMediumTo ->
|
||||||
val callback: ((FinTsClientResponse) -> Unit)? = if (result.changeTanMediumResultCallback == null) null
|
val callback: ((FinTsClientResponse) -> Unit)? = if (result.changeTanMediumResultCallback == null) null
|
||||||
else { response -> result.changeTanMediumResultCallback?.invoke(mapResponse(response)) }
|
else { response -> result.changeTanMediumResultCallback?.invoke(mapResponse(response)) }
|
||||||
return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanMedium(mapTanMedium(changeTanMediumTo, customer), callback)
|
return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanMedium(mapTanMedium(changeTanMediumTo, bank), callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.enteredTan?.let { enteredTan ->
|
result.enteredTan?.let { enteredTan ->
|
||||||
|
|
Loading…
Reference in New Issue