Replaced callbacks with coroutines
This commit is contained in:
parent
bdd28f2587
commit
54c430af2b
|
@ -3,6 +3,7 @@ package net.codinux.banking.fints4k.android
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.FinTsClientDeprecated
|
||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
||||
|
@ -27,10 +28,11 @@ class Presenter {
|
|||
|
||||
|
||||
fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String, retrievedResult: (AddAccountResponse) -> Unit) {
|
||||
fintsClient.addAccountAsync(AddAccountParameter(bankCode, customerId, pin, finTs3ServerAddress)) { response ->
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val response = fintsClient.addAccountAsync(AddAccountParameter(bankCode, customerId, pin, finTs3ServerAddress))
|
||||
log.info("Retrieved response from ${response.bank.bankName} for ${response.bank.customerName}")
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
withContext(Dispatchers.Main) {
|
||||
retrievedResult(response)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.dankito.banking.fints.FinTsClientDeprecated
|
||||
import net.dankito.banking.fints.model.AccountTransaction
|
||||
import net.dankito.banking.fints.model.AddAccountParameter
|
||||
|
@ -23,7 +25,8 @@ class AccountTransactionsView(props: AccountTransactionsViewProps) : RComponent<
|
|||
|
||||
// due to CORS your bank's servers can not be requested directly from browser -> set a CORS proxy url in main.kt
|
||||
// TODO: set your credentials here
|
||||
props.client.addAccountAsync(AddAccountParameter("", "", "", "")) { response ->
|
||||
GlobalScope.launch {
|
||||
val response = props.client.addAccountAsync(AddAccountParameter("", "", "", ""))
|
||||
if (response.successful) {
|
||||
val balance = response.retrievedData.sumOf { it.balance?.amount?.string?.replace(',', '.')?.toDoubleOrNull() ?: 0.0 } // i know, double is not an appropriate data type for amounts
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ open class FinTsClientDeprecated(
|
|||
open fun getAnonymousBankInfoAsync(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||
|
||||
GlobalScope.launch {
|
||||
getAnonymousBankInfo(bank, callback)
|
||||
callback(getAnonymousBankInfo(bank))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,90 +54,74 @@ open class FinTsClientDeprecated(
|
|||
*
|
||||
* On success [bank] parameter is updated afterwards.
|
||||
*/
|
||||
open fun getAnonymousBankInfo(bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||
open suspend fun getAnonymousBankInfo(bank: BankData): FinTsClientResponse {
|
||||
val context = JobContext(JobContextType.AnonymousBankInfo, this.callback, product, bank)
|
||||
|
||||
jobExecutor.getAnonymousBankInfo(context) { response ->
|
||||
callback(FinTsClientResponse(context, response))
|
||||
}
|
||||
val response = jobExecutor.getAnonymousBankInfo(context)
|
||||
return FinTsClientResponse(context, response)
|
||||
}
|
||||
|
||||
|
||||
open fun addAccountAsync(parameter: AddAccountParameter, callback: (AddAccountResponse) -> Unit) {
|
||||
open suspend fun addAccountAsync(parameter: AddAccountParameter): AddAccountResponse {
|
||||
val bank = parameter.bank
|
||||
val context = JobContext(JobContextType.AddAccount, this.callback, product, bank)
|
||||
|
||||
/* First dialog: Get user's basic data like BPD, customer system ID and her TAN methods */
|
||||
|
||||
jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium) { newUserInfoResponse ->
|
||||
val newUserInfoResponse = jobExecutor.retrieveBasicDataLikeUsersTanMethods(context, parameter.preferredTanMethods, parameter.preferredTanMedium)
|
||||
|
||||
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
||||
callback(AddAccountResponse(context, newUserInfoResponse))
|
||||
return@retrieveBasicDataLikeUsersTanMethods
|
||||
}
|
||||
|
||||
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
||||
strong customer authorization TAN media is required */
|
||||
|
||||
addAccountGetAccountsAndTransactions(context, parameter, callback)
|
||||
if (newUserInfoResponse.successful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong
|
||||
return AddAccountResponse(context, newUserInfoResponse)
|
||||
}
|
||||
|
||||
/* Second dialog, executed in retrieveBasicDataLikeUsersTanMethods() if required: some banks require that in order to initialize a dialog with
|
||||
strong customer authorization TAN media is required */
|
||||
|
||||
return addAccountGetAccountsAndTransactions(context, parameter)
|
||||
}
|
||||
|
||||
protected open fun addAccountGetAccountsAndTransactions(context: JobContext, parameter: AddAccountParameter,
|
||||
callback: (AddAccountResponse) -> Unit) {
|
||||
protected open suspend fun addAccountGetAccountsAndTransactions(context: JobContext, parameter: AddAccountParameter): AddAccountResponse {
|
||||
|
||||
/* Third dialog: Now we can initialize our first dialog with strong customer authorization. Use it to get UPD and customer's accounts */
|
||||
|
||||
jobExecutor.getAccounts(context) { getAccountsResponse ->
|
||||
val getAccountsResponse = jobExecutor.getAccounts(context)
|
||||
|
||||
if (getAccountsResponse.successful == false) {
|
||||
callback(AddAccountResponse(context, getAccountsResponse))
|
||||
return@getAccounts
|
||||
}
|
||||
if (getAccountsResponse.successful == false) {
|
||||
return AddAccountResponse(context, getAccountsResponse)
|
||||
}
|
||||
|
||||
/* Fourth dialog (if requested): Try to retrieve account balances and transactions of last 90 days without TAN */
|
||||
/* Fourth dialog (if requested): Try to retrieve account balances and transactions of last 90 days without TAN */
|
||||
|
||||
if (parameter.fetchBalanceAndTransactions) {
|
||||
addAccountGetAccountBalancesAndTransactions(context, getAccountsResponse, callback)
|
||||
}
|
||||
else {
|
||||
addAccountDone(context, getAccountsResponse, listOf(), callback)
|
||||
}
|
||||
if (parameter.fetchBalanceAndTransactions) {
|
||||
return addAccountGetAccountBalancesAndTransactions(context, getAccountsResponse)
|
||||
}
|
||||
else {
|
||||
return addAccountDone(context, getAccountsResponse, listOf())
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun addAccountGetAccountBalancesAndTransactions(context: JobContext, getAccountsResponse: BankResponse,
|
||||
callback: (AddAccountResponse) -> Unit) {
|
||||
protected open suspend fun addAccountGetAccountBalancesAndTransactions(context: JobContext, getAccountsResponse: BankResponse): AddAccountResponse {
|
||||
|
||||
val bank = context.bank
|
||||
val retrievedTransactionsResponses = mutableListOf<GetAccountTransactionsResponse>()
|
||||
|
||||
val accountsSupportingRetrievingTransactions = bank.accounts.filter { it.supportsRetrievingBalance || it.supportsRetrievingAccountTransactions }
|
||||
val countAccountsSupportingRetrievingTransactions = accountsSupportingRetrievingTransactions.size
|
||||
var countRetrievedAccounts = 0
|
||||
|
||||
if (countAccountsSupportingRetrievingTransactions == 0) {
|
||||
addAccountDone(context, getAccountsResponse, retrievedTransactionsResponses, callback)
|
||||
return // not necessary just to make it clearer that code below doesn't get called
|
||||
if (accountsSupportingRetrievingTransactions.isEmpty()) {
|
||||
return addAccountDone(context, getAccountsResponse, retrievedTransactionsResponses)
|
||||
}
|
||||
|
||||
accountsSupportingRetrievingTransactions.forEach { account ->
|
||||
tryGetAccountTransactionsOfLast90DaysWithoutTan(bank, account) { response ->
|
||||
retrievedTransactionsResponses.add(response)
|
||||
|
||||
countRetrievedAccounts++
|
||||
if (countRetrievedAccounts == countAccountsSupportingRetrievingTransactions) {
|
||||
addAccountDone(context, getAccountsResponse, retrievedTransactionsResponses, callback)
|
||||
}
|
||||
}
|
||||
retrievedTransactionsResponses.add(tryGetAccountTransactionsOfLast90DaysWithoutTan(bank, account))
|
||||
}
|
||||
|
||||
return addAccountDone(context, getAccountsResponse, retrievedTransactionsResponses)
|
||||
}
|
||||
|
||||
protected open fun addAccountDone(context: JobContext, getAccountsResponse: BankResponse,
|
||||
retrievedTransactionsResponses: List<GetAccountTransactionsResponse>,
|
||||
callback: (AddAccountResponse) -> Unit) {
|
||||
retrievedTransactionsResponses: List<GetAccountTransactionsResponse>): AddAccountResponse {
|
||||
|
||||
callback(AddAccountResponse(context, getAccountsResponse, retrievedTransactionsResponses))
|
||||
return AddAccountResponse(context, getAccountsResponse, retrievedTransactionsResponses)
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,9 +131,9 @@ open class FinTsClientDeprecated(
|
|||
*
|
||||
* Check if bank supports this.
|
||||
*/
|
||||
open fun tryGetAccountTransactionsOfLast90DaysWithoutTan(bank: BankData, account: AccountData, callback: (GetAccountTransactionsResponse) -> Unit) {
|
||||
open suspend fun tryGetAccountTransactionsOfLast90DaysWithoutTan(bank: BankData, account: AccountData): GetAccountTransactionsResponse {
|
||||
|
||||
getAccountTransactionsAsync(createGetAccountTransactionsOfLast90DaysParameter(bank, account), callback)
|
||||
return getAccountTransactionsAsync(createGetAccountTransactionsOfLast90DaysParameter(bank, account))
|
||||
}
|
||||
|
||||
protected open fun createGetAccountTransactionsOfLast90DaysParameter(bank: BankData, account: AccountData): GetAccountTransactionsParameter {
|
||||
|
@ -159,36 +143,35 @@ open class FinTsClientDeprecated(
|
|||
return GetAccountTransactionsParameter(bank, account, account.supportsRetrievingBalance, ninetyDaysAgo, abortIfTanIsRequired = true)
|
||||
}
|
||||
|
||||
open fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter, callback: (GetAccountTransactionsResponse) -> Unit) {
|
||||
open suspend fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
|
||||
|
||||
val context = JobContext(JobContextType.GetTransactions, this.callback, product, parameter.bank, parameter.account)
|
||||
|
||||
jobExecutor.getTransactionsAsync(context, parameter, callback)
|
||||
return jobExecutor.getTransactionsAsync(context, parameter)
|
||||
}
|
||||
|
||||
|
||||
open fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
||||
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien, callback: (GetTanMediaListResponse) -> Unit) {
|
||||
open suspend fun getTanMediaList(bank: BankData, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle,
|
||||
tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): GetTanMediaListResponse {
|
||||
|
||||
val context = JobContext(JobContextType.GetTanMedia, this.callback, product, bank)
|
||||
|
||||
jobExecutor.getTanMediaList(context, tanMediaKind, tanMediumClass, callback)
|
||||
return jobExecutor.getTanMediaList(context, tanMediaKind, tanMediumClass)
|
||||
}
|
||||
|
||||
|
||||
open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, callback: (FinTsClientResponse) -> Unit) {
|
||||
open suspend fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData): FinTsClientResponse {
|
||||
val context = JobContext(JobContextType.ChangeTanMedium, this.callback, product, bank)
|
||||
|
||||
jobExecutor.changeTanMedium(context, newActiveTanMedium) { response ->
|
||||
callback(FinTsClientResponse(context, response))
|
||||
}
|
||||
val response = jobExecutor.changeTanMedium(context, newActiveTanMedium)
|
||||
return FinTsClientResponse(context, response)
|
||||
}
|
||||
|
||||
|
||||
open fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
||||
open suspend fun doBankTransferAsync(bankTransferData: BankTransferData, bank: BankData, account: AccountData): FinTsClientResponse {
|
||||
val context = JobContext(JobContextType.TransferMoney, this.callback, product, bank, account)
|
||||
|
||||
jobExecutor.doBankTransferAsync(context, bankTransferData, callback)
|
||||
return jobExecutor.doBankTransferAsync(context, bankTransferData)
|
||||
}
|
||||
|
||||
}
|
|
@ -39,22 +39,22 @@ open class FinTsClientForCustomer(
|
|||
}
|
||||
|
||||
|
||||
open fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
|
||||
addAccountAsync(bank.toAddAccountParameter(), callback)
|
||||
open suspend fun addAccountAsync(): AddAccountResponse {
|
||||
return addAccountAsync(bank.toAddAccountParameter())
|
||||
}
|
||||
|
||||
open fun addAccountAsync(parameter: AddAccountParameter, callback: (AddAccountResponse) -> Unit) {
|
||||
client.addAccountAsync(parameter, callback)
|
||||
open suspend fun addAccountAsync(parameter: AddAccountParameter): AddAccountResponse {
|
||||
return client.addAccountAsync(parameter)
|
||||
}
|
||||
|
||||
|
||||
open fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter, callback: (GetAccountTransactionsResponse) -> Unit) {
|
||||
client.getAccountTransactionsAsync(parameter, callback)
|
||||
open suspend fun getAccountTransactionsAsync(parameter: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
|
||||
return client.getAccountTransactionsAsync(parameter)
|
||||
}
|
||||
|
||||
|
||||
open fun doBankTransferAsync(bankTransferData: BankTransferData, account: AccountData, callback: (FinTsClientResponse) -> Unit) {
|
||||
client.doBankTransferAsync(bankTransferData, bank, account, callback)
|
||||
open suspend fun doBankTransferAsync(bankTransferData: BankTransferData, account: AccountData): FinTsClientResponse {
|
||||
return client.doBankTransferAsync(bankTransferData, bank, account)
|
||||
}
|
||||
|
||||
}
|
|
@ -41,21 +41,21 @@ open class FinTsJobExecutor(
|
|||
}
|
||||
|
||||
|
||||
open fun getAnonymousBankInfo(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
open suspend fun getAnonymousBankInfo(context: JobContext): BankResponse {
|
||||
context.startNewDialog()
|
||||
|
||||
val message = messageBuilder.createAnonymousDialogInitMessage(context)
|
||||
|
||||
getAndHandleResponseForMessage(context, message) { response ->
|
||||
if (response.successful) {
|
||||
closeAnonymousDialog(context, response)
|
||||
}
|
||||
val response = getAndHandleResponseForMessage(context, message)
|
||||
|
||||
callback(response)
|
||||
if (response.successful) {
|
||||
closeAnonymousDialog(context, response)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
protected open fun closeAnonymousDialog(context: JobContext, response: BankResponse) {
|
||||
protected open suspend fun closeAnonymousDialog(context: JobContext, response: BankResponse) {
|
||||
|
||||
// bank already closed dialog -> there's no need to send dialog end message
|
||||
if (shouldNotCloseDialog(context)) {
|
||||
|
@ -75,8 +75,8 @@ open class FinTsJobExecutor(
|
|||
*
|
||||
* Be aware this method resets BPD, UPD and selected TAN method!
|
||||
*/
|
||||
open fun retrieveBasicDataLikeUsersTanMethods(context: JobContext, preferredTanMethods: List<TanMethodType>? = null, preferredTanMedium: String? = null,
|
||||
closeDialog: Boolean = false, callback: (BankResponse) -> Unit) {
|
||||
open suspend fun retrieveBasicDataLikeUsersTanMethods(context: JobContext, preferredTanMethods: List<TanMethodType>? = null, preferredTanMedium: String? = null,
|
||||
closeDialog: Boolean = false): BankResponse {
|
||||
val bank = context.bank
|
||||
|
||||
// just to ensure settings are in its initial state and that bank sends us bank parameter (BPD),
|
||||
|
@ -96,38 +96,38 @@ open class FinTsJobExecutor(
|
|||
|
||||
val message = messageBuilder.createInitDialogMessage(context)
|
||||
|
||||
getAndHandleResponseForMessage(context, message) { response ->
|
||||
closeDialog(context)
|
||||
val response = getAndHandleResponseForMessage(context, message)
|
||||
|
||||
handleGetUsersTanMethodsResponse(context, response) { getTanMethodsResponse ->
|
||||
if (bank.tanMethodsAvailableForUser.isEmpty()) { // could not retrieve supported tan methods for user
|
||||
callback(getTanMethodsResponse)
|
||||
} else {
|
||||
getUsersTanMethod(context, preferredTanMethods) {
|
||||
if (bank.isTanMethodSelected == false) {
|
||||
callback(getTanMethodsResponse)
|
||||
} else if (bank.tanMedia.isEmpty() && isJobSupported(bank, CustomerSegmentId.TanMediaList)) { // tan media not retrieved yet
|
||||
getTanMediaList(context, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, preferredTanMedium) {
|
||||
callback(getTanMethodsResponse) // TODO: judge if bank requires selecting TAN media and if though evaluate getTanMediaListResponse
|
||||
}
|
||||
} else {
|
||||
callback(getTanMethodsResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
closeDialog(context)
|
||||
|
||||
val getTanMethodsResponse = handleGetUsersTanMethodsResponse(context, response)
|
||||
|
||||
if (bank.tanMethodsAvailableForUser.isEmpty()) { // could not retrieve supported tan methods for user
|
||||
return getTanMethodsResponse
|
||||
} else {
|
||||
getUsersTanMethod(context, preferredTanMethods)
|
||||
|
||||
if (bank.isTanMethodSelected == false) {
|
||||
return getTanMethodsResponse
|
||||
} else if (bank.tanMedia.isEmpty() && isJobSupported(bank, CustomerSegmentId.TanMediaList)) { // tan media not retrieved yet
|
||||
getTanMediaList(context, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien, preferredTanMedium)
|
||||
|
||||
return getTanMethodsResponse // TODO: judge if bank requires selecting TAN media and if though evaluate getTanMediaListResponse
|
||||
} else {
|
||||
return getTanMethodsResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun handleGetUsersTanMethodsResponse(context: JobContext, response: BankResponse, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun handleGetUsersTanMethodsResponse(context: JobContext, response: BankResponse): BankResponse {
|
||||
val getUsersTanMethodsResponse = GetUserTanMethodsResponse(response)
|
||||
|
||||
// even though it is required by specification some banks don't support retrieving user's TAN method by setting TAN method to '999'
|
||||
if (bankDoesNotSupportRetrievingUsersTanMethods(getUsersTanMethodsResponse)) {
|
||||
getBankDataForNewUserViaAnonymousDialog(context, callback) // TODO: should not be necessary anymore
|
||||
return getBankDataForNewUserViaAnonymousDialog(context) // TODO: should not be necessary anymore
|
||||
}
|
||||
else {
|
||||
callback(getUsersTanMethodsResponse)
|
||||
return getUsersTanMethodsResponse
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,73 +138,70 @@ open class FinTsJobExecutor(
|
|||
}
|
||||
|
||||
// TODO: this is only a quick fix. Find a better and general solution
|
||||
protected open fun getBankDataForNewUserViaAnonymousDialog(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
getAnonymousBankInfo(context) { anonymousBankInfoResponse ->
|
||||
val bank = context.bank
|
||||
protected open suspend fun getBankDataForNewUserViaAnonymousDialog(context: JobContext): BankResponse {
|
||||
val anonymousBankInfoResponse = getAnonymousBankInfo(context)
|
||||
|
||||
if (anonymousBankInfoResponse.successful == false) {
|
||||
callback(anonymousBankInfoResponse)
|
||||
} else if (bank.tanMethodsSupportedByBank.isEmpty()) { // should only be a theoretical error
|
||||
callback(BankResponse(true, internalError = "Die TAN Verfahren der Bank konnten nicht ermittelt werden")) // TODO: translate
|
||||
val bank = context.bank
|
||||
|
||||
if (anonymousBankInfoResponse.successful == false) {
|
||||
return anonymousBankInfoResponse
|
||||
} else if (bank.tanMethodsSupportedByBank.isEmpty()) { // should only be a theoretical error
|
||||
return BankResponse(true, internalError = "Die TAN Verfahren der Bank konnten nicht ermittelt werden") // TODO: translate
|
||||
} else {
|
||||
bank.tanMethodsAvailableForUser = bank.tanMethodsSupportedByBank
|
||||
|
||||
val didSelectTanMethod = getUsersTanMethod(context)
|
||||
|
||||
if (didSelectTanMethod) {
|
||||
val initDialogResponse = initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context)
|
||||
|
||||
closeDialog(context)
|
||||
|
||||
return initDialogResponse
|
||||
} else {
|
||||
bank.tanMethodsAvailableForUser = bank.tanMethodsSupportedByBank
|
||||
|
||||
getUsersTanMethod(context) { didSelectTanMethod ->
|
||||
if (didSelectTanMethod) {
|
||||
initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context) { initDialogResponse ->
|
||||
closeDialog(context)
|
||||
|
||||
callback(initDialogResponse)
|
||||
}
|
||||
} else {
|
||||
callback(createNoTanMethodSelectedResponse(bank))
|
||||
}
|
||||
}
|
||||
return createNoTanMethodSelectedResponse(bank)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open fun getAccounts(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context) { response ->
|
||||
closeDialog(context)
|
||||
open suspend fun getAccounts(context: JobContext): BankResponse {
|
||||
val response = initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context)
|
||||
|
||||
callback(response)
|
||||
}
|
||||
closeDialog(context)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
open fun getTransactionsAsync(context: JobContext, parameter: GetAccountTransactionsParameter, callback: (GetAccountTransactionsResponse) -> Unit) {
|
||||
open suspend fun getTransactionsAsync(context: JobContext, parameter: GetAccountTransactionsParameter): GetAccountTransactionsResponse {
|
||||
|
||||
val dialogContext = context.startNewDialog() // TODO: initDialogWithStrongCustomerAuthentication() also starts a new dialog in initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks()
|
||||
|
||||
initDialogWithStrongCustomerAuthentication(context) { initDialogResponse ->
|
||||
val initDialogResponse = initDialogWithStrongCustomerAuthentication(context)
|
||||
|
||||
if (initDialogResponse.successful == false) {
|
||||
callback(GetAccountTransactionsResponse(context, initDialogResponse, RetrievedAccountData.unsuccessful(parameter.account)))
|
||||
}
|
||||
else {
|
||||
// we now retrieved the fresh account information from FinTS server, use that one
|
||||
parameter.account = getUpdatedAccount(context, parameter.account)
|
||||
if (initDialogResponse.successful == false) {
|
||||
return GetAccountTransactionsResponse(context, initDialogResponse, RetrievedAccountData.unsuccessful(parameter.account))
|
||||
}
|
||||
else {
|
||||
// we now retrieved the fresh account information from FinTS server, use that one
|
||||
parameter.account = getUpdatedAccount(context, parameter.account)
|
||||
|
||||
mayGetBalance(context, parameter) { balanceResponse ->
|
||||
if (dialogContext.didBankCloseDialog) {
|
||||
callback(GetAccountTransactionsResponse(context, balanceResponse ?: initDialogResponse, RetrievedAccountData.unsuccessful(parameter.account)))
|
||||
}
|
||||
else {
|
||||
getTransactionsAfterInitAndGetBalance(context, parameter, balanceResponse, callback)
|
||||
}
|
||||
}
|
||||
val balanceResponse = mayGetBalance(context, parameter)
|
||||
if (dialogContext.didBankCloseDialog) {
|
||||
return GetAccountTransactionsResponse(context, balanceResponse ?: initDialogResponse, RetrievedAccountData.unsuccessful(parameter.account))
|
||||
} else {
|
||||
return getTransactionsAfterInitAndGetBalance(context, parameter, balanceResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUpdatedAccount(context: JobContext, account: AccountData): AccountData {
|
||||
protected open fun getUpdatedAccount(context: JobContext, account: AccountData): AccountData {
|
||||
return context.bank.accounts.firstOrNull { it.accountIdentifier == account.accountIdentifier } ?: account
|
||||
}
|
||||
|
||||
protected open fun getTransactionsAfterInitAndGetBalance(context: JobContext, parameter: GetAccountTransactionsParameter,
|
||||
balanceResponse: BankResponse?, callback: (GetAccountTransactionsResponse) -> Unit) {
|
||||
protected open suspend fun getTransactionsAfterInitAndGetBalance(context: JobContext, parameter: GetAccountTransactionsParameter,
|
||||
balanceResponse: BankResponse?): GetAccountTransactionsResponse {
|
||||
var balance: Money? = balanceResponse?.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
|
||||
Money(it.balance, it.currency)
|
||||
}
|
||||
|
@ -234,29 +231,28 @@ open class FinTsJobExecutor(
|
|||
}
|
||||
}
|
||||
|
||||
getAndHandleResponseForMessage(context, message) { response ->
|
||||
closeDialog(context)
|
||||
val response = getAndHandleResponseForMessage(context, message)
|
||||
|
||||
val successful = response.tanRequiredButWeWereToldToAbortIfSo
|
||||
|| (response.successful && (parameter.alsoRetrieveBalance == false || balance != null))
|
||||
val fromDate = parameter.fromDate
|
||||
?: parameter.account.countDaysForWhichTransactionsAreKept?.let { LocalDate.todayAtSystemDefaultTimeZone().minusDays(it) }
|
||||
?: bookedTransactions.minByOrNull { it.valueDate.millisSinceEpochAtEuropeBerlin }?.valueDate
|
||||
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: LocalDate.todayAtEuropeBerlin(), response.internalError)
|
||||
closeDialog(context)
|
||||
|
||||
callback(GetAccountTransactionsResponse(context, response, retrievedData,
|
||||
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null))
|
||||
}
|
||||
val successful = response.tanRequiredButWeWereToldToAbortIfSo
|
||||
|| (response.successful && (parameter.alsoRetrieveBalance == false || balance != null))
|
||||
val fromDate = parameter.fromDate
|
||||
?: parameter.account.countDaysForWhichTransactionsAreKept?.let { LocalDate.todayAtSystemDefaultTimeZone().minusDays(it) }
|
||||
?: bookedTransactions.minByOrNull { it.valueDate.millisSinceEpochAtEuropeBerlin }?.valueDate
|
||||
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: LocalDate.todayAtEuropeBerlin(), response.internalError)
|
||||
|
||||
return GetAccountTransactionsResponse(context, response, retrievedData,
|
||||
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null)
|
||||
}
|
||||
|
||||
protected open fun mayGetBalance(context: JobContext, parameter: GetAccountTransactionsParameter, callback: (BankResponse?) -> Unit) {
|
||||
protected open suspend fun mayGetBalance(context: JobContext, parameter: GetAccountTransactionsParameter): BankResponse? {
|
||||
if (parameter.alsoRetrieveBalance && parameter.account.supportsRetrievingBalance) {
|
||||
val message = messageBuilder.createGetBalanceMessage(context, parameter.account)
|
||||
|
||||
getAndHandleResponseForMessage(context, message, callback)
|
||||
}
|
||||
else {
|
||||
callback(null)
|
||||
return getAndHandleResponseForMessage(context, message)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,38 +268,37 @@ open class FinTsJobExecutor(
|
|||
*
|
||||
* If you change customer system id during a dialog your messages get rejected by bank institute.
|
||||
*/
|
||||
protected open fun synchronizeCustomerSystemId(context: JobContext, callback: (FinTsClientResponse) -> Unit) {
|
||||
protected open suspend fun synchronizeCustomerSystemId(context: JobContext): FinTsClientResponse {
|
||||
|
||||
context.startNewDialog()
|
||||
|
||||
val message = messageBuilder.createSynchronizeCustomerSystemIdMessage(context)
|
||||
|
||||
getAndHandleResponseForMessage(context, message) { response ->
|
||||
if (response.successful) {
|
||||
closeDialog(context)
|
||||
}
|
||||
val response = getAndHandleResponseForMessage(context, message)
|
||||
|
||||
callback(FinTsClientResponse(context, response))
|
||||
if (response.successful) {
|
||||
closeDialog(context)
|
||||
}
|
||||
|
||||
return FinTsClientResponse(context, response)
|
||||
}
|
||||
|
||||
|
||||
open fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien,
|
||||
callback: (GetTanMediaListResponse) -> Unit) {
|
||||
getTanMediaList(context, tanMediaKind, tanMediumClass, null, callback)
|
||||
open suspend fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien): GetTanMediaListResponse {
|
||||
return getTanMediaList(context, tanMediaKind, tanMediumClass, null)
|
||||
}
|
||||
|
||||
protected open fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien,
|
||||
preferredTanMedium: String? = null, callback: (GetTanMediaListResponse) -> Unit) {
|
||||
protected open suspend fun getTanMediaList(context: JobContext, tanMediaKind: TanMedienArtVersion = TanMedienArtVersion.Alle, tanMediumClass: TanMediumKlasse = TanMediumKlasse.AlleMedien,
|
||||
preferredTanMedium: String? = null): GetTanMediaListResponse {
|
||||
|
||||
sendMessageInNewDialogAndHandleResponse(context, CustomerSegmentId.TanMediaList, false, {
|
||||
val response = sendMessageInNewDialogAndHandleResponse(context, CustomerSegmentId.TanMediaList, false) {
|
||||
messageBuilder.createGetTanMediaListMessage(context, tanMediaKind, tanMediumClass)
|
||||
}) { response ->
|
||||
handleGetTanMediaListResponse(context, response, preferredTanMedium, callback)
|
||||
}
|
||||
|
||||
return handleGetTanMediaListResponse(context, response, preferredTanMedium)
|
||||
}
|
||||
|
||||
protected open fun handleGetTanMediaListResponse(context: JobContext, response: BankResponse, preferredTanMedium: String? = null, callback: (GetTanMediaListResponse) -> Unit) {
|
||||
protected open fun handleGetTanMediaListResponse(context: JobContext, response: BankResponse, preferredTanMedium: String? = null): GetTanMediaListResponse {
|
||||
val bank = context.bank
|
||||
|
||||
// TAN media list (= TAN generator list) is only returned for users with chipTAN TAN methods
|
||||
|
@ -318,83 +313,80 @@ open class FinTsJobExecutor(
|
|||
?: bank.tanMedia.firstOrNull { it.mediumName != null }
|
||||
}
|
||||
|
||||
callback(GetTanMediaListResponse(context, response, tanMediaList))
|
||||
return GetTanMediaListResponse(context, response, tanMediaList)
|
||||
}
|
||||
|
||||
|
||||
open fun changeTanMedium(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, callback: (BankResponse) -> Unit) {
|
||||
open suspend fun changeTanMedium(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium): BankResponse {
|
||||
val bank = context.bank
|
||||
|
||||
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
|
||||
context.callback.enterTanGeneratorAtc(bank, newActiveTanMedium) { enteredAtc ->
|
||||
if (enteredAtc.hasAtcBeenEntered == false) {
|
||||
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
|
||||
callback(BankResponse(false, internalError = message))
|
||||
}
|
||||
else {
|
||||
sendChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc, callback)
|
||||
}
|
||||
val enteredAtc = context.callback.enterTanGeneratorAtc(bank, newActiveTanMedium)
|
||||
if (enteredAtc.hasAtcBeenEntered == false) {
|
||||
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
|
||||
return BankResponse(false, internalError = message)
|
||||
} else {
|
||||
return sendChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc)
|
||||
}
|
||||
}
|
||||
else {
|
||||
sendChangeTanMediumMessage(context, newActiveTanMedium, null, callback)
|
||||
return sendChangeTanMediumMessage(context, newActiveTanMedium, null)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun sendChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?,
|
||||
callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun sendChangeTanMediumMessage(context: JobContext, newActiveTanMedium: TanGeneratorTanMedium, enteredAtc: EnterTanGeneratorAtcResult?): BankResponse {
|
||||
|
||||
sendMessageInNewDialogAndHandleResponse(context, null, true, {
|
||||
return sendMessageInNewDialogAndHandleResponse(context, null, true) {
|
||||
messageBuilder.createChangeTanMediumMessage(context, newActiveTanMedium, enteredAtc?.tan, enteredAtc?.atc)
|
||||
}, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open fun doBankTransferAsync(context: JobContext, bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) {
|
||||
open suspend fun doBankTransferAsync(context: JobContext, bankTransferData: BankTransferData): FinTsClientResponse {
|
||||
|
||||
sendMessageInNewDialogAndHandleResponse(context, null, true, {
|
||||
val response = sendMessageInNewDialogAndHandleResponse(context, null, true) {
|
||||
val updatedAccount = getUpdatedAccount(context, context.account!!)
|
||||
messageBuilder.createBankTransferMessage(context, bankTransferData, updatedAccount)
|
||||
}) { response ->
|
||||
callback(FinTsClientResponse(context, response))
|
||||
}
|
||||
|
||||
return FinTsClientResponse(context, response)
|
||||
}
|
||||
|
||||
|
||||
protected open fun getAndHandleResponseForMessage(context: JobContext, message: MessageBuilderResult, callback: (BankResponse) -> Unit) {
|
||||
requestExecutor.getAndHandleResponseForMessage(message, context,
|
||||
{ tanResponse, bankResponse, tanRequiredCallback ->
|
||||
protected open suspend fun getAndHandleResponseForMessage(context: JobContext, message: MessageBuilderResult): BankResponse {
|
||||
val response = requestExecutor.getAndHandleResponseForMessage(message, context, { tanResponse, bankResponse ->
|
||||
// if we receive a message that tells us a TAN is required below callback doesn't get called for that message -> update data here
|
||||
// for Hypovereinsbank it's absolutely necessary to update bank data (more specific: PinInfo / HIPINS) after first strong authentication dialog init response
|
||||
// as HIPINS differ in anonymous and in authenticated dialog. Anonymous dialog tells us for HKSAL and HKKAZ no TAN is needed
|
||||
updateBankAndCustomerDataIfResponseSuccessful(context, bankResponse)
|
||||
handleEnteringTanRequired(context, tanResponse, bankResponse, tanRequiredCallback)
|
||||
}) { response ->
|
||||
// TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part
|
||||
updateBankAndCustomerDataIfResponseSuccessful(context, response)
|
||||
|
||||
callback(response)
|
||||
}
|
||||
handleEnteringTanRequired(context, tanResponse, bankResponse)
|
||||
})
|
||||
|
||||
// TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part
|
||||
updateBankAndCustomerDataIfResponseSuccessful(context, response)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
protected open fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) {
|
||||
protected open suspend fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) {
|
||||
requestExecutor.fireAndForgetMessage(context, message)
|
||||
}
|
||||
|
||||
|
||||
protected open fun handleEnteringTanRequired(context: JobContext, tanResponse: TanResponse, response: BankResponse, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun handleEnteringTanRequired(context: JobContext, tanResponse: TanResponse, response: BankResponse): BankResponse {
|
||||
val bank = context.bank // TODO: copy required data to TanChallenge
|
||||
val tanChallenge = createTanChallenge(tanResponse, bank)
|
||||
|
||||
val userDidCancelEnteringTan = ObjectReference(false)
|
||||
|
||||
context.callback.enterTan(bank, tanChallenge) { enteredTanResult ->
|
||||
userDidCancelEnteringTan.value = true
|
||||
val enteredTanResult = context.callback.enterTan(bank, tanChallenge)
|
||||
userDidCancelEnteringTan.value = true
|
||||
|
||||
handleEnterTanResult(context, enteredTanResult, tanResponse, response, callback)
|
||||
}
|
||||
return handleEnterTanResult(context, enteredTanResult, tanResponse, response)
|
||||
|
||||
mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse, userDidCancelEnteringTan)
|
||||
// TODO:
|
||||
// mayRetrieveAutomaticallyIfUserEnteredDecoupledTan(context, tanChallenge, tanResponse, userDidCancelEnteringTan)
|
||||
}
|
||||
|
||||
protected open fun createTanChallenge(tanResponse: TanResponse, bank: BankData): TanChallenge {
|
||||
|
@ -431,36 +423,36 @@ open class FinTsJobExecutor(
|
|||
log.info("automaticallyRetrieveIfUserEnteredDecoupledTan() called for $tanChallenge")
|
||||
}
|
||||
|
||||
protected open fun handleEnterTanResult(context: JobContext, enteredTanResult: EnterTanResult, tanResponse: TanResponse,
|
||||
response: BankResponse, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun handleEnterTanResult(context: JobContext, enteredTanResult: EnterTanResult, tanResponse: TanResponse,
|
||||
response: BankResponse): BankResponse {
|
||||
|
||||
if (enteredTanResult.changeTanMethodTo != null) {
|
||||
handleUserAsksToChangeTanMethodAndResendLastMessage(context, enteredTanResult.changeTanMethodTo, callback)
|
||||
return handleUserAsksToChangeTanMethodAndResendLastMessage(context, enteredTanResult.changeTanMethodTo)
|
||||
}
|
||||
else if (enteredTanResult.changeTanMediumTo is TanGeneratorTanMedium) {
|
||||
handleUserAsksToChangeTanMediumAndResendLastMessage(context, enteredTanResult.changeTanMediumTo,
|
||||
enteredTanResult.changeTanMediumResultCallback, callback)
|
||||
return handleUserAsksToChangeTanMediumAndResendLastMessage(context, enteredTanResult.changeTanMediumTo,
|
||||
enteredTanResult.changeTanMediumResultCallback)
|
||||
}
|
||||
else if (enteredTanResult.enteredTan == null) {
|
||||
// i tried to send a HKTAN with cancelJob = true but then i saw there are no tan methods that support cancellation (at least not at my bank)
|
||||
// but it's not required anyway, tan times out after some time. Simply don't respond anything and close dialog
|
||||
response.tanRequiredButUserDidNotEnterOne = true
|
||||
|
||||
callback(response)
|
||||
return response
|
||||
}
|
||||
else {
|
||||
sendTanToBank(context, enteredTanResult.enteredTan, tanResponse, callback)
|
||||
return sendTanToBank(context, enteredTanResult.enteredTan, tanResponse)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun sendTanToBank(context: JobContext, enteredTan: String, tanResponse: TanResponse, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun sendTanToBank(context: JobContext, enteredTan: String, tanResponse: TanResponse): BankResponse {
|
||||
|
||||
val message = messageBuilder.createSendEnteredTanMessage(context, enteredTan, tanResponse)
|
||||
|
||||
getAndHandleResponseForMessage(context, message, callback)
|
||||
return getAndHandleResponseForMessage(context, message)
|
||||
}
|
||||
|
||||
protected open fun handleUserAsksToChangeTanMethodAndResendLastMessage(context: JobContext, changeTanMethodTo: TanMethod, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun handleUserAsksToChangeTanMethodAndResendLastMessage(context: JobContext, changeTanMethodTo: TanMethod): BankResponse {
|
||||
|
||||
context.bank.selectedTanMethod = changeTanMethodTo
|
||||
|
||||
|
@ -469,131 +461,125 @@ open class FinTsJobExecutor(
|
|||
|
||||
lastCreatedMessage?.let { closeDialog(context) }
|
||||
|
||||
resendMessageInNewDialog(context, lastCreatedMessage, callback)
|
||||
return resendMessageInNewDialog(context, lastCreatedMessage)
|
||||
}
|
||||
|
||||
protected open fun handleUserAsksToChangeTanMediumAndResendLastMessage(context: JobContext, changeTanMediumTo: TanGeneratorTanMedium,
|
||||
changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?,
|
||||
callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun handleUserAsksToChangeTanMediumAndResendLastMessage(context: JobContext, changeTanMediumTo: TanGeneratorTanMedium,
|
||||
changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): BankResponse {
|
||||
|
||||
val lastCreatedMessage = context.dialog.currentMessage
|
||||
|
||||
lastCreatedMessage?.let { closeDialog(context) }
|
||||
|
||||
|
||||
changeTanMedium(context, changeTanMediumTo) { changeTanMediumResponse ->
|
||||
changeTanMediumResultCallback?.invoke(FinTsClientResponse(context, changeTanMediumResponse))
|
||||
val changeTanMediumResponse = changeTanMedium(context, changeTanMediumTo)
|
||||
|
||||
if (changeTanMediumResponse.successful == false || lastCreatedMessage == null) {
|
||||
callback(changeTanMediumResponse)
|
||||
}
|
||||
else {
|
||||
resendMessageInNewDialog(context, lastCreatedMessage, callback)
|
||||
}
|
||||
changeTanMediumResultCallback?.invoke(FinTsClientResponse(context, changeTanMediumResponse))
|
||||
|
||||
if (changeTanMediumResponse.successful == false || lastCreatedMessage == null) {
|
||||
return changeTanMediumResponse
|
||||
}
|
||||
else {
|
||||
return resendMessageInNewDialog(context, lastCreatedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected open fun resendMessageInNewDialog(context: JobContext, lastCreatedMessage: MessageBuilderResult?, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun resendMessageInNewDialog(context: JobContext, lastCreatedMessage: MessageBuilderResult?): BankResponse {
|
||||
|
||||
if (lastCreatedMessage != null) { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message
|
||||
context.startNewDialog(chunkedResponseHandler = context.dialog.chunkedResponseHandler)
|
||||
|
||||
initDialogWithStrongCustomerAuthentication(context) { initDialogResponse ->
|
||||
if (initDialogResponse.successful == false) {
|
||||
callback(initDialogResponse)
|
||||
}
|
||||
else {
|
||||
val newMessage = messageBuilder.rebuildMessage(context, lastCreatedMessage)
|
||||
val initDialogResponse = initDialogWithStrongCustomerAuthentication(context)
|
||||
|
||||
getAndHandleResponseForMessage(context, newMessage) { response ->
|
||||
closeDialog(context)
|
||||
if (initDialogResponse.successful == false) {
|
||||
return initDialogResponse
|
||||
} else {
|
||||
val newMessage = messageBuilder.rebuildMessage(context, lastCreatedMessage)
|
||||
|
||||
callback(response)
|
||||
}
|
||||
}
|
||||
val response = getAndHandleResponseForMessage(context, newMessage)
|
||||
|
||||
closeDialog(context)
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
else {
|
||||
val errorMessage = "There's no last action (like retrieve account transactions, transfer money, ...) to re-send with new TAN method. Probably an internal programming error." // TODO: translate
|
||||
callback(BankResponse(false, internalError = errorMessage)) // should never come to this
|
||||
return BankResponse(false, internalError = errorMessage) // should never come to this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected open fun sendMessageInNewDialogAndHandleResponse(context: JobContext, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null,
|
||||
closeDialog: Boolean = true, createMessage: () -> MessageBuilderResult, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun sendMessageInNewDialogAndHandleResponse(context: JobContext, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess: CustomerSegmentId? = null,
|
||||
closeDialog: Boolean = true, createMessage: () -> MessageBuilderResult): BankResponse {
|
||||
|
||||
context.startNewDialog(closeDialog)
|
||||
|
||||
if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) {
|
||||
initDialogWithStrongCustomerAuthentication(context) { initDialogResponse ->
|
||||
sendMessageAndHandleResponseAfterDialogInitialization(context, initDialogResponse, createMessage, callback)
|
||||
}
|
||||
}
|
||||
else {
|
||||
initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess) { initDialogResponse ->
|
||||
sendMessageAndHandleResponseAfterDialogInitialization(context, initDialogResponse, createMessage, callback)
|
||||
}
|
||||
val initDialogResponse = if (segmentForNonStrongCustomerAuthenticationTwoStepTanProcess == null) {
|
||||
initDialogWithStrongCustomerAuthentication(context)
|
||||
} else {
|
||||
initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context, segmentForNonStrongCustomerAuthenticationTwoStepTanProcess)
|
||||
}
|
||||
|
||||
return sendMessageAndHandleResponseAfterDialogInitialization(context, initDialogResponse, createMessage)
|
||||
}
|
||||
|
||||
private fun sendMessageAndHandleResponseAfterDialogInitialization(context: JobContext, initDialogResponse: BankResponse,
|
||||
createMessage: () -> MessageBuilderResult, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun sendMessageAndHandleResponseAfterDialogInitialization(context: JobContext, initDialogResponse: BankResponse,
|
||||
createMessage: () -> MessageBuilderResult): BankResponse {
|
||||
|
||||
if (initDialogResponse.successful == false) {
|
||||
callback(initDialogResponse)
|
||||
return initDialogResponse
|
||||
}
|
||||
else {
|
||||
val message = createMessage()
|
||||
|
||||
getAndHandleResponseForMessage(context, message) { response ->
|
||||
closeDialog(context)
|
||||
val response = getAndHandleResponseForMessage(context, message)
|
||||
|
||||
callback(response)
|
||||
}
|
||||
closeDialog(context)
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun initDialogWithStrongCustomerAuthentication(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun initDialogWithStrongCustomerAuthentication(context: JobContext): BankResponse {
|
||||
|
||||
// we first need to retrieve supported tan methods and jobs before we can do anything
|
||||
ensureBasicBankDataRetrieved(context) { retrieveBasicBankDataResponse ->
|
||||
if (retrieveBasicBankDataResponse.successful == false) {
|
||||
callback(retrieveBasicBankDataResponse)
|
||||
val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(context)
|
||||
|
||||
if (retrieveBasicBankDataResponse.successful == false) {
|
||||
return retrieveBasicBankDataResponse
|
||||
}
|
||||
else {
|
||||
// as in the next step we have to supply user's tan method, ensure user selected his or her
|
||||
val tanMethodSelectedResponse = ensureTanMethodIsSelected(context)
|
||||
|
||||
if (tanMethodSelectedResponse.successful == false) {
|
||||
return tanMethodSelectedResponse
|
||||
}
|
||||
else {
|
||||
// as in the next step we have to supply user's tan method, ensure user selected his or her
|
||||
ensureTanMethodIsSelected(context) { tanMethodSelectedResponse ->
|
||||
if (tanMethodSelectedResponse.successful == false) {
|
||||
callback(tanMethodSelectedResponse)
|
||||
}
|
||||
else {
|
||||
initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context, callback)
|
||||
}
|
||||
}
|
||||
return initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun initDialogWithStrongCustomerAuthenticationAfterSuccessfulPreconditionChecks(context: JobContext): BankResponse {
|
||||
|
||||
context.startNewDialog(false) // don't know if it's ok for all invocations of this method to set closeDialog to false (was actually only set in getAccounts())
|
||||
|
||||
val message = messageBuilder.createInitDialogMessage(context)
|
||||
|
||||
getAndHandleResponseForMessage(context, message, callback)
|
||||
return getAndHandleResponseForMessage(context, message)
|
||||
}
|
||||
|
||||
protected open fun initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?,
|
||||
callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun initDialogMessageWithoutStrongCustomerAuthenticationAfterSuccessfulChecks(context: JobContext, segmentIdForTwoStepTanProcess: CustomerSegmentId?): BankResponse {
|
||||
|
||||
val message = messageBuilder.createInitDialogMessageWithoutStrongCustomerAuthentication(context, segmentIdForTwoStepTanProcess)
|
||||
|
||||
getAndHandleResponseForMessage(context, message, callback)
|
||||
return getAndHandleResponseForMessage(context, message)
|
||||
}
|
||||
|
||||
protected open fun closeDialog(context: JobContext) {
|
||||
protected open suspend fun closeDialog(context: JobContext) {
|
||||
|
||||
// bank already closed dialog -> there's no need to send dialog end message
|
||||
if (shouldNotCloseDialog(context)) {
|
||||
|
@ -605,82 +591,80 @@ open class FinTsJobExecutor(
|
|||
fireAndForgetMessage(context, dialogEndRequestBody)
|
||||
}
|
||||
|
||||
private fun shouldNotCloseDialog(context: JobContext): Boolean {
|
||||
protected open fun shouldNotCloseDialog(context: JobContext): Boolean {
|
||||
return context.dialog.closeDialog == false || context.dialog.didBankCloseDialog
|
||||
}
|
||||
|
||||
|
||||
protected open fun ensureBasicBankDataRetrieved(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun ensureBasicBankDataRetrieved(context: JobContext): BankResponse {
|
||||
val bank = context.bank
|
||||
|
||||
if (bank.tanMethodsSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) {
|
||||
retrieveBasicDataLikeUsersTanMethods(context) { getBankInfoResponse ->
|
||||
if (getBankInfoResponse.successful == false) {
|
||||
callback(getBankInfoResponse)
|
||||
} else if (bank.tanMethodsSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) {
|
||||
callback(BankResponse(false, internalError =
|
||||
"Could not retrieve basic bank data like supported tan methods or supported jobs")) // TODO: translate // TODO: add as messageToShowToUser
|
||||
} else {
|
||||
callback(BankResponse(true))
|
||||
}
|
||||
val getBankInfoResponse = retrieveBasicDataLikeUsersTanMethods(context)
|
||||
|
||||
if (getBankInfoResponse.successful == false) {
|
||||
return getBankInfoResponse
|
||||
} else if (bank.tanMethodsSupportedByBank.isEmpty() || bank.supportedJobs.isEmpty()) {
|
||||
return BankResponse(false, internalError =
|
||||
"Could not retrieve basic bank data like supported tan methods or supported jobs") // TODO: translate // TODO: add as messageToShowToUser
|
||||
} else {
|
||||
return BankResponse(true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(BankResponse(true))
|
||||
return BankResponse(true)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun ensureTanMethodIsSelected(context: JobContext, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun ensureTanMethodIsSelected(context: JobContext): BankResponse {
|
||||
val bank = context.bank
|
||||
|
||||
if (bank.isTanMethodSelected == false) {
|
||||
if (bank.tanMethodsAvailableForUser.isEmpty()) {
|
||||
retrieveBasicDataLikeUsersTanMethods(context) { retrieveBasicDataResponse ->
|
||||
callback(retrieveBasicDataResponse)
|
||||
}
|
||||
return retrieveBasicDataLikeUsersTanMethods(context)
|
||||
}
|
||||
else {
|
||||
getUsersTanMethod(context) {
|
||||
callback(createNoTanMethodSelectedResponse(bank))
|
||||
}
|
||||
getUsersTanMethod(context)
|
||||
|
||||
return createNoTanMethodSelectedResponse(bank)
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(createNoTanMethodSelectedResponse(bank))
|
||||
return createNoTanMethodSelectedResponse(bank)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNoTanMethodSelectedResponse(bank: BankData): BankResponse {
|
||||
protected open fun createNoTanMethodSelectedResponse(bank: BankData): BankResponse {
|
||||
val noTanMethodSelected = !!!bank.isTanMethodSelected
|
||||
val errorMessage = if (noTanMethodSelected) "User did not select a TAN method" else null // TODO: translate
|
||||
|
||||
return BankResponse(true, noTanMethodSelected = noTanMethodSelected, internalError = errorMessage)
|
||||
}
|
||||
|
||||
open fun getUsersTanMethod(context: JobContext, preferredTanMethods: List<TanMethodType>? = null, done: (Boolean) -> Unit) {
|
||||
open suspend fun getUsersTanMethod(context: JobContext, preferredTanMethods: List<TanMethodType>? = null): Boolean {
|
||||
val bank = context.bank
|
||||
|
||||
if (bank.tanMethodsAvailableForUser.size == 1) { // user has only one TAN method -> set it and we're done
|
||||
bank.selectedTanMethod = bank.tanMethodsAvailableForUser.first()
|
||||
done(true)
|
||||
return true
|
||||
}
|
||||
else {
|
||||
tanMethodSelector.findPreferredTanMethod(bank.tanMethodsAvailableForUser, preferredTanMethods)?.let {
|
||||
bank.selectedTanMethod = it
|
||||
done(true)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
// we know user's supported tan methods, now ask user which one to select
|
||||
val suggestedTanMethod = tanMethodSelector.getSuggestedTanMethod(bank.tanMethodsAvailableForUser)
|
||||
context.callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod) { selectedTanMethod ->
|
||||
if (selectedTanMethod != null) {
|
||||
bank.selectedTanMethod = selectedTanMethod
|
||||
done(true)
|
||||
}
|
||||
else {
|
||||
done(false)
|
||||
}
|
||||
|
||||
val selectedTanMethod = context.callback.askUserForTanMethod(bank.tanMethodsAvailableForUser, suggestedTanMethod)
|
||||
|
||||
if (selectedTanMethod != null) {
|
||||
bank.selectedTanMethod = selectedTanMethod
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,79 +21,78 @@ open class RequestExecutor(
|
|||
) {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(FinTsJobExecutor::class)
|
||||
private val log = LoggerFactory.getLogger(RequestExecutor::class)
|
||||
}
|
||||
|
||||
|
||||
open fun getAndHandleResponseForMessage(message: MessageBuilderResult, context: JobContext,
|
||||
tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit, callback: (BankResponse) -> Unit) {
|
||||
open suspend fun getAndHandleResponseForMessage(message: MessageBuilderResult, context: JobContext, tanRequiredCallback: suspend (TanResponse, BankResponse) -> BankResponse): BankResponse {
|
||||
if (message.createdMessage == null) {
|
||||
log.error("Could not create FinTS message to be sent to bank. isJobAllowed ${message.isJobAllowed}, isJobVersionSupported = ${message.isJobVersionSupported}," +
|
||||
"allowedVersions = ${message.allowedVersions}, supportedVersions = ${message.supportedVersions}.")
|
||||
callback(BankResponse(false, messageThatCouldNotBeCreated = message, internalError = "Could not create FinTS message to be sent to bank")) // TODO: translate
|
||||
return BankResponse(false, messageThatCouldNotBeCreated = message, internalError = "Could not create FinTS message to be sent to bank") // TODO: translate
|
||||
}
|
||||
else {
|
||||
getAndHandleResponseForMessage(context, message.createdMessage) { response ->
|
||||
handleMayRequiresTan(context, response, tanRequiredCallback) { handledResponse ->
|
||||
// if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt
|
||||
handledResponse.aufsetzpunkt?.let { continuationId ->
|
||||
if (handledResponse.followUpResponse == null) { // for re-sent messages followUpResponse is already set and dialog already closed -> would be overwritten with an error response that dialog is closed
|
||||
if (message.isSendEnteredTanMessage() == false) { // for sending TAN no follow up message can be created -> filter out, otherwise chunkedResponseHandler would get called twice for same response
|
||||
context.dialog.chunkedResponseHandler?.invoke(handledResponse)
|
||||
}
|
||||
val response = getAndHandleResponseForMessage(context, message.createdMessage)
|
||||
|
||||
getFollowUpMessageForContinuationId(context, handledResponse, continuationId, message, tanRequiredCallback) { followUpResponse ->
|
||||
handledResponse.followUpResponse = followUpResponse
|
||||
handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null
|
||||
val handledResponse = handleMayRequiresTan(context, response, tanRequiredCallback)
|
||||
|
||||
callback(handledResponse)
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(handledResponse)
|
||||
}
|
||||
// if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt
|
||||
handledResponse.aufsetzpunkt?.let { continuationId ->
|
||||
if (handledResponse.followUpResponse == null) { // for re-sent messages followUpResponse is already set and dialog already closed -> would be overwritten with an error response that dialog is closed
|
||||
if (message.isSendEnteredTanMessage() == false) { // for sending TAN no follow up message can be created -> filter out, otherwise chunkedResponseHandler would get called twice for same response
|
||||
context.dialog.chunkedResponseHandler?.invoke(handledResponse)
|
||||
}
|
||||
?: run {
|
||||
// e.g. response = enter TAN response, but handledResponse is then response after entering TAN, e.g. account transactions
|
||||
// -> chunkedResponseHandler would get called for same handledResponse multiple times
|
||||
if (response == handledResponse) {
|
||||
context.dialog.chunkedResponseHandler?.invoke(handledResponse)
|
||||
}
|
||||
|
||||
callback(handledResponse)
|
||||
}
|
||||
val followUpResponse = getFollowUpMessageForContinuationId(context, handledResponse, continuationId, message, tanRequiredCallback)
|
||||
|
||||
handledResponse.followUpResponse = followUpResponse
|
||||
handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null
|
||||
|
||||
return handledResponse
|
||||
}
|
||||
else {
|
||||
return handledResponse
|
||||
}
|
||||
}
|
||||
?: run {
|
||||
// e.g. response = enter TAN response, but handledResponse is then response after entering TAN, e.g. account transactions
|
||||
// -> chunkedResponseHandler would get called for same handledResponse multiple times
|
||||
if (response == handledResponse) {
|
||||
context.dialog.chunkedResponseHandler?.invoke(handledResponse)
|
||||
}
|
||||
|
||||
return handledResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getAndHandleResponseForMessage(context: JobContext, requestBody: String, callback: (BankResponse) -> Unit) {
|
||||
protected open suspend fun getAndHandleResponseForMessage(context: JobContext, requestBody: String): BankResponse {
|
||||
addMessageLog(context, MessageLogEntryType.Sent, requestBody)
|
||||
|
||||
getResponseForMessage(requestBody, context.bank.finTs3ServerAddress) { webResponse ->
|
||||
val response = handleResponse(context, webResponse)
|
||||
val webResponse = getResponseForMessage(requestBody, context.bank.finTs3ServerAddress)
|
||||
|
||||
val dialog = context.dialog
|
||||
dialog.response = response
|
||||
val response = handleResponse(context, webResponse)
|
||||
|
||||
response.messageHeader?.let { header -> dialog.dialogId = header.dialogId }
|
||||
dialog.didBankCloseDialog = response.didBankCloseDialog
|
||||
val dialog = context.dialog
|
||||
dialog.response = response
|
||||
|
||||
callback(response)
|
||||
}
|
||||
response.messageHeader?.let { header -> dialog.dialogId = header.dialogId }
|
||||
dialog.didBankCloseDialog = response.didBankCloseDialog
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
protected open fun getResponseForMessage(requestBody: String, finTs3ServerAddress: String, callback: (WebClientResponse) -> Unit) {
|
||||
protected open suspend fun getResponseForMessage(requestBody: String, finTs3ServerAddress: String): WebClientResponse {
|
||||
val encodedRequestBody = base64Service.encode(requestBody)
|
||||
|
||||
webClient.post(finTs3ServerAddress, encodedRequestBody, "application/octet-stream", IWebClient.DefaultUserAgent, callback)
|
||||
return webClient.post(finTs3ServerAddress, encodedRequestBody, "application/octet-stream", IWebClient.DefaultUserAgent)
|
||||
}
|
||||
|
||||
open fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) {
|
||||
open suspend fun fireAndForgetMessage(context: JobContext, message: MessageBuilderResult) {
|
||||
message.createdMessage?.let { requestBody ->
|
||||
addMessageLog(context, MessageLogEntryType.Sent, requestBody)
|
||||
|
||||
getResponseForMessage(requestBody, context.bank.finTs3ServerAddress) { }
|
||||
getResponseForMessage(requestBody, context.bank.finTs3ServerAddress)
|
||||
|
||||
// if really needed add received response to message log here
|
||||
}
|
||||
|
@ -129,34 +128,30 @@ open class RequestExecutor(
|
|||
}
|
||||
|
||||
|
||||
protected open fun getFollowUpMessageForContinuationId(context: JobContext, response: BankResponse, continuationId: String, message: MessageBuilderResult,
|
||||
tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit,
|
||||
callback: (BankResponse?) -> Unit) {
|
||||
protected open suspend fun getFollowUpMessageForContinuationId(context: JobContext, response: BankResponse, continuationId: String, message: MessageBuilderResult,
|
||||
tanRequiredCallback: suspend (TanResponse, BankResponse) -> BankResponse): BankResponse? {
|
||||
|
||||
messageBuilder.rebuildMessageWithContinuationId(context, message, continuationId)?.let { followUpMessage ->
|
||||
getAndHandleResponseForMessage(followUpMessage, context, tanRequiredCallback, callback)
|
||||
return getAndHandleResponseForMessage(followUpMessage, context, tanRequiredCallback)
|
||||
}
|
||||
?: run { callback(null) }
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
protected open fun handleMayRequiresTan(context: JobContext, response: BankResponse,
|
||||
tanRequiredCallback: (TanResponse, BankResponse, callback: (BankResponse) -> Unit) -> Unit,
|
||||
callback: (BankResponse) -> Unit) { // TODO: use response from DialogContext
|
||||
protected open suspend fun handleMayRequiresTan(context: JobContext, response: BankResponse,
|
||||
tanRequiredCallback: suspend (TanResponse, BankResponse) -> BankResponse): BankResponse { // TODO: use response from DialogContext
|
||||
|
||||
if (response.isStrongAuthenticationRequired) {
|
||||
if (context.dialog.abortIfTanIsRequired) {
|
||||
response.tanRequiredButWeWereToldToAbortIfSo = true
|
||||
|
||||
callback(response)
|
||||
return
|
||||
return response
|
||||
}
|
||||
else if (response.tanResponse != null) {
|
||||
response.tanResponse?.let { tanResponse ->
|
||||
tanRequiredCallback(tanResponse, response, callback)
|
||||
return tanRequiredCallback(tanResponse, response)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +162,7 @@ open class RequestExecutor(
|
|||
// TODO: also check '9931 Sperrung des Kontos nach %1 Fehlversuchen' -> if %1 == 3 synchronize TAN generator
|
||||
// as it's quite unrealistic that user entered TAN wrong three times, in most cases TAN generator is not synchronized
|
||||
|
||||
callback(response)
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,15 +16,15 @@ interface FinTsClientCallback {
|
|||
* If you do not support an enter tan dialog or if your enter tan dialog supports selecting a TAN method, it's
|
||||
* best returning [suggestedTanMethod] and to not show an extra select TAN method dialog.
|
||||
*/
|
||||
fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?, callback: (TanMethod?) -> Unit)
|
||||
suspend fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?): TanMethod?
|
||||
|
||||
fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
|
||||
suspend fun enterTan(bank: BankData, tanChallenge: TanChallenge): EnterTanResult
|
||||
|
||||
/**
|
||||
* 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]
|
||||
*/
|
||||
fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
|
||||
suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult
|
||||
|
||||
}
|
|
@ -6,18 +6,16 @@ import net.dankito.banking.fints.model.*
|
|||
|
||||
open class NoOpFinTsClientCallback : FinTsClientCallback {
|
||||
|
||||
override fun askUserForTanMethod(supportedTanMethods: List<TanMethod>,
|
||||
suggestedTanMethod: TanMethod?, callback: (TanMethod?) -> Unit) {
|
||||
|
||||
callback(suggestedTanMethod)
|
||||
override suspend fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?): TanMethod? {
|
||||
return suggestedTanMethod
|
||||
}
|
||||
|
||||
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||
callback(EnterTanResult.userDidNotEnterTan())
|
||||
override suspend fun enterTan(bank: BankData, tanChallenge: TanChallenge): EnterTanResult {
|
||||
return EnterTanResult.userDidNotEnterTan()
|
||||
}
|
||||
|
||||
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||
callback(EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
||||
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
|
||||
return EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
||||
}
|
||||
|
||||
}
|
|
@ -5,9 +5,9 @@ import net.dankito.banking.fints.model.*
|
|||
|
||||
|
||||
open class SimpleFinTsClientCallback(
|
||||
protected val enterTan: ((bank: BankData, tanChallenge: TanChallenge) -> EnterTanResult)? = null,
|
||||
protected val enterTanGeneratorAtc: ((bank: BankData, tanMedium: TanGeneratorTanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
||||
protected val askUserForTanMethod: ((supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?) -> TanMethod?)? = null
|
||||
protected open val enterTan: ((bank: BankData, tanChallenge: TanChallenge) -> EnterTanResult)? = null,
|
||||
protected open val enterTanGeneratorAtc: ((bank: BankData, tanMedium: TanGeneratorTanMedium) -> EnterTanGeneratorAtcResult)? = null,
|
||||
protected open val askUserForTanMethod: ((supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?) -> TanMethod?)? = null
|
||||
) : FinTsClientCallback {
|
||||
|
||||
constructor() : this(null) // Swift does not support default parameter values -> create constructor overloads
|
||||
|
@ -15,18 +15,17 @@ open class SimpleFinTsClientCallback(
|
|||
constructor(enterTan: ((bank: BankData, tanChallenge: TanChallenge) -> EnterTanResult)?) : this(enterTan, null)
|
||||
|
||||
|
||||
override fun askUserForTanMethod(supportedTanMethods: List<TanMethod>,
|
||||
suggestedTanMethod: TanMethod?, callback: (TanMethod?) -> Unit) {
|
||||
override suspend fun askUserForTanMethod(supportedTanMethods: List<TanMethod>, suggestedTanMethod: TanMethod?): TanMethod? {
|
||||
|
||||
callback(askUserForTanMethod?.invoke(supportedTanMethods, suggestedTanMethod) ?: suggestedTanMethod)
|
||||
return askUserForTanMethod?.invoke(supportedTanMethods, suggestedTanMethod) ?: suggestedTanMethod
|
||||
}
|
||||
|
||||
override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
|
||||
callback(enterTan?.invoke(bank, tanChallenge) ?: EnterTanResult.userDidNotEnterTan())
|
||||
override suspend fun enterTan(bank: BankData, tanChallenge: TanChallenge): EnterTanResult {
|
||||
return enterTan?.invoke(bank, tanChallenge) ?: EnterTanResult.userDidNotEnterTan()
|
||||
}
|
||||
|
||||
override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
|
||||
callback(enterTanGeneratorAtc?.invoke(bank, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc())
|
||||
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
|
||||
return enterTanGeneratorAtc?.invoke(bank, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,10 @@ interface IWebClient {
|
|||
}
|
||||
|
||||
|
||||
fun post(url: String, body: String, contentType: String = "application/octet-stream", userAgent: String = DefaultUserAgent, callback: (WebClientResponse) -> Unit)
|
||||
suspend fun post(url: String, body: String): WebClientResponse { // some platforms don't support default parameters
|
||||
return post(url, body)
|
||||
}
|
||||
|
||||
suspend fun post(url: String, body: String, contentType: String = "application/octet-stream", userAgent: String = DefaultUserAgent): WebClientResponse
|
||||
|
||||
}
|
|
@ -35,13 +35,11 @@ open class KtorWebClient : IWebClient {
|
|||
}
|
||||
|
||||
|
||||
override fun post(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) {
|
||||
GlobalScope.async {
|
||||
postInCoroutine(url, body, contentType, userAgent, callback)
|
||||
}
|
||||
override suspend fun post(url: String, body: String, contentType: String, userAgent: String): WebClientResponse {
|
||||
return postInCoroutine(url, body, contentType, userAgent)
|
||||
}
|
||||
|
||||
protected open suspend fun postInCoroutine(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) {
|
||||
protected open suspend fun postInCoroutine(url: String, body: String, contentType: String, userAgent: String): WebClientResponse {
|
||||
try {
|
||||
val clientResponse = client.post(url) {
|
||||
contentType(ContentType.Application.OctetStream)
|
||||
|
@ -50,11 +48,11 @@ open class KtorWebClient : IWebClient {
|
|||
|
||||
val responseBody = clientResponse.bodyAsText()
|
||||
|
||||
callback(WebClientResponse(clientResponse.status.value == 200, clientResponse.status.value, body = responseBody))
|
||||
return WebClientResponse(clientResponse.status.value == 200, clientResponse.status.value, body = responseBody)
|
||||
} catch (e: Exception) {
|
||||
log.error(e) { "Could not send request to url '$url'" }
|
||||
|
||||
callback(WebClientResponse(false, error = e))
|
||||
return WebClientResponse(false, error = e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ class ProxyingWebClient(proxyUrl: String, private val delegate: IWebClient) : IW
|
|||
private val proxyUrl = if (proxyUrl.endsWith("/")) proxyUrl else proxyUrl + "/"
|
||||
|
||||
|
||||
override fun post(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) {
|
||||
delegate.post(proxyUrl + url, body, contentType, userAgent, callback)
|
||||
override suspend fun post(url: String, body: String, contentType: String, userAgent: String): WebClientResponse {
|
||||
return delegate.post(proxyUrl + url, body, contentType, userAgent)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.FinTsClientDeprecated
|
||||
import net.dankito.banking.fints.FinTsJobExecutor
|
||||
import net.dankito.banking.fints.RequestExecutor
|
||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
||||
import net.dankito.banking.fints.model.AddAccountParameter
|
||||
import net.dankito.banking.fints.model.RetrievedAccountData
|
||||
import net.dankito.banking.fints.response.client.AddAccountResponse
|
||||
import net.dankito.banking.fints.webclient.BlockingKtorWebClient
|
||||
import net.dankito.utils.multiplatform.extensions.*
|
||||
import platform.posix.exit
|
||||
|
||||
|
@ -25,13 +22,13 @@ class Application {
|
|||
|
||||
fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String) {
|
||||
runBlocking {
|
||||
val client = FinTsClientDeprecated(SimpleFinTsClientCallback(), FinTsJobExecutor(RequestExecutor(webClient = BlockingKtorWebClient())))
|
||||
val client = FinTsClientDeprecated(SimpleFinTsClientCallback())
|
||||
|
||||
client.addAccountAsync(AddAccountParameter(bankCode, customerId, pin, finTs3ServerAddress)) { response ->
|
||||
println("Retrieved response from ${response.bank.bankName} for ${response.bank.customerName}")
|
||||
val response = client.addAccountAsync(AddAccountParameter(bankCode, customerId, pin, finTs3ServerAddress))
|
||||
|
||||
displayRetrievedAccountData(response)
|
||||
}
|
||||
println("Retrieved response from ${response.bank.bankName} for ${response.bank.customerName}")
|
||||
|
||||
displayRetrievedAccountData(response)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package net.dankito.banking.fints.webclient
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
open class BlockingKtorWebClient : KtorWebClient() {
|
||||
|
||||
|
||||
override fun post(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) {
|
||||
runBlocking {
|
||||
postInCoroutine(url, body, contentType, userAgent, callback)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue