2020-01-02 21:39:02 +00:00
package net.dankito.banking
2020-01-02 23:35:36 +00:00
import net.dankito.banking.ui.BankingClientCallback
2020-01-02 21:39:02 +00:00
import net.dankito.banking.ui.IBankingClient
2020-06-14 21:15:52 +00:00
import net.dankito.banking.ui.model.Customer
2020-01-02 21:39:02 +00:00
import net.dankito.banking.ui.model.BankAccount
2020-05-16 20:51:51 +00:00
import net.dankito.banking.ui.model.MessageLogEntry
2020-01-02 21:39:02 +00:00
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
2020-05-18 19:23:03 +00:00
import net.dankito.banking.fints.FinTsClientForCustomer
import net.dankito.banking.fints.callback.FinTsClientCallback
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.banking.fints.model.*
2020-06-03 15:49:29 +00:00
import net.dankito.banking.mapper.BankDataMapper
2020-05-18 19:23:03 +00:00
import net.dankito.banking.fints.util.IBase64Service
2020-06-03 15:49:29 +00:00
import net.dankito.banking.fints.util.PureKotlinBase64Service
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient
import net.dankito.banking.bankfinder.BankInfo
2020-07-02 21:08:35 +00:00
import net.dankito.banking.extensions.toMoney
2020-07-15 19:45:20 +00:00
import net.dankito.banking.fints.response.client.FinTsClientResponse
2020-07-12 10:26:16 +00:00
import net.dankito.banking.util.ISerializer
import net.dankito.utils.multiplatform.File
import net.dankito.utils.multiplatform.log.LoggerFactory
2020-01-02 21:39:02 +00:00
2020-05-16 18:19:42 +00:00
open class fints4kBankingClient (
2020-01-02 21:39:02 +00:00
bankInfo : BankInfo ,
customerId : String ,
pin : String ,
2020-01-25 19:29:44 +00:00
protected val dataFolder : File ,
2020-07-12 10:26:16 +00:00
protected val serializer : ISerializer ,
2020-06-03 15:49:29 +00:00
webClient : IWebClient = KtorWebClient ( ) ,
base64Service : IBase64Service = PureKotlinBase64Service ( ) ,
2020-01-02 23:35:36 +00:00
callback : BankingClientCallback
2020-01-02 21:39:02 +00:00
) : IBankingClient {
2020-01-25 19:29:44 +00:00
companion object {
2020-05-16 18:19:42 +00:00
val fints4kClientDataFilename = " fints4kClientData.json "
2020-01-25 19:29:44 +00:00
2020-07-12 10:26:16 +00:00
private val log = LoggerFactory . getLogger ( fints4kBankingClient :: class )
2020-01-25 19:29:44 +00:00
}
2020-05-16 18:19:42 +00:00
protected val mapper = net . dankito . banking . mapper . fints4kModelMapper ( )
2020-01-02 21:39:02 +00:00
protected val bankDataMapper = BankDataMapper ( )
2020-07-27 14:57:38 +00:00
protected var didTryToGetAccountDataFromBank = false
2020-01-25 19:29:44 +00:00
2020-01-02 21:39:02 +00:00
protected val bank = bankDataMapper . mapFromBankInfo ( bankInfo )
2020-06-14 21:15:52 +00:00
protected val fints4kCustomer = CustomerData ( customerId , pin )
2020-01-02 21:39:02 +00:00
2020-06-14 21:15:52 +00:00
protected var customer : Customer = mapper . mapCustomer ( fints4kCustomer , bank ) // temporary save temp customer, we update with data from server response like BankAccounts later
2020-01-02 23:35:36 +00:00
2020-07-15 19:45:20 +00:00
protected open val client = FinTsClientForCustomer ( bank , fints4kCustomer , createFinTsClientCallback ( callback ) , webClient , base64Service )
2020-01-02 21:39:02 +00:00
2020-05-16 20:51:51 +00:00
override val messageLogWithoutSensitiveData : List < MessageLogEntry >
2020-07-12 10:26:16 +00:00
get ( ) = client . messageLogWithoutSensitiveData . map { MessageLogEntry ( it . message , it . time , customer ) }
2020-05-16 20:51:51 +00:00
2020-01-02 21:39:02 +00:00
override fun addAccountAsync ( callback : ( AddAccountResponse ) -> Unit ) {
client . addAccountAsync { response ->
2020-07-15 19:45:20 +00:00
handleAddAccountResponse ( response , callback )
}
}
2020-01-02 21:39:02 +00:00
2020-07-15 19:45:20 +00:00
protected open fun handleAddAccountResponse ( response : net . dankito . banking . fints . response . client . AddAccountResponse ,
callback : ( AddAccountResponse ) -> Unit ) {
this . customer = mapper . mapCustomer ( fints4kCustomer , bank )
val mappedResponse = mapper . mapResponse ( customer , response )
2020-01-25 19:29:44 +00:00
2020-07-15 19:45:20 +00:00
saveData ( )
callback ( mappedResponse )
2020-01-02 21:39:02 +00:00
}
2020-07-15 19:45:20 +00:00
2020-01-02 21:39:02 +00:00
override fun getTransactionsAsync ( bankAccount : BankAccount , parameter : GetTransactionsParameter , callback : ( GetTransactionsResponse ) -> Unit ) {
2020-07-27 14:57:38 +00:00
findAccountForBankAccount ( bankAccount ) { account , errorMessage ->
if ( account == null ) {
callback ( GetTransactionsResponse ( bankAccount , false , errorMessage ) )
}
else {
val mappedParameter = GetTransactionsParameter ( parameter . alsoRetrieveBalance , parameter . fromDate ,
parameter . toDate , null , parameter . abortIfTanIsRequired ,
{ parameter . retrievedChunkListener ?. invoke ( mapper . mapTransactions ( bankAccount , it ) ) } )
doGetTransactionsAsync ( mappedParameter , account , bankAccount , callback )
}
2020-07-15 19:45:20 +00:00
}
}
2020-01-25 19:29:44 +00:00
2020-07-15 19:45:20 +00:00
protected open fun doGetTransactionsAsync ( parameter : net . dankito . banking . fints . model . GetTransactionsParameter ,
account : AccountData , bankAccount : BankAccount , callback : ( GetTransactionsResponse ) -> Unit ) {
client . getTransactionsAsync ( parameter , account ) { response ->
handleGetTransactionsResponse ( bankAccount , response , callback )
2020-01-02 21:39:02 +00:00
}
}
2020-07-15 19:45:20 +00:00
protected open fun handleGetTransactionsResponse ( bankAccount : BankAccount , response : net . dankito . banking . fints . response . client . GetTransactionsResponse ,
callback : ( GetTransactionsResponse ) -> Unit ) {
val mappedResponse = mapper . mapResponse ( bankAccount , response )
saveData ( )
callback ( mappedResponse )
}
2020-01-20 23:54:06 +00:00
override fun transferMoneyAsync ( data : TransferMoneyData , bankAccount : BankAccount , callback : ( BankingClientResponse ) -> Unit ) {
2020-07-27 14:57:38 +00:00
findAccountForBankAccount ( bankAccount ) { account , errorMessage ->
if ( account == null ) {
callback ( BankingClientResponse ( false , errorMessage ) )
}
else {
val mappedData = BankTransferData ( data . creditorName , data . creditorIban , data . creditorBic , data . amount . toMoney ( ) , data . usage , data . instantPayment )
doBankTransferAsync ( mappedData , account , callback )
}
2020-07-15 19:45:20 +00:00
}
}
2020-01-25 19:29:44 +00:00
2020-07-15 19:45:20 +00:00
protected open fun doBankTransferAsync ( data : BankTransferData , account : AccountData , callback : ( BankingClientResponse ) -> Unit ) {
client . doBankTransferAsync ( data , account ) { response ->
handleBankTransferResponse ( callback , response )
2020-01-02 21:39:02 +00:00
}
}
2020-01-19 15:22:43 +00:00
2020-07-15 19:45:20 +00:00
protected open fun handleBankTransferResponse ( callback : ( BankingClientResponse ) -> Unit , response : FinTsClientResponse ) {
saveData ( )
callback ( mapper . mapResponse ( response ) )
}
2020-01-25 19:29:44 +00:00
2020-07-27 14:57:38 +00:00
protected open fun findAccountForBankAccount ( bankAccount : BankAccount , findAccountResult : ( AccountData ? , error : String ? ) -> Unit ) {
val mappedAccount = mapper . findAccountForBankAccount ( fints4kCustomer , bankAccount )
if ( mappedAccount != null ) {
findAccountResult ( mappedAccount , null )
}
else if ( didTryToGetAccountDataFromBank == false ) { // then try to get account data by fetching data from bank
addAccountAsync { response ->
didTryToGetAccountDataFromBank = !! ! response . isSuccessful
findAccountResult ( mapper . findAccountForBankAccount ( fints4kCustomer , bankAccount ) ,
response . errorToShowToUser ?: response . error ?. message )
}
}
else {
findAccountResult ( null , " Cannot find account for ${bankAccount.identifier} " ) // TODO: translate
}
}
2020-01-25 19:29:44 +00:00
override fun restoreData ( ) {
2020-07-12 10:26:16 +00:00
val deserializedCustomer = serializer . deserializeObject ( getFints4kClientDataFile ( ) , CustomerData :: class )
2020-01-25 19:29:44 +00:00
deserializedCustomer ?. let {
2020-06-14 21:15:52 +00:00
mapper . updateCustomer ( fints4kCustomer , deserializedCustomer )
2020-04-28 16:17:47 +00:00
2020-06-14 21:15:52 +00:00
customer = mapper . mapCustomer ( fints4kCustomer , bank )
2020-01-25 19:29:44 +00:00
}
}
protected open fun saveData ( ) {
try {
2020-06-14 19:00:45 +00:00
val clientDataFile = getFints4kClientDataFile ( )
2020-06-14 21:15:52 +00:00
serializer . serializeObject ( fints4kCustomer , clientDataFile )
2020-01-25 19:29:44 +00:00
} catch ( e : Exception ) {
2020-06-14 21:15:52 +00:00
log . error ( " Could not save customer data for $fints4kCustomer " , e )
2020-01-25 19:29:44 +00:00
}
}
2020-05-16 18:19:42 +00:00
protected open fun getFints4kClientDataFile ( ) : File {
2020-07-27 14:58:29 +00:00
val folder = File ( dataFolder , " fints4k-client " )
folder . mkdirs ( )
return File ( folder , " ${bank.bankCode} _ ${fints4kCustomer.customerId} _ $fints4kClientDataFilename " )
2020-01-25 19:29:44 +00:00
}
2020-07-15 19:45:20 +00:00
2020-07-21 08:59:33 +00:00
protected open fun createFinTsClientCallback ( clientCallback : BankingClientCallback ) : FinTsClientCallback {
2020-07-15 19:45:20 +00:00
return object : FinTsClientCallback {
2020-07-21 08:59:33 +00:00
override fun askUserForTanProcedure ( supportedTanProcedures : List < TanProcedure > , suggestedTanProcedure : TanProcedure ? , callback : ( TanProcedure ? ) -> Unit ) {
handleAskUserForTanProcedure ( supportedTanProcedures , suggestedTanProcedure , callback )
2020-07-15 19:45:20 +00:00
}
2020-07-21 08:59:33 +00:00
override fun enterTan ( customer : CustomerData , tanChallenge : TanChallenge , callback : ( EnterTanResult ) -> Unit ) {
handleEnterTan ( customer , tanChallenge , callback , clientCallback )
2020-07-15 19:45:20 +00:00
}
2020-07-21 08:59:33 +00:00
override fun enterTanGeneratorAtc ( customer : CustomerData , tanMedium : TanGeneratorTanMedium , callback : ( EnterTanGeneratorAtcResult ) -> Unit ) {
handleEnterTanGeneratorAtc ( customer , tanMedium , callback , clientCallback )
2020-07-15 19:45:20 +00:00
}
}
}
2020-07-21 08:59:33 +00:00
protected open fun handleAskUserForTanProcedure ( supportedTanProcedures : List < TanProcedure > , suggestedTanProcedure : TanProcedure ? , callback : ( TanProcedure ? ) -> Unit ) {
2020-07-15 19:45:20 +00:00
// we simply return suggestedTanProcedure as even so it's not user's preferred TAN procedure she still can select it in EnterTanDialog
2020-07-21 08:59:33 +00:00
callback ( suggestedTanProcedure )
2020-07-15 19:45:20 +00:00
}
2020-07-21 08:59:33 +00:00
protected open fun handleEnterTan ( customer : CustomerData , tanChallenge : TanChallenge , enterTanCallback : ( EnterTanResult ) -> Unit , clientCallback : BankingClientCallback ) {
2020-07-15 19:45:20 +00:00
mapper . updateTanMediaAndProcedures ( this @fints4kBankingClient . customer , customer )
2020-07-21 08:59:33 +00:00
clientCallback . enterTan ( this @fints4kBankingClient . customer , mapper . mapTanChallenge ( tanChallenge ) ) { result ->
enterTanCallback ( mapper . mapEnterTanResult ( result , customer ) )
}
2020-07-15 19:45:20 +00:00
}
2020-07-21 08:59:33 +00:00
protected open fun handleEnterTanGeneratorAtc ( customer : CustomerData , tanMedium : TanGeneratorTanMedium , enterAtcCallback : ( EnterTanGeneratorAtcResult ) -> Unit , clientCallback : BankingClientCallback ) {
2020-07-15 19:45:20 +00:00
mapper . updateTanMediaAndProcedures ( this @fints4kBankingClient . customer , customer )
2020-07-21 08:59:33 +00:00
clientCallback . enterTanGeneratorAtc ( mapper . mapTanMedium ( tanMedium ) ) { result ->
enterAtcCallback ( mapper . mapEnterTanGeneratorAtcResult ( result ) )
}
2020-07-15 19:45:20 +00:00
}
2020-01-02 21:39:02 +00:00
}