Added or extended value objects for Bank, Customer and Product; fixed tests

This commit is contained in:
dankl 2019-10-05 02:27:24 +02:00 committed by dankito
parent 9ac4af58ff
commit b4ba9a0bcf
26 changed files with 269 additions and 275 deletions

View File

@ -1,12 +1,9 @@
package net.dankito.fints package net.dankito.fints
import net.dankito.fints.messages.MessageBuilder import net.dankito.fints.messages.MessageBuilder
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.model.BankData
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID import net.dankito.fints.model.CustomerData
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.fints.model.ProductData
import net.dankito.fints.model.AccountCredentials
import net.dankito.fints.model.BankInfo
import net.dankito.fints.model.ProductInfo
import net.dankito.fints.util.IBase64Service import net.dankito.fints.util.IBase64Service
import net.dankito.utils.web.client.IWebClient import net.dankito.utils.web.client.IWebClient
import net.dankito.utils.web.client.OkHttpWebClient import net.dankito.utils.web.client.OkHttpWebClient
@ -21,31 +18,28 @@ open class FinTsClient(
) { ) {
fun getAnonymousBankInfo(bankInfo: BankInfo, productInfo: ProductInfo) { fun getAnonymousBankInfo(bank: BankData, product: ProductData) {
val requestBody = messageBuilder.createAnonymousDialogInitMessage(bankInfo.countryCode, bankInfo.bankCode, val requestBody = messageBuilder.createAnonymousDialogInitMessage(bank, product)
productInfo.productName, productInfo.productVersion)
val response = getResponseForMessage(requestBody, bankInfo) val response = getResponseForMessage(requestBody, bank)
handleResponse(response) handleResponse(response)
} }
fun getBankInfo(credentials: AccountCredentials, bankInfo: BankInfo, productInfo: ProductInfo) { fun getBankInfo(bank: BankData, customer: CustomerData, product: ProductData) {
val requestBody = messageBuilder.createDialogInitMessage(bankInfo.countryCode, bankInfo.bankCode, val requestBody = messageBuilder.createDialogInitMessage(bank, customer, product)
credentials.customerId, KundensystemID.PinTan, KundensystemStatusWerte.Benoetigt, 0, 0, Dialogsprache.German,
productInfo.productName, productInfo.productVersion)
val response = getResponseForMessage(requestBody, bankInfo) val response = getResponseForMessage(requestBody, bank)
handleResponse(response) handleResponse(response)
} }
protected open fun getResponseForMessage(requestBody: String, bankInfo: BankInfo): WebClientResponse { protected open fun getResponseForMessage(requestBody: String, bank: BankData): WebClientResponse {
val encodedRequestBody = base64Service.encode(requestBody) val encodedRequestBody = base64Service.encode(requestBody)
return webClient.post( return webClient.post(
RequestParameters(bankInfo.finTsServerAddress, encodedRequestBody, "application/octet-stream") RequestParameters(bank.finTs3ServerAddress, encodedRequestBody, "application/octet-stream")
) )
} }

View File

@ -1,13 +1,15 @@
package net.dankito.fints.messages package net.dankito.fints.messages
import net.dankito.fints.messages.datenelemente.implementierte.* import net.dankito.fints.messages.datenelemente.implementierte.Nachrichtennummer
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanProcess import net.dankito.fints.messages.datenelemente.implementierte.tan.TanProcess
import net.dankito.fints.messages.nachrichten.Nachricht import net.dankito.fints.messages.nachrichten.Nachricht
import net.dankito.fints.messages.segmente.ISegmentNumberGenerator import net.dankito.fints.messages.segmente.ISegmentNumberGenerator
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.messages.segmente.SegmentNumberGenerator import net.dankito.fints.messages.segmente.SegmentNumberGenerator
import net.dankito.fints.messages.segmente.implementierte.* import net.dankito.fints.messages.segmente.implementierte.*
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.ProductData
import net.dankito.fints.util.FinTsUtils import net.dankito.fints.util.FinTsUtils
@ -33,56 +35,43 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
* *
* Bei anonymen Dialogen werden Nachrichten weder signiert, noch können sie verschlüsselt und komprimiert werden. * Bei anonymen Dialogen werden Nachrichten weder signiert, noch können sie verschlüsselt und komprimiert werden.
*/ */
open fun createAnonymousDialogInitMessage( open fun createAnonymousDialogInitMessage(bank: BankData, product: ProductData): String {
bankCountryCode: Int,
bankCode: String,
productName: String,
productVersion: String
): String {
val customerId = KundenID.Anonymous /**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
return createMessage(false, false, bankCountryCode, bankCode, customerId, listOf( val customer = CustomerData.Anonymous
IdentifikationsSegment(generator.resetSegmentNumber(1), bankCountryCode, bankCode, customerId, KundensystemID.Anonymous, KundensystemStatusWerte.NichtBenoetigt),
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), BPDVersion.VersionNotReceivedYet, UPDVersion.VersionNotReceivedYet, Dialogsprache.Default, productName, productVersion) return createMessage(false, false, bank, customer, listOf(
IdentifikationsSegment(generator.resetSegmentNumber(1), bank, customer),
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product)
)) ))
} }
open fun createDialogInitMessage( open fun createDialogInitMessage(bank: BankData, customer: CustomerData, product: ProductData): String {
bankCountryCode: Int,
bankCode: String,
customerId: String,
customerSystemId: String,
status: KundensystemStatusWerte,
bpdVersion: Int,
updVersion: Int,
language: Dialogsprache,
productName: String,
productVersion: String
): String {
return createMessage(true, true, bankCountryCode, bankCode, customerId, listOf( return createMessage(true, true, bank, customer, listOf(
IdentifikationsSegment(generator.resetSegmentNumber(2), bankCountryCode, bankCode, customerId, customerSystemId, status), IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer),
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bpdVersion, updVersion, language, productName, productVersion), Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, "HKIDN") ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, "HKIDN")
)) ))
} }
open fun createMessage(signMessage: Boolean, encryptMessage: Boolean, bankCountryCode: Int, bankCode: String, customerId: String, open fun createMessage(signMessage: Boolean, encryptMessage: Boolean, bank: BankData, customer: CustomerData,
payloadSegments: List<Segment>): String { payloadSegments: List<Segment>): String {
var payload = payloadSegments var payload = payloadSegments
val partyIdentification = "0"
val date = utils.formatDateTodayAsInt() val date = utils.formatDateTodayAsInt()
val time = utils.formatTimeNowAsInt() val time = utils.formatTimeNowAsInt()
if (signMessage) { if (signMessage) {
payload = signPayload(2, partyIdentification, date, time, bankCountryCode, bankCode, customerId, payload) payload = signPayload(2, bank, customer, date, time, payload)
} }
if (encryptMessage) { if (encryptMessage) {
payload = encryptPayload(partyIdentification, date, time, bankCountryCode, bankCode, customerId, payload) payload = encryptPayload(bank, customer, date, time, payload)
} }
val formattedPayload = formatPayload(payload) val formattedPayload = formatPayload(payload)
@ -99,37 +88,33 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
} }
protected open fun signPayload(headerSegmentNumber: Int, partyIdentification: String, date: Int, time: Int, protected open fun signPayload(headerSegmentNumber: Int, bank: BankData, customer: CustomerData, date: Int, time: Int,
bankCountryCode: Int, bankCode: String, customerId: String,
payloadSegments: List<Segment>): List<Segment> { payloadSegments: List<Segment>): List<Segment> {
val controlReference = "1" // TODO val controlReference = "1" // TODO
val signatureHeader = PinTanSignaturkopf( val signatureHeader = PinTanSignaturkopf(
headerSegmentNumber, headerSegmentNumber,
Sicherheitsfunktion.PIN_TAN_911, // TODO bank,
customer,
controlReference, controlReference,
"0", date,
utils.formatDateTodayAsInt(), time
utils.formatTimeNowAsInt(),
bankCountryCode,
bankCode,
customerId
) )
val signatureClosing = Signaturabschluss( val signatureClosing = Signaturabschluss(
generator.getNextSegmentNumber(), generator.getNextSegmentNumber(),
controlReference, controlReference,
"12345" // TODO customer.pin
) )
return listOf(signatureHeader, *payloadSegments.toTypedArray(), signatureClosing) return listOf(signatureHeader, *payloadSegments.toTypedArray(), signatureClosing)
} }
private fun encryptPayload(partyIdentification: String, date: Int, time: Int, private fun encryptPayload(bank: BankData, customer: CustomerData, date: Int, time: Int,
bankCountryCode: Int, bankCode: String, customerId: String, payload: List<Segment>): List<Segment> { payload: List<Segment>): List<Segment> {
val encryptionHeader = PinTanVerschluesselungskopf(partyIdentification, date, time, bankCountryCode, bankCode, customerId) val encryptionHeader = PinTanVerschluesselungskopf(bank, customer, date, time)
val encryptedData = VerschluesselteDaten(formatPayload(payload) + Nachricht.SegmentSeparator) val encryptedData = VerschluesselteDaten(formatPayload(payload) + Nachricht.SegmentSeparator)

View File

@ -1,4 +1,4 @@
package net.dankito.fints.messages.datenelemente.implementierte package net.dankito.fints.messages.datenelemente.abgeleiteteformate
import net.dankito.fints.messages.Existenzstatus import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.ZiffernDatenelement import net.dankito.fints.messages.datenelemente.basisformate.ZiffernDatenelement

View File

@ -9,7 +9,7 @@ import net.dankito.fints.messages.datenelemente.Datenelement
* *
* It simply gets, prefixed by '@<payload_length>@', appended to VerschluesselteDaten segment header * It simply gets, prefixed by '@<payload_length>@', appended to VerschluesselteDaten segment header
*/ */
class PinTanVerschluesselteDatenDatenelement(val payload: String) : Datenelement(Existenzstatus.Mandatory) { open class PinTanVerschluesselteDatenDatenelement(val payload: String) : Datenelement(Existenzstatus.Mandatory) {
override fun format(): String { override fun format(): String {
return "@${payload.length}@" + payload return "@${payload.length}@" + payload

View File

@ -11,6 +11,9 @@ import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikatio
open class IdentifizierungDerPartei(identification: String) : Identifikation(identification, Existenzstatus.Optional) { open class IdentifizierungDerPartei(identification: String) : Identifikation(identification, Existenzstatus.Optional) {
companion object { companion object {
/**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
const val SynchronizingCustomerSystemId = "0" const val SynchronizingCustomerSystemId = "0"
} }

View File

@ -1,13 +1,17 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte package net.dankito.fints.messages.datenelementgruppen.implementierte
import net.dankito.fints.messages.Existenzstatus import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
import net.dankito.fints.messages.datenelemente.implementierte.Kreditinstitutscode import net.dankito.fints.messages.datenelemente.implementierte.Kreditinstitutscode
import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
open class Kreditinstitutskennung(bankCountryCode: Int, bankCode: String) open class Kreditinstitutskennung @JvmOverloads constructor(
bankCountryCode: Int,
bankCode: String,
existenzstatus: Existenzstatus = Existenzstatus.Mandatory
)
: Datenelementgruppe(listOf( : Datenelementgruppe(listOf(
Laenderkennzeichen(bankCountryCode, Existenzstatus.Mandatory), Laenderkennzeichen(bankCountryCode, Existenzstatus.Mandatory),
Kreditinstitutscode(bankCode, Existenzstatus.Mandatory) Kreditinstitutscode(bankCode, Existenzstatus.Mandatory)
), Existenzstatus.Mandatory) ), existenzstatus)

View File

@ -1,43 +0,0 @@
package net.dankito.fints.messages.nachrichten.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelemente.implementierte.Nachrichtennummer.Companion.FirstMessageNumber
import net.dankito.fints.messages.nachrichten.Nachricht
import net.dankito.fints.messages.segmente.implementierte.IdentifikationsSegment
import net.dankito.fints.messages.segmente.implementierte.Nachrichtenabschluss
import net.dankito.fints.messages.segmente.implementierte.Nachrichtenkopf
import net.dankito.fints.messages.segmente.implementierte.Verarbeitungsvorbereitung
open class Dialoginitialisierung(
messageSize: Int, // TODO: how to get / calculate size? (give each Segment, Dataelement, ... a size value?)
bankCountryCode: Int,
bankCode: String,
customerId: String,
customerSystemId: String,
bpdVersion: Int,
updVersion: Int,
language: Dialogsprache,
productName: String,
productVersion: String
)
: Nachricht(listOf(
Nachrichtenkopf(1, messageSize, "0", FirstMessageNumber),
IdentifikationsSegment(2, bankCountryCode, bankCode, customerId, customerSystemId, KundensystemStatusWerte.NichtBenoetigt), // TODO: KundensystemStatusWerte
Verarbeitungsvorbereitung(3, bpdVersion, updVersion, language, productName, productVersion),
Nachrichtenabschluss(4, FirstMessageNumber)
)) {
/**
* Zur Einleitung des Prozesses der Gewährleistung einer starken Kun-
denauthentifizierung gemäß [PSD2] muss bei TAN-Verfahren ein HKTAN-
Segment ab Segmentversion #6 eingestellt werden, wenn ein Kreditinstitut
die Verwendung von HKTAN #6 unterstützt (BPD). Wenn HKTAN #6
nicht gesendet wird, kann der Dialog vom Institut mit dem Rückmeldungs-
code 9075 Dialog abgebrochen - Starke Authentifizierung
erforderlich abgewiesen werden.
*/
}

View File

@ -4,24 +4,22 @@ import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.KundenID import net.dankito.fints.messages.datenelemente.implementierte.KundenID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatus import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatus
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
open class IdentifikationsSegment( open class IdentifikationsSegment(
segmentNumber: Int, segmentNumber: Int,
bankCountryCode: Int, bank: BankData,
bankCode: String, customer: CustomerData
customerId: String,
customerSystemId: String,
status: KundensystemStatusWerte
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HKIDN", 2, segmentNumber), Segmentkopf("HKIDN", 2, segmentNumber),
Kreditinstitutskennung(bankCountryCode, bankCode), Kreditinstitutskennung(bank.countryCode, bank.bankCode),
KundenID(customerId), KundenID(customer.customerId),
KundensystemID(customerSystemId), KundensystemID(customer.customerSystemId),
KundensystemStatus(status, Existenzstatus.Mandatory) KundensystemStatus(customer.customerSystemStatus, Existenzstatus.Mandatory)
), Existenzstatus.Mandatory) ), Existenzstatus.Mandatory)

View File

@ -1,36 +1,30 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.signatur.* import net.dankito.fints.messages.datenelemente.implementierte.signatur.OperationsmodusKodiert
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselversion
import net.dankito.fints.messages.datenelemente.implementierte.signatur.SignaturalgorithmusKodiert
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
open class PinTanSignaturkopf( open class PinTanSignaturkopf(
segmentNumber: Int, segmentNumber: Int,
securityFunction: Sicherheitsfunktion, bank: BankData,
customer: CustomerData,
securityControlReference: String, securityControlReference: String,
/**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
partyIdentification: String,
date: Int, date: Int,
time: Int, time: Int
bankCountryCode: Int,
bankCode: String,
userIdentification: String
) : Signaturkopf( ) : Signaturkopf(
segmentNumber, segmentNumber,
Sicherheitsverfahren.PIN_TAN_Verfahren, bank,
VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt, customer,
securityFunction,
securityControlReference, securityControlReference,
partyIdentification,
date, date,
time, time,
SignaturalgorithmusKodiert.FinTsMockValue, SignaturalgorithmusKodiert.FinTsMockValue,
OperationsmodusKodiert.FinTsMockValue, OperationsmodusKodiert.FinTsMockValue,
bankCountryCode,
bankCode,
userIdentification,
Schluesselnummer.FinTsMockValue, Schluesselnummer.FinTsMockValue,
Schluesselversion.FinTsMockValue Schluesselversion.FinTsMockValue
) )

View File

@ -1,27 +1,26 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.encryption.Komprimierungsfunktion import net.dankito.fints.messages.datenelemente.implementierte.encryption.Komprimierungsfunktion
import net.dankito.fints.messages.datenelemente.implementierte.signatur.* import net.dankito.fints.messages.datenelemente.implementierte.signatur.OperationsmodusKodiert
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselart
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselversion
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
open class PinTanVerschluesselungskopf( open class PinTanVerschluesselungskopf(
partyIdentification: String, bank: BankData,
customer: CustomerData,
date: Int, date: Int,
time: Int, time: Int
bankCountryCode: Int,
bankCode: String,
userIdentification: String
) : Verschluesselungskopf( ) : Verschluesselungskopf(
Sicherheitsverfahren.PIN_TAN_Verfahren, bank,
VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt, customer,
partyIdentification,
date, date,
time, time,
OperationsmodusKodiert.FinTsMockValue, OperationsmodusKodiert.FinTsMockValue,
bankCountryCode,
bankCode,
userIdentification,
Schluesselart.Chiffrierschluessel, Schluesselart.Chiffrierschluessel,
Schluesselnummer.FinTsMockValue, Schluesselnummer.FinTsMockValue,
Schluesselversion.FinTsMockValue, Schluesselversion.FinTsMockValue,

View File

@ -5,6 +5,8 @@ import net.dankito.fints.messages.datenelemente.implementierte.signatur.*
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.* import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.*
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
/** /**
@ -22,36 +24,28 @@ import net.dankito.fints.messages.segmente.Segment
*/ */
open class Signaturkopf( open class Signaturkopf(
segmentNumber: Int, segmentNumber: Int,
method: Sicherheitsverfahren, bank: BankData,
version: VersionDesSicherheitsverfahrens, customer: CustomerData,
securityFunction: Sicherheitsfunktion,
securityControlReference: String, securityControlReference: String,
/**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
partyIdentification: String,
date: Int, date: Int,
time: Int, time: Int,
algorithm: Signaturalgorithmus, algorithm: Signaturalgorithmus,
mode: Operationsmodus, mode: Operationsmodus,
bankCountryCode: Int,
bankCode: String,
userIdentification: String,
keyNumber: Int, keyNumber: Int,
keyVersion: Int keyVersion: Int
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HNSHK", 4, segmentNumber), // allowed Segmentkopf("HNSHK", 4, segmentNumber), // allowed
Sicherheitsprofil(method, version), // allowed: method: RAH, PIN; Sicherheitsprofil(customer.securityMethod!!, customer.version!!), // allowed: method: RAH, PIN;
SicherheitsfunktionKodiert(securityFunction), // allowed: 1, 2 SicherheitsfunktionKodiert(customer.selectedTanProcedure?.securityFunction!!), // allowed: 1, 2
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0 Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ? BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
RolleDesSicherheitslieferantenKodiert(), // allowed: 1 RolleDesSicherheitslieferantenKodiert(), // allowed: 1
SicherheitsidentifikationDetails(partyIdentification), SicherheitsidentifikationDetails(customer.partyIdentification),
// "Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet. // "Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet.
Sicherheitsreferenznummer(1), // TODO: is this always 1? Sicherheitsreferenznummer(1), // TODO: is this always 1?
SicherheitsdatumUndUhrzeit(date, time), SicherheitsdatumUndUhrzeit(date, time),
HashalgorithmusDatenelementgruppe(), HashalgorithmusDatenelementgruppe(),
SignaturalgorithmusDatenelementgruppe(algorithm, mode), SignaturalgorithmusDatenelementgruppe(algorithm, mode),
Schluesselname(bankCountryCode, bankCode, userIdentification, Schluesselart.Signierschluessel, keyNumber, keyVersion) Schluesselname(bank.countryCode, bank.bankCode, customer.customerId, Schluesselart.Signierschluessel, keyNumber, keyVersion)
), Existenzstatus.Mandatory) ), Existenzstatus.Mandatory)

View File

@ -4,20 +4,21 @@ import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.* import net.dankito.fints.messages.datenelemente.implementierte.*
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.ProductData
open class Verarbeitungsvorbereitung( open class Verarbeitungsvorbereitung(
segmentNumber: Int, segmentNumber: Int,
bpdVersion: Int, bank: BankData,
updVersion: Int, customer: CustomerData,
language: Dialogsprache, product: ProductData
productName: String,
productVersion: String
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HKVVB", 3, segmentNumber), Segmentkopf("HKVVB", 3, segmentNumber),
BPDVersion(bpdVersion, Existenzstatus.Mandatory), BPDVersion(bank.bpdVersion, Existenzstatus.Mandatory),
UPDVersion(updVersion, Existenzstatus.Mandatory), UPDVersion(customer.updVersion, Existenzstatus.Mandatory),
DialogspracheDatenelement(language, Existenzstatus.Mandatory), DialogspracheDatenelement(customer.selectedLanguage, Existenzstatus.Mandatory),
Produktbezeichnung(productName, Existenzstatus.Mandatory), Produktbezeichnung(product.name, Existenzstatus.Mandatory),
Produktversion(productVersion, Existenzstatus.Mandatory) Produktversion(product.version, Existenzstatus.Mandatory)
), Existenzstatus.Mandatory) ), Existenzstatus.Mandatory)

View File

@ -12,6 +12,8 @@ import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Si
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.SicherheitsidentifikationDetails import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.SicherheitsidentifikationDetails
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Sicherheitsprofil import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Sicherheitsprofil
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
/** /**
@ -31,15 +33,11 @@ import net.dankito.fints.messages.segmente.Segment
* Dieses Feld darf nicht belegt werden. * Dieses Feld darf nicht belegt werden.
*/ */
open class Verschluesselungskopf( open class Verschluesselungskopf(
method: Sicherheitsverfahren, bank: BankData,
version: VersionDesSicherheitsverfahrens, customer: CustomerData,
partyIdentification: String,
date: Int, date: Int,
time: Int, time: Int,
mode: Operationsmodus, mode: Operationsmodus,
bankCountryCode: Int,
bankCode: String,
userIdentification: String,
key: Schluesselart, key: Schluesselart,
keyNumber: Int, keyNumber: Int,
keyVersion: Int, keyVersion: Int,
@ -47,13 +45,13 @@ open class Verschluesselungskopf(
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HNVSK", 3, 998), Segmentkopf("HNVSK", 3, 998),
Sicherheitsprofil(method, version), Sicherheitsprofil(customer.securityMethod!!, customer.version!!),
SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext), // allowed: 4 SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext), // allowed: 4
RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4 RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4
SicherheitsidentifikationDetails(partyIdentification), SicherheitsidentifikationDetails(customer.partyIdentification),
SicherheitsdatumUndUhrzeit(date, time), SicherheitsdatumUndUhrzeit(date, time),
VerschluesselungsalgorithmusDatenelementgruppe(mode), VerschluesselungsalgorithmusDatenelementgruppe(mode),
Schluesselname(bankCountryCode, bankCode, userIdentification, key, keyNumber, keyVersion), Schluesselname(bank.countryCode, bank.bankCode, customer.customerId, key, keyNumber, keyVersion),
KomprimierungsfunktionDatenelement(algorithm), KomprimierungsfunktionDatenelement(algorithm),
// Certificate not applicapable for PIN/TAN; it should be also fine to write nothing at all and therefore leave NotAllowedDatenelement away // Certificate not applicapable for PIN/TAN; it should be also fine to write nothing at all and therefore leave NotAllowedDatenelement away
NotAllowedDatenelement() // Zertifikat is actually a Datenelementgruppe, not a Datenelement NotAllowedDatenelement() // Zertifikat is actually a Datenelementgruppe, not a Datenelement

View File

@ -1,9 +0,0 @@
package net.dankito.fints.model
open class AccountCredentials @JvmOverloads constructor(
val bankCode: String,
val customerId: String,
val pin: String,
val userId: String = customerId
)

View File

@ -0,0 +1,19 @@
package net.dankito.fints.model
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
open class BankData(
val bankCode: String,
val countryCode: Int,
var finTs3ServerAddress: String,
var bpdVersion: Int = 0,
var supportedLanguages: List<Dialogsprache> = listOf()
) {
override fun toString(): String {
return bankCode
}
}

View File

@ -1,8 +0,0 @@
package net.dankito.fints.model
open class BankInfo(
val bankCode: String,
val countryCode: Int,
val finTsServerAddress: String
)

View File

@ -0,0 +1,35 @@
package net.dankito.fints.model
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.KundenID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelemente.implementierte.signatur.IdentifizierungDerPartei
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsverfahren
import net.dankito.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
open class CustomerData(
val customerId: String,
var pin: String,
val userId: String = customerId,
var updVersion: Int = 0,
var availableTanProcedures: List<TanProcedure> = listOf(),
var selectedTanProcedure: TanProcedure? = null,
var securityMethod: Sicherheitsverfahren = Sicherheitsverfahren.PIN_TAN_Verfahren,
var version: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt,
var selectedLanguage: Dialogsprache = Dialogsprache.Default,
var customerSystemId: String = KundensystemID.Anonymous,
var customerSystemStatus: KundensystemStatusWerte = KundensystemStatusWerte.Benoetigt,
var partyIdentification: String = IdentifizierungDerPartei.SynchronizingCustomerSystemId
) {
companion object {
val Anonymous = CustomerData(KundenID.Anonymous, "", customerSystemStatus = KundensystemStatusWerte.NichtBenoetigt)
}
override fun toString(): String {
return customerId
}
}

View File

@ -0,0 +1,13 @@
package net.dankito.fints.model
open class ProductData(
val name: String,
val version: String
) {
override fun toString(): String {
return "$name $version"
}
}

View File

@ -1,7 +0,0 @@
package net.dankito.fints.model
open class ProductInfo(
val productName: String,
val productVersion: String
)

View File

@ -0,0 +1,16 @@
package net.dankito.fints.model
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
open class TanProcedure(
val displayName: String,
val securityFunction: Sicherheitsfunktion,
val type: TanProcedureType
) {
override fun toString(): String {
return "$displayName ($type, ${securityFunction.code}"
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.fints.model
enum class TanProcedureType {
EnterTan,
ChipTan,
ChipTanQrCode,
SmsTan,
PushTan
}

View File

@ -0,0 +1,44 @@
package net.dankito.fints
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.model.*
abstract class FinTsTestBase {
companion object {
const val BankCode = "12345678"
val Bank = BankData(BankCode, Laenderkennzeichen.Germany, "")
const val CustomerId = "0987654321"
const val Pin = "12345"
val Language = Dialogsprache.German
val SecurityFunction = Sicherheitsfunktion.PIN_TAN_911
const val ControlReference = "1"
val Customer = CustomerData(CustomerId, Pin, selectedTanProcedure = TanProcedure("chipTAN-optisch", SecurityFunction, TanProcedureType.ChipTan), selectedLanguage = Language)
const val ProductName = "FinTS-TestClient25Stellen"
const val ProductVersion = "1"
val Product = ProductData(ProductName, ProductVersion)
const val Date = 19880327
const val Time = 182752
}
protected open fun normalizeBinaryData(message: String): String {
return message.replace(0.toChar(), ' ')
}
}

View File

@ -1,39 +1,13 @@
package net.dankito.fints.messages package net.dankito.fints.messages
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.FinTsTestBase
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.util.FinTsUtils import net.dankito.fints.util.FinTsUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
class MessageBuilderTest { class MessageBuilderTest : FinTsTestBase() {
companion object {
const val BankCode = "12345678"
const val CustomerId = "0987654321"
const val Pin = "12345"
const val Date = 19880327
const val Time = 182752
val Language = Dialogsprache.German
val SecurityFunction = Sicherheitsfunktion.PIN_TAN_911
const val ControlReference = "1"
const val ProductName = "FinTS-TestClient25Stellen"
const val ProductVersion = "1"
}
private val underTest = MessageBuilder(utils = object : FinTsUtils() { private val underTest = MessageBuilder(utils = object : FinTsUtils() {
override fun formatDate(date: Date): String { override fun formatDate(date: Date): String {
@ -50,8 +24,7 @@ class MessageBuilderTest {
fun createAnonymousDialogInitMessage() { fun createAnonymousDialogInitMessage() {
// given // given
val underTest = underTest.createAnonymousDialogInitMessage( val underTest = underTest.createAnonymousDialogInitMessage(Bank, Product)
Laenderkennzeichen.Germany, BankCode, ProductName, ProductVersion)
// when // when
val result = underTest.format() val result = underTest.format()
@ -69,9 +42,7 @@ class MessageBuilderTest {
fun createDialogInitMessage() { fun createDialogInitMessage() {
// given // given
val underTest = underTest.createDialogInitMessage(Laenderkennzeichen.Germany, BankCode, CustomerId, val underTest = underTest.createDialogInitMessage(Bank, Customer, Product)
KundensystemID.PinTan, KundensystemStatusWerte.Benoetigt, 0, 0, Language,
ProductName, ProductVersion)
// when // when
val result = underTest.format() val result = underTest.format()
@ -89,8 +60,4 @@ class MessageBuilderTest {
)) ))
} }
protected open fun normalizeBinaryData(message: String): String {
return message.replace(0.toChar(), ' ')
}
} }

View File

@ -1,26 +1,23 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.KundenID import net.dankito.fints.FinTsTestBase
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
class IdentifikationsSegmentTest { class IdentifikationsSegmentTest : FinTsTestBase() {
@Test @Test
fun format() { fun format() {
// given // given
val underTest = IdentifikationsSegment(2, Laenderkennzeichen.Germany, "12345678", KundenID.Anonymous, KundensystemID.Anonymous, KundensystemStatusWerte.NichtBenoetigt) val underTest = IdentifikationsSegment(2, Bank, Customer)
// when // when
val result = underTest.format() val result = underTest.format()
// then // then
assertThat(result).isEqualTo("HKIDN:2:2+280:12345678+9999999999+0+0") assertThat(result).isEqualTo("HKIDN:2:2+280:12345678+0987654321+0+1")
} }
} }

View File

@ -1,36 +1,26 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen import net.dankito.fints.FinTsTestBase
import net.dankito.fints.messages.datenelemente.implementierte.signatur.IdentifizierungDerPartei
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
class SignaturkopfTest { class SignaturkopfTest : FinTsTestBase() {
@Test @Test
fun format() { fun format() {
// given // given
val securityFunction = Sicherheitsfunktion.PIN_TAN_911
val controlReference = "1902675680" val controlReference = "1902675680"
val partyIdentification = IdentifizierungDerPartei.SynchronizingCustomerSystemId
val date = 20191002
val time = 212757
val bankCode = "12345678"
val customerId = "0987654321"
val keyNumber = 0
val keyVersion = 0
val underTest = PinTanSignaturkopf(2, securityFunction, controlReference, partyIdentification, val underTest = PinTanSignaturkopf(2, Bank, Customer,
date, time, Laenderkennzeichen.Germany, bankCode, customerId) controlReference, Date, Time)
// when // when
val result = underTest.format() val result = underTest.format()
// then // then
assertThat(result).isEqualTo("HNSHK:2:4+PIN:2+${securityFunction.code}+$controlReference+1+1+1::0+1+1:$date:$time+1:999:1+6:10:16+280:$bankCode:$customerId:S:$keyNumber:$keyVersion") assertThat(result).isEqualTo("HNSHK:2:4+PIN:2+${SecurityFunction.code}+$controlReference+1+1+1::0+1+1:$Date:$Time+1:999:1+6:10:16+280:$BankCode:$CustomerId:S:0:0")
} }
} }

View File

@ -1,30 +1,24 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen import net.dankito.fints.FinTsTestBase
import net.dankito.fints.messages.datenelemente.implementierte.signatur.IdentifizierungDerPartei
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
class VerschluesselungskopfTest {
class VerschluesselungskopfTest : FinTsTestBase() {
@Test @Test
fun format() { fun format() {
// given // given
val partyIdentification = IdentifizierungDerPartei.SynchronizingCustomerSystemId
val date = 20191002
val time = 212757
val bankCode = "12345678"
val customerId = "0987654321"
val underTest = PinTanVerschluesselungskopf(partyIdentification, date, time, val underTest = PinTanVerschluesselungskopf(Bank, Customer, Date, Time)
Laenderkennzeichen.Germany, bankCode, customerId)
// when // when
val result = underTest.format() val result = underTest.format()
// then // then
assertThat(result).isEqualTo("HNVSK:998:3+PIN:2+998+1+1::0+1:$date:$time+2:2:13:@8@ :5:1+280:$bankCode:$customerId:V:0:0+0") assertThat(normalizeBinaryData(result)).isEqualTo("HNVSK:998:3+PIN:2+998+1+1::0+1:$Date:$Time+2:16:14:@8@ :5:1+280:$BankCode:$CustomerId:V:0:0+0+")
} }
} }