2020-01-02 21:39:02 +00:00
package net.dankito.banking
2020-06-03 15:49:29 +00:00
import com.soywiz.klock.jvm.toDate
import net.dankito.banking.extensions.toKlockDate
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
2020-01-25 19:29:44 +00:00
import net.dankito.utils.serialization.JacksonJsonSerializer
2020-06-03 15:49:29 +00:00
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.toAmount
import net.dankito.banking.extensions.toMoney
2020-01-25 19:29:44 +00:00
import org.slf4j.LoggerFactory
import java.io.File
2020-07-02 21:08:35 +00:00
import java.math.BigDecimal
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-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-05-16 18:19:42 +00:00
private val log = LoggerFactory . getLogger ( fints4kBankingClient :: class . java )
2020-06-03 15:49:29 +00:00
init {
net . dankito . banking . fints . util . log . LoggerFactory . loggerFactory = net . dankito . banking . fints . util . log . Slf4jLoggerFactory ( )
}
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-01-25 19:29:44 +00:00
protected val serializer = JacksonJsonSerializer ( )
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-06-14 21:15:52 +00:00
protected val client = FinTsClientForCustomer ( bank , fints4kCustomer , object : FinTsClientCallback {
2020-01-19 15:50:26 +00:00
override fun askUserForTanProcedure ( supportedTanProcedures : List < TanProcedure > , suggestedTanProcedure : TanProcedure ? ) : TanProcedure ? {
// we simply return suggestedTanProcedure as even so it's not user's preferred TAN procedure she still can select it in EnterTanDialog
return suggestedTanProcedure
}
2020-01-02 23:35:36 +00:00
override fun enterTan ( customer : CustomerData , tanChallenge : TanChallenge ) : EnterTanResult {
2020-06-14 21:15:52 +00:00
mapper . updateTanMediaAndProcedures ( this @fints4kBankingClient . customer , customer )
2020-01-19 15:22:43 +00:00
2020-06-14 21:15:52 +00:00
val result = callback . enterTan ( this @fints4kBankingClient . customer , mapper . mapTanChallenge ( tanChallenge ) )
2020-01-02 23:35:36 +00:00
return mapper . mapEnterTanResult ( result , customer )
}
override fun enterTanGeneratorAtc ( customer : CustomerData , tanMedium : TanGeneratorTanMedium ) : EnterTanGeneratorAtcResult {
2020-06-14 21:15:52 +00:00
mapper . updateTanMediaAndProcedures ( this @fints4kBankingClient . customer , customer )
2020-01-19 15:22:43 +00:00
2020-01-02 23:35:36 +00:00
val result = callback . enterTanGeneratorAtc ( mapper . mapTanMedium ( tanMedium ) )
return mapper . mapEnterTanGeneratorAtcResult ( result )
}
2020-06-14 19:09:36 +00:00
} , webClient , base64Service )
2020-01-02 21:39:02 +00:00
2020-05-16 20:51:51 +00:00
override val messageLogWithoutSensitiveData : List < MessageLogEntry >
2020-06-14 21:15:52 +00:00
get ( ) = client . messageLogWithoutSensitiveData . map { MessageLogEntry ( it . message , it . time . toDate ( ) , 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-06-14 21:15:52 +00:00
this . customer = mapper . mapCustomer ( fints4kCustomer , bank )
val mappedResponse = mapper . mapResponse ( customer , response )
2020-01-02 21:39:02 +00:00
2020-01-25 19:29:44 +00:00
saveData ( )
2020-01-02 21:39:02 +00:00
callback ( mappedResponse )
}
}
override fun getTransactionsAsync ( bankAccount : BankAccount , parameter : GetTransactionsParameter , callback : ( GetTransactionsResponse ) -> Unit ) {
2020-06-14 21:15:52 +00:00
val account = mapper . findAccountForBankAccount ( fints4kCustomer , bankAccount )
2020-01-02 21:39:02 +00:00
2020-06-14 19:00:45 +00:00
if ( account == null ) { // TODO: in this case retrieve data from bank, all data should be re-creatable
2020-05-25 17:05:56 +00:00
callback ( GetTransactionsResponse ( bankAccount , false , " Cannot find account for ${bankAccount.identifier} " ) ) // TODO: translate
2020-01-20 23:54:06 +00:00
}
else {
2020-06-03 15:49:29 +00:00
client . getTransactionsAsync ( GetTransactionsParameter ( parameter . alsoRetrieveBalance , parameter . fromDate ?. toKlockDate ( ) , parameter . toDate ?. toKlockDate ( ) , null , parameter . abortIfTanIsRequired ,
2020-05-20 22:29:53 +00:00
{ parameter . retrievedChunkListener ?. invoke ( mapper . mapTransactions ( bankAccount , it ) ) } ) , account ) { response ->
2020-01-02 21:39:02 +00:00
2020-01-20 23:54:06 +00:00
val mappedResponse = mapper . mapResponse ( bankAccount , response )
2020-01-25 19:29:44 +00:00
saveData ( )
2020-01-20 23:54:06 +00:00
callback ( mappedResponse )
}
2020-01-02 21:39:02 +00:00
}
}
2020-01-20 23:54:06 +00:00
override fun transferMoneyAsync ( data : TransferMoneyData , bankAccount : BankAccount , callback : ( BankingClientResponse ) -> Unit ) {
2020-06-14 21:15:52 +00:00
val account = mapper . findAccountForBankAccount ( fints4kCustomer , bankAccount )
2020-01-20 23:54:06 +00:00
if ( account == null ) {
callback ( BankingClientResponse ( false , " Cannot find account for ${bankAccount.identifier} " ) ) // TODO: translate
}
else {
2020-07-02 21:08:35 +00:00
val mappedData = BankTransferData ( data . creditorName , data . creditorIban , data . creditorBic , data . amount . toMoney ( ) , data . usage , data . instantPayment )
2020-01-02 21:39:02 +00:00
2020-01-20 23:54:06 +00:00
client . doBankTransferAsync ( mappedData , account ) { response ->
2020-01-25 19:29:44 +00:00
saveData ( )
2020-01-20 23:54:06 +00:00
callback ( mapper . mapResponse ( response ) )
}
2020-01-02 21:39:02 +00:00
}
}
2020-01-19 15:22:43 +00:00
2020-01-25 19:29:44 +00:00
override fun restoreData ( ) {
2020-05-16 18:19:42 +00:00
val deserializedCustomer = serializer . deserializeObject ( getFints4kClientDataFile ( ) , CustomerData :: class . java )
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 ( )
clientDataFile . parentFile . mkdirs ( )
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-06-14 21:15:52 +00:00
return File ( File ( dataFolder , " fints4k-client " ) , " ${bank.bankCode} _ ${fints4kCustomer.customerId} _ $fints4kClientDataFilename " )
2020-01-25 19:29:44 +00:00
}
2020-01-02 21:39:02 +00:00
}