216 lines
9.5 KiB
Kotlin
216 lines
9.5 KiB
Kotlin
package net.dankito.banking
|
|
|
|
import net.dankito.banking.model.AccountCredentials
|
|
import net.dankito.banking.ui.BankingClientCallback
|
|
import net.dankito.banking.ui.model.Customer
|
|
import net.dankito.banking.ui.model.tan.FlickerCodeTanChallenge
|
|
import net.dankito.banking.ui.model.tan.ImageTanChallenge
|
|
import net.dankito.banking.ui.model.tan.TanChallenge
|
|
import net.dankito.banking.ui.model.tan.TanImage
|
|
import net.dankito.banking.util.hbci4jModelMapper
|
|
import net.dankito.utils.multiplatform.Date
|
|
import org.kapott.hbci.callback.AbstractHBCICallback
|
|
import org.kapott.hbci.callback.HBCICallback
|
|
import org.kapott.hbci.manager.HBCIUtils
|
|
import org.kapott.hbci.manager.MatrixCode
|
|
import org.kapott.hbci.manager.QRCode
|
|
import org.kapott.hbci.passport.HBCIPassport
|
|
import org.slf4j.LoggerFactory
|
|
|
|
|
|
/**
|
|
* Ueber diesen Callback kommuniziert HBCI4Java mit dem Benutzer und fragt die benoetigten
|
|
* Informationen wie Benutzerkennung, PIN usw. ab.
|
|
*/
|
|
open class HbciCallback(
|
|
protected val credentials: AccountCredentials,
|
|
protected val customer: Customer,
|
|
protected val mapper: hbci4jModelMapper,
|
|
protected val callback: BankingClientCallback
|
|
) : AbstractHBCICallback() {
|
|
|
|
companion object {
|
|
private val log = LoggerFactory.getLogger(HbciCallback::class.java)
|
|
}
|
|
|
|
|
|
/**
|
|
* @see org.kapott.hbci.callback.HBCICallback.log
|
|
*/
|
|
override fun log(msg: String, level: Int, date: Date, trace: StackTraceElement) {
|
|
// Ausgabe von Log-Meldungen bei Bedarf
|
|
when (level) {
|
|
HBCIUtils.LOG_ERR -> log.error(msg)
|
|
HBCIUtils.LOG_WARN -> log.warn(msg)
|
|
HBCIUtils.LOG_INFO-> log.info(msg)
|
|
HBCIUtils.LOG_DEBUG, HBCIUtils.LOG_DEBUG2 -> log.debug(msg)
|
|
else -> log.trace(msg)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.kapott.hbci.callback.HBCICallback.callback
|
|
*/
|
|
override fun callback(passport: HBCIPassport, reason: Int, msg: String, datatype: Int, retData: StringBuffer) {
|
|
log.info("Callback: [$reason] $msg ($retData)") // TODO: remove again
|
|
|
|
// Diese Funktion ist wichtig. Ueber die fragt HBCI4Java die benoetigten Daten von uns ab.
|
|
when (reason) {
|
|
// Mit dem Passwort verschluesselt HBCI4Java die Passport-Datei.
|
|
// Wir nehmen hier der Einfachheit halber direkt die PIN. In der Praxis
|
|
// sollte hier aber ein staerkeres Passwort genutzt werden.
|
|
// Die Ergebnis-Daten muessen in dem StringBuffer "retData" platziert werden.
|
|
// if you like or need to change your pin, return your old one for NEED_PASSPHRASE_LOAD and your new
|
|
// one for NEED_PASSPHRASE_SAVE
|
|
HBCICallback.NEED_PASSPHRASE_LOAD, HBCICallback.NEED_PASSPHRASE_SAVE -> retData.replace(0, retData.length, credentials.password)
|
|
|
|
|
|
/* Customer (authentication) data */
|
|
|
|
// BLZ wird benoetigt
|
|
HBCICallback.NEED_BLZ -> retData.replace(0, retData.length, credentials.bankCode)
|
|
|
|
// Die Benutzerkennung
|
|
HBCICallback.NEED_USERID -> retData.replace(0, retData.length, credentials.customerId)
|
|
|
|
// Die Kundenkennung. Meist identisch mit der Benutzerkennung.
|
|
// Bei manchen Banken kann man die auch leer lassen
|
|
HBCICallback.NEED_CUSTOMERID -> retData.replace(0, retData.length, credentials.customerId)
|
|
|
|
// PIN wird benoetigt
|
|
HBCICallback.NEED_PT_PIN -> retData.replace(0, retData.length, credentials.password)
|
|
|
|
|
|
/* TAN */
|
|
|
|
// ADDED: Auswaehlen welches PinTan Verfahren verwendet werden soll
|
|
HBCICallback.NEED_PT_SECMECH -> selectTanProcedure(retData.toString())?.let { selectedTanProcedure ->
|
|
customer.selectedTanProcedure = selectedTanProcedure
|
|
retData.replace(0, retData.length, selectedTanProcedure.bankInternalProcedureCode)
|
|
}
|
|
|
|
// chipTan or simple TAN request (iTAN, smsTAN, ...)
|
|
HBCICallback.NEED_PT_TAN -> {
|
|
getTanFromUser(customer, msg, retData.toString())?.let { enteredTan ->
|
|
retData.replace(0, retData.length, enteredTan)
|
|
}
|
|
}
|
|
|
|
// chipTAN-QR
|
|
HBCICallback.NEED_PT_QRTAN -> { // use class QRCode to display QR code
|
|
val qrData = retData.toString()
|
|
val qrCode = QRCode(qrData, msg)
|
|
val enterTanResult = callback.enterTan(customer, ImageTanChallenge(TanImage(qrCode.mimetype, qrCode.image), msg, customer.selectedTanProcedure!!))
|
|
enterTanResult.enteredTan?.let { enteredTan ->
|
|
retData.replace(0, retData.length, enteredTan)
|
|
}
|
|
}
|
|
|
|
// photoTan
|
|
HBCICallback.NEED_PT_PHOTOTAN -> { // use class MatrixCode to display photo
|
|
val matrixCode = MatrixCode(retData.toString())
|
|
val enterTanResult = callback.enterTan(customer, ImageTanChallenge(TanImage(matrixCode.mimetype, matrixCode.image), msg, customer.selectedTanProcedure!!))
|
|
enterTanResult.enteredTan?.let { enteredTan ->
|
|
retData.replace(0, retData.length, enteredTan)
|
|
}
|
|
}
|
|
|
|
// smsTan: Select cell phone to which SMS should be send
|
|
HBCICallback.NEED_PT_TANMEDIA -> {
|
|
log.info("TODO: select cell phone: $msg ($retData)")
|
|
}
|
|
|
|
// wrong pin entered -> inform user
|
|
HBCICallback.WRONG_PIN -> {
|
|
log.info("TODO: user entered wrong pin: $msg ($retData)")
|
|
}
|
|
|
|
// UserId changed -> inform user
|
|
HBCICallback.USERID_CHANGED -> { // im Parameter retData stehen die neuen Daten im Format UserID|CustomerID drin
|
|
log.info("TODO: UserId changed: $msg ($retData)")
|
|
}
|
|
|
|
// user entered wrong Banleitzahl or Kontonummer -> inform user
|
|
HBCICallback.HAVE_CRC_ERROR -> { // retData contains wrong values in form "BLZ|KONTONUMMER". Set correct ones in the same form in retData
|
|
log.info("TODO: wrong Banleitzahl or Kontonummer entered: $msg ($retData)")
|
|
}
|
|
|
|
// user entered wrong IBAN -> inform user
|
|
HBCICallback.HAVE_IBAN_ERROR -> { // retData contains wrong IBAN. Set correct IBAN in retData
|
|
log.info("TODO: wrong IBAN entered: $msg ($retData)")
|
|
}
|
|
|
|
// message from bank to user. should get displayed to user
|
|
HBCICallback.HAVE_INST_MSG -> {
|
|
// TODO: inform user
|
|
log.error("TODO: inform user, received a message from bank: $msg\n$retData")
|
|
}
|
|
|
|
// Manche Fehlermeldungen werden hier ausgegeben
|
|
HBCICallback.HAVE_ERROR -> { // to ignore error set an empty String in retData
|
|
// TODO: inform user
|
|
log.error("TODO: inform user, error occurred: $msg\n$retData")
|
|
}
|
|
|
|
else -> { // Wir brauchen nicht alle der Callbacks
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.kapott.hbci.callback.HBCICallback.status
|
|
*/
|
|
override fun status(passport: HBCIPassport, statusTag: Int, o: Array<Any>?) {
|
|
// So aehnlich wie log(String,int,Date,StackTraceElement) jedoch fuer Status-Meldungen.
|
|
val param = if (o == null) "" else o.joinToString()
|
|
|
|
when (statusTag) {
|
|
HBCICallback.STATUS_MSG_RAW_SEND -> log.debug("Sending message:\n$param")
|
|
HBCICallback.STATUS_MSG_RAW_RECV -> log.debug("Received message:\n$param")
|
|
// else -> log.debug("New status [$statusTag]: $param")
|
|
}
|
|
}
|
|
|
|
|
|
open fun getTanFromUser(customer: Customer, messageToShowToUser: String, challengeHHD_UC: String): String? {
|
|
// Wenn per "retData" Daten uebergeben wurden, dann enthalten diese
|
|
// den fuer chipTAN optisch zu verwendenden Flickercode.
|
|
// Falls nicht, ist es eine TAN-Abfrage, fuer die keine weiteren
|
|
// Parameter benoetigt werden (z.B. smsTAN, iTAN oder aehnliches)
|
|
|
|
// Die Variable "msg" aus der Methoden-Signatur enthaelt uebrigens
|
|
// den bankspezifischen Text mit den Instruktionen fuer den User.
|
|
// Der Text aus "msg" sollte daher im Dialog dem User angezeigt
|
|
// werden.
|
|
|
|
val enterTanResult = if (challengeHHD_UC.isNullOrEmpty()) {
|
|
callback.enterTan(customer, TanChallenge(messageToShowToUser, customer.selectedTanProcedure!!))
|
|
}
|
|
else {
|
|
// for Sparkasse messageToShowToUser started with "chipTAN optisch\nTAN-Nummer\n\n"
|
|
val usefulMessage = messageToShowToUser.split("\n").last().trim()
|
|
|
|
// val parsedDataSet = FlickerCode(challengeHHD_UC).render()
|
|
callback.enterTan(customer, FlickerCodeTanChallenge(net.dankito.banking.ui.model.tan.FlickerCode("", challengeHHD_UC), usefulMessage, customer.selectedTanProcedure!!))
|
|
}
|
|
|
|
return enterTanResult.enteredTan
|
|
}
|
|
|
|
|
|
|
|
open fun selectTanProcedure(supportedTanProceduresString: String): net.dankito.banking.ui.model.tan.TanProcedure? {
|
|
val supportedTanProcedures = mapper.mapTanProcedures(supportedTanProceduresString)
|
|
|
|
customer.supportedTanProcedures = supportedTanProcedures
|
|
|
|
if (supportedTanProcedures.isNotEmpty()) {
|
|
// select any procedure, user then can select her preferred one in EnterTanDialog; try not to select 'chipTAN manuell'
|
|
return supportedTanProcedures.firstOrNull { it.displayName.contains("manuell", true) == false }
|
|
?: supportedTanProcedures.firstOrNull()
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
} |