Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion lib/Classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ var Nachricht = function (proto_version) {
seg.nr = me_msg.segments_ctr + 1
me_msg.segments[me_msg.segments_ctr] = seg
me_msg.segments_ctr++

return seg.nr
}

Expand Down Expand Up @@ -763,7 +764,7 @@ util.inherits(Exceptions.MalformedMessageFormat, Exceptions.OpenFinTSClientExcep
Exceptions.OrderFailedException = function (msg) {
Exceptions.OpenFinTSClientException.call(this)
this.msg_detail = msg
this.message = 'Failed to perform Order, got error Message from Server.:' + msg.getEl(3)
this.message = 'Failed to perform Order, got error Message from Server.:' + msg.getEl ? msg.getEl(3) : msg[2]
}
util.inherits(Exceptions.OrderFailedException, Exceptions.OpenFinTSClientException)

Expand Down Expand Up @@ -948,6 +949,25 @@ var Order = function (client) {
}
// 2. Add Segment
msg.addSeg(int_send_msg[j].segment)

// Kontoumsätze abrufen requires HKTAN
if (int_send_msg[j].segment.name === 'HKKAZ') {
let HKTAN_vers;
var tan_verfahren = me_order.client.bpd.tan.tan_verfahren[me_order.client.upd.availible_tan_verfahren[0]];
try {
HKTAN_vers = tan_verfahren.HITAN_vers;
client.log.con.info({
gv: 'HKKAZ'
}, `TAN-Verfahren: '${tan_verfahren.desc}', v${HKTAN_vers}`);
} catch (e) {
client.log.con.error(e, {
gv: 'HKKAZ'
}, `Error reading TAN-Verfahren data. Using HKTAN v6.`);
}
msg.addSeg(
Helper.newSegFromArray('HKTAN', HKTAN_vers ?? 6, ['4', 'HKIDN', '', '', '', '', 'N', '', '', '', ''])
);
}
}
}
// Send Segments to Destination
Expand Down
187 changes: 148 additions & 39 deletions lib/FinTSClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ var encoding = require('encoding')
closeSecure () - Stellt sicher, dass keine Sensiblen Informationen wie die PIN noch im RAM sind, sollte am Ende immer gerufen werden

*/
var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) {
var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger, in_client_name, in_client_version, in_available_tan_verfahren) {
var me = this
me.Exceptions = Exceptions
// Logger
Expand Down Expand Up @@ -276,8 +276,8 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
// Technical
me.dialog_id = 0
me.next_msg_nr = 1
me.client_name = 'Open-FinTS-JS-Client'
me.client_version = 4
me.client_name = in_client_name || 'Open-FinTS-JS-Client'
me.client_version = in_client_version || 4
me.proto_version = 300 // 300=FinTS;220=HBCI 2.2
me.in_connection = false

Expand Down Expand Up @@ -348,7 +348,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
me.upd = {
'vers_upd': '0',
'geschaefts_vorg_gesp': true,
'availible_tan_verfahren': ['999'],
'availible_tan_verfahren': in_available_tan_verfahren || ['999'],
'clone': function () {
return JSON.parse(JSON.stringify(this))
}
Expand Down Expand Up @@ -397,6 +397,16 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
// BPD Vers = 0; UPD Vers = 0; Dialogspr. = 0
var HKVVB = Helper.newSegFromArray('HKVVB', 3, [me.bpd.vers_bpd, me.upd.vers_upd, 0, me.client_name, me.client_version])
msg.addSeg(HKVVB)

// in step 1 me.upd.availible_tan_verfahren[0] is always '999' and we use HKTAN_vers 6
// in step 2 we use the tan_verfahren requested by the bank server and send the corresponding HITANS version
const tan_verfahren = me.bpd.tan.tan_verfahren[me.upd.availible_tan_verfahren[0]];
const HKTAN_vers = tan_verfahren.HITAN_vers || 6;

msg.addSeg(
Helper.newSegFromArray('HKTAN', HKTAN_vers, ['4', 'HKIDN', '', '', '', '', 'N', '', '', '', ''])
);

if (me.kunden_id != 9999999999 && me.sys_id == 0) var syn = msg.addSeg(Helper.newSegFromArray('HKSYN', me.proto_version == 220 ? 2 : 3, [0])) // Synchronisierung starten
me.log.gv.debug({
gv: 'HKVVB'
Expand Down Expand Up @@ -534,46 +544,121 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
}
try {
// 5.4 Tan
var HITANS = recvMsg.selectSegByName('HITANS')[0]
if (HITANS.vers == 5) {
var HITANSs = recvMsg.selectSegByName('HITANS');

me.bpd.tan.tan_verfahren = {};
for (var HITANS of HITANSs) {
me.log.gv.info({
gv: 'HITANS'
}, 'Analyse HITANS v' + HITANS.vers);

var tan_data = HITANS.getEl(4)
me.bpd.tan.one_step_availible = tan_data.getEl(1).toUpperCase() == 'J'
me.bpd.tan.multiple_tan = tan_data.getEl(2).toUpperCase() == 'J'
me.bpd.tan.hash_type = tan_data.getEl(3)
me.bpd.tan.tan_verfahren = {}
for (var i = 3; i < tan_data.data.length; i++) {
var sicherheitsfunktion = {}
sicherheitsfunktion.code = tan_data.data[i]
sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1] // "1": Einschrittverfahren, "2": Zweischritt
sicherheitsfunktion.tech_id = tan_data.data[i + 2]
sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3]
sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4]
sicherheitsfunktion.desc = tan_data.data[i + 5]
sicherheitsfunktion.max_len_tan = tan_data.data[i + 6]
sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] == '2'
sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8]
sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9]
sicherheitsfunktion.anz_tanlist = tan_data.data[i + 10]
sicherheitsfunktion.multi_tan = tan_data.data[i + 11].toUpperCase() == 'J'
sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 12]
sicherheitsfunktion.tan_list_nr_req = tan_data.data[i + 13]
sicherheitsfunktion.auftragsstorno = tan_data.data[i + 14].toUpperCase() == 'J'
sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 15]
sicherheitsfunktion.auftrag_konto = tan_data.data[i + 16]
sicherheitsfunktion.challange_class_req = tan_data.data[i + 17].toUpperCase() == 'J'
sicherheitsfunktion.challange_structured = tan_data.data[i + 18].toUpperCase() == 'J'
sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 19]
sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 20]
sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 21]
// sicherheitsfunktion.challange_value_req = tan_data.data[i+14].toUpperCase()=="J";
me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion
i += 21

if (HITANS.vers == 5) {
for (var i = 3; i < tan_data.data.length; i+= 22) {
var sicherheitsfunktion = {}
sicherheitsfunktion.code = tan_data.data[i]
sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1] // "1": Einschrittverfahren, "2": Zweischritt
sicherheitsfunktion.tech_id = tan_data.data[i + 2]
sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3]
sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4]
sicherheitsfunktion.desc = tan_data.data[i + 5]
sicherheitsfunktion.max_len_tan = tan_data.data[i + 6]
sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] == '2'
sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8]
sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9]
sicherheitsfunktion.anz_tanlist = tan_data.data[i + 10]
sicherheitsfunktion.multi_tan = tan_data.data[i + 11]?.toUpperCase() == 'J'
sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 12]
sicherheitsfunktion.tan_list_nr_req = tan_data.data[i + 13]
sicherheitsfunktion.auftragsstorno = tan_data.data[i + 14]?.toUpperCase() == 'J'
sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 15]
sicherheitsfunktion.auftrag_konto = tan_data.data[i + 16]
sicherheitsfunktion.challange_class_req = tan_data.data[i + 17]?.toUpperCase() == 'J'
sicherheitsfunktion.challange_structured = tan_data.data[i + 18]?.toUpperCase() == 'J'
sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 19]
sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 20]
sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 21]
// sicherheitsfunktion.challange_value_req = tan_data.data[i+14].toUpperCase()=="J";

sicherheitsfunktion.HITAN_vers = HITANS.vers;
me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion
}
} else if (HITANS.vers == 6) {
for (var i = 3; i < tan_data.data.length; i+=21) {
var sicherheitsfunktion = {};
sicherheitsfunktion.code = tan_data.data[i];
sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1]; // "1": Einschrittverfahren, "2": Zweischritt
sicherheitsfunktion.tech_id = tan_data.data[i + 2];
sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3];
sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4];
sicherheitsfunktion.desc = tan_data.data[i + 5];
sicherheitsfunktion.max_len_tan = tan_data.data[i + 6];
sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] === '2';
sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8];
sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9];
sicherheitsfunktion.multi_tan = tan_data.data[i + 10]?.toUpperCase() === 'J';
sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 11];
sicherheitsfunktion.auftragsstorno = tan_data.data[i + 12]?.toUpperCase() === 'J';
sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 13];
sicherheitsfunktion.auftrag_konto = tan_data.data[i + 14];
sicherheitsfunktion.challange_class_req = tan_data.data[i + 15]?.toUpperCase() === 'J';
sicherheitsfunktion.challange_structured = tan_data.data[i + 16]?.toUpperCase() === 'J';
sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 17];
sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 18];
sicherheitsfunktion.antwort_HHD_UC_req = tan_data.data[i + 19]?.toUpperCase() === 'J';
sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 20];

sicherheitsfunktion.HITAN_vers = HITANS.vers;
me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion
}
} else if (HITANS.vers == 7) {
for (var i = 3; i < tan_data.data.length; i+=26) {
var sicherheitsfunktion = {};
sicherheitsfunktion.code = tan_data.data[i];
sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1]; // "1": Einschrittverfahren, "2": Zweischritt
sicherheitsfunktion.tech_id = tan_data.data[i + 2];
sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3];
sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4];
sicherheitsfunktion.desc = tan_data.data[i + 5];
sicherheitsfunktion.max_len_tan = tan_data.data[i + 6];
sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] === '2';
sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8];
sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9];
sicherheitsfunktion.multi_tan = tan_data.data[i + 10]?.toUpperCase() === 'J';
sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 11];
sicherheitsfunktion.auftragsstorno = tan_data.data[i + 12]?.toUpperCase() === 'J';
sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 13];
sicherheitsfunktion.auftrag_konto = tan_data.data[i + 14];
sicherheitsfunktion.challange_class_req = tan_data.data[i + 15]?.toUpperCase() === 'J';
sicherheitsfunktion.challange_structured = tan_data.data[i + 16]?.toUpperCase() === 'J';
sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 17];
sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 18];
sicherheitsfunktion.antwort_HHD_UC_req = tan_data.data[i + 19]?.toUpperCase() === 'J';
sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 20];
sicherheitsfunktion.max_anz_statusabfr = tan_data.data[i + 21];
sicherheitsfunktion.wait_first_statusabfr = tan_data.data[i + 22];
sicherheitsfunktion.wait_next_statusabfr = tan_data.data[i + 23];
sicherheitsfunktion.manual_confirm = tan_data.data[i + 24];
sicherheitsfunktion.auto_statusabfr_erlaubt = tan_data.data[i + 25]?.toUpperCase() === 'J';

sicherheitsfunktion.HITAN_vers = HITANS.vers;
me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion
}
}
}
for (const [code, sicherheitsfunktion] of Object.entries(me.bpd.tan.tan_verfahren)){
me.log.gv.info({
gv: 'HITANS'
}, `HITANS entry ${code}: '${sicherheitsfunktion.desc}', v${sicherheitsfunktion.HITAN_vers}`);
}
} catch (ee) {
me.log.gv.error(ee, {
gv: 'HITANS'
}, 'Error while analyse HITANS')
}, `Error while analyse HITANS: ${ee.toString()}`);
}
// 6. Analysiere UPD
try {
Expand All @@ -583,7 +668,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
} catch (ee) {
me.log.gv.error(ee, {
gv: 'HIUPA'
}, 'Error while analyse UPD')
}, `Error while analyse UPD: ${ee.toString()}`)
}
// 7. Analysiere Verfügbare Tan Verfahren
try {
Expand Down Expand Up @@ -621,7 +706,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
} catch (ee) {
me.log.gv.error(ee, {
gv: 'HKVVB'
}, 'Error while analyse HKVVB result Tan Verfahren')
}, 'Error while analyse HKVVB gv params')
}
try {
cb(error, recvMsg, has_neu_url)
Expand Down Expand Up @@ -682,7 +767,29 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
hirmsg: HIRMG
}, 'Error while analyse HKVVB Response Wrong HIRMG response code')
try {
cb('Fehlerhafter Rückmeldungscode: ' + (HIRMG === null ? 'keiner' : HIRMG.getEl(1).getEl(3)), recvMsg, false)
const segMsgs = recvMsg.selectSegByName("HIRMS");
const segMsgStr = segMsgs.map(rseg => {
const belongs_to = rseg.bez;
const segFor = msg.segments[belongs_to - 1];
const segForStr = !!segFor ? segFor.name : "[Segment " + belongs_to + "]";
const msgStr = rseg.store.data.map(data => {
const msgCode = data.data[0];
const msgText = data.data[2];
return msgCode + ": " + msgText;
}).join(", ");
return "Antworten auf "+segForStr+": ("+msgStr+")";
}).join(", ");
const globMsgStr = !!HIRMG
? HIRMG.store.data.map(data => {
const msgCode = data.data[0];
const msgText = data.data[2];
return msgCode + ": " + msgText;
}).join(", ")
: '(HIRMG nicht gefunden)';
const errMsg = "Antwort des Bankservers: "+ globMsgStr +
(segMsgStr.length > 0 ? ", "+segMsgStr : "");

cb(errMsg, recvMsg, false);
} catch (cb_error) {
me.log.gv.error(cb_error, {
gv: 'HKVVB'
Expand Down Expand Up @@ -1147,6 +1254,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
// Im Step 1 und 2 bleiben keine Verbindungen erhalten
// Diese Verbindung auf jeden Fall beenden
var neu_url = me.bpd.url
var neu_tan_verfahren = me.bpd.tan.tan_verfahren
var neu_sig_method = me.upd.availible_tan_verfahren[0]
me.bpd = original_bpd.clone()
me.upd = original_upd.clone()
Expand All @@ -1166,6 +1274,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
})
me.clear()
me.bpd.url = neu_url
me.bpd.tan.tan_verfahren = neu_tan_verfahren
me.upd.availible_tan_verfahren[0] = neu_sig_method
me.sys_id = orig_sys_id
me.last_signatur_id = orig_last_sig
Expand Down Expand Up @@ -1300,7 +1409,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger)
}
var txt = msg.transformForSend()
me.debugLogMsg(txt, true)
var post_data = new Buffer(txt).toString('base64')
var post_data = encoding.convert(txt, 'ISO-8859-1').toString('base64')
var u = url.parse(me.bpd.url)
var options = {
hostname: u.hostname,
Expand Down
3 changes: 2 additions & 1 deletion lib/MTParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports = function () {
parser.nextPos()
parser.nextPos()
// Für Feld Mehrfach
while (parser.hasNext() && !(parser.getCurrentChar() == ':' || parser.getCurrentChar() == '-' || parser.getCurrentChar() == '@')) {
while (parser.hasNext() && !(parser.getCurrentChar() == ':' || (parser.getCurrentChar() == '-' && parser.data[parser.cur_pos + 1] == '\r' && parser.data[parser.cur_pos + 2] == '\n') || parser.getCurrentChar() == '@')) {
parser.setMarkerWithCurrentPos('start')
parser.gotoNextValidString(['\r\n', '@@'])
val += parser.getTextFromMarkerToCurrentPos('start')
Expand Down Expand Up @@ -203,6 +203,7 @@ module.exports = function () {
if (satz.is_verwendungszweck_object) {
satz.verwendungszweck = {}
satz.verwendungszweck.text = ''
satz.verwendungszweck.name_kontrahent = ''
var p = new Parser(raw_verwen_zweck)
p.gotoNextValidChar('?')
while (p.hasNext()) {
Expand Down