Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

NPM Version

Open-Fin-TS-JS-Client

FinTS/HBCI ist eine standardisierte Schnittstelle zur Kommunikation mit Banken von der Deutschen Kreditwirtschaft (DK). Es existieren derzeit drei Versionen der Schnittstelle.

  • HBCI 2.2 bzw. HBCI+ - Diese API unterstützt diese Version.
  • FinTS 3.0 (noch weitestgehend auf HBCI basierend) - Diese API unterstützt diese Version.
  • FinTS 4.1 (neuste auf XML basierend) - keine Unterstützung (noch keine große Verbreitung)

Der Open-Fin-TS-JS-Client unterstützt zurzeit nur die Version FinTS 3.0 mit der Pin/Tan Sicherheitsvariante.

Es existieren nur wenige Implementierungen für HBCI bzw. FinTS. Zurzeit (2015) nur eine für Javascript, diese ist allerdings kommerziell. Dieses Projekt versucht diese Lücke zu schließen.

Unterstützte Geschäftsvorfälle

  • SEPA Kontoinformationen laden (HKSPA)
  • Kontoumsätze laden (HKKAZ)
  • Salden laden (HKSAL)
  • SEPA Einzelüberweisung (HKCCS) - geplant
  • Kontoumsätze im Format camt anfordern (HKCAZ) - geplant

Unterstützte Plattformen

  • Node-JS
  • weitere Plattformen mit Anpassungen denkbar

JS im Browser ist ohne besondere Umwege(zB Browser Extensions) aufgrund der Architektur von FinTS(Server senden keinen CORS Header) nicht möglich.

License

Das Projekt wurde 2015 von Jens Schyma jeschyma@gmail.com ins Leben gerufen.

Apache2

Quick-Start

Der einfachste Weg ist Open-Fin-TS-JS-Client über NPM durch eine Dependency in der package.json in ein Projekt einzubinden. Am folgenden Beispiel zum Laden von Kontoumsätzen wird gezeigt wie der Client zu bedienen ist.

varFinTSClient=require("open-fin-ts-js-client");// 1. Definition der Bankenliste - Echte URLs sind hier http://www.hbci-zka.de/institute/institut_auswahl.htm erhältlich.varbankenliste={'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}};// 2. FinTSClient anlegen// BLZ: 12345678// Kunden-ID/Benutzerkennung: test1// PIN: 1234// Bankenliste siehe obenvarclient=newFinTSClient(12345678,"test1","1234",bankenliste);// 3. Verbindung aufbauenclient.EstablishConnection(function(error){if(error){console.log("Fehler: "+error);}else{console.log("Erfolgreich Verbunden");// 4. Kontoumsätze für das 1. Konto(client.konten[0]) ladenclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){if(error){console.log("Fehler beim laden der Umsätze: "+error2);}else{// Alles gut// 4. Umsätze darstellenconsole.log(JSON.stringify(data));// 5. Verbindung beendenclient.MsgEndDialog(function(error,recvMsg2){// 6. Secure Daten im Objekt aus dem Ram löschenclient.closeSecure();console.log("ENDE");});}});}});

API Beschreibung

FinTSClient(in_blz,in_kunden_id,in_pin,in_logger)in_blz-DieentsprechendeBLZalsZahloderStringin_kunden_id-DieBenutzerkennungbzw.Kunden-ID-9999999999=AnonymerBenutzerin_pin-DiePinin_logger-EinBunyanLoggerperdefaultwirdnichtsgeloggedAttribute=NotwendigumdieVerbindungherzustellen=blzctry-Zurzeitimmer280fürDeutschlandkunden_id-pin-tan-NochNULL,dakeineGeschäftsvorfällemitTanzurzeitunterstütztdebug_mode-DebugModus(Logging)=StatusdesaktuellenClientObjekts=dialog_id-EinFinTSClientObjektrepräsentierteinDialog/diesistdievomKIzugewieseneIDnext_msg_nr-NachrichtenwerdenNummeriertbeginnendvon1diesistdienächsteNummerclient_name-NamedesClients,sollteindividuellbelegtwerdenmitdemNamenderdieAPInutzendenSoftwareclient_version-VersiondesClients=BankParameterDatenundSystem-ID+letztebenutzteSignatur-IDsys_id-vomKIzugewieseneSystem-ID,identifiziertdieseAnwendungfürdenentsprechendenBenutzereindeutig.SollteumdieGenerierungUnmengenneuerIDszuvermeidenfürweitereVerbindungenbeibehaltenwerden(fürimmer).last_signatur_id-ZuletztverwendeteSignatur-IDhängtanderSystem-IDundgewährleistet,dassNachrichtennichtmehrfacheingereichtwerden.bpd-DieBankParameterDatensieheDokumentationzumehrDetails{'vers_bpd':"0",// Version der BPD'bank_name':"",// Name der Bank'supported_vers':["300"],// Unterstützte HBCI/FinTS Versionen'url':"",// URL für Pin/Tan, wird durch die Bankenliste und die BLZ vorbelegt'pin':{'min_length':0,// Minimal Länge der Pin'max_length':100,// Maximal Länge der Pin'max_tan_length':100,// Maximale Länger der Tan'txt_benutzerkennung':'Benutzerkennung',// Vorbelegungs Text für das Feld Benutzerkennung'txt_kunden_id':'Kunden ID',// Vorbelegungs Text für das Feld Kunden-ID'availible_seg':{// Verfügbare Geschäftsvorfälle als Key und Wert für Tanerforderlichkeit'HXXXX':true,// Wert true -> mit Tan'HXXXX':false// Wert false -> ohne Tan}},'tan':{'one_step_availible':true,// Ein-Schritt-Verfahren verfügbar'multiple_tan':false,// Mehrfachtan'hash_type':"0",// zu verwendender Hash Algorithmus'tan_verfahren':{'999':{// Verfügbare Tan Verfahren'code':'999',// Code des Verfahrens'one_two_step_vers':"1",// 1-Ein Schritt-Verfahren / 2-Zwei Schritt-Verfahren'tech_id':'PIN',// Technische ID des Verfahrens'desc':'Einfaches Pin-Verfahren',// Lesbare Beschreibung des Verfahrens'max_len_tan':100,// Maximal Länge der Tan'tan_alphanum':true,// Tan Alphanumerisch?'txt_rueckwert':'Rückgabewert',// Vorbelegungs Text Rückgabewert'max_len_rueckwert':100,// Maximale Länge des Rückgabewerts'anz_tanlist':'2',// Anzahl Tan-Listen'multi_tan':true,// Mehrfachtan?'tan_zeit_diabez':"",// Tan Zeit Dialog Bezug'tan_list_nr_req':"",// Tan Listennummer erforderlich?'auftragsstorno':false,// Auftragsstorno?'challange_class_req':false,// Challange Klasse erforderlich?'challange_value_req':false// Challange Wert erforderlich?}}},'clone':function()// Funktion um die Daten zu Clonen};=UserParameterDaten=upd-DieUserParameterDaten{'vers_upd':"0",// Version der User Parameter Daten'geschaefts_vorg_gesp':true,// Wie sind die nicht aufgeführten Geschäftsvorfälle zu Werten? true = sind gesperrt / false = keine Aussage darüber treffbar'availible_tan_verfahren':["999"],// Verfügbare Tan Verfahren für den Benutzer, [0] ist die aktuell verwendete'clone':function()// Funktion um die Daten zu clonen};konten-ListederKontendesBenutzers[{'iban':"",// IBAN des Kontos'konto_nr': // Konto-Nr'unter_konto': // Unterkonto Merkmal'ctry_code': // Länderkennzeichen idr. 280 für Deutschland'blz': // BLZ'kunden_id': // Kunden ID dem das Konto gehört'kontoar': // Art des Kontos'currency': // Währung des Kontos'kunde1_name': // Name des Kunden'product_name': // Produktbezeichnung'sepa_data':{// Zusätzliche Daten für SEPA Konten, kann null sein, wenn kein SEPA Konto z.B. Depots etc.'is_sepa': true,// Ist SEPA Konto?'iban':"",// IBAN'bic':"",// BIC'konto_nr':"",// Konto_NR'unter_konto':"",// Unter Konto'ctry_code':"280",// Ctry Code'blz':""// BLZ}}]Methoden<--Internal-->clear()-InitialisiertalleAttributegetNewSigId()-ErzeugteineneueSignaturIDreturnssig_id(int)SendMsgToDestination(msg,callback)-VerschicktNachrichtperHTTPSandieBankmsg(Nachricht)callback(function(error,msg))=WirdgerufenwennNachrichterfolgreich(error==null)verschickt+Antwort(msginstanceofNachricht)empfangendebugLogMsg(txt,send)-ZumLoggenvonNachrichten<--Public-->MsgInitDialog(callback)-InitialisierteinenDialogcallback(function(error,recvMsg,has_neu_url))-error==nullKeinFehler-recvMsg(Nachricht)-has_neu_url==truewenneineandereURLzurückgemeldetwurdeMsgEndDialog(callback)-BeendeteinenDialogcallback(function(error,recvMsg))-error==nullkeinFehler-recvMsg(Nachricht)EstablishConnection(callback)-VereinfachteVarianteumeineVerbindungmitderBankaufzubauencallback(function(error))-error==nullkeinFehler!=nullFehler/einMsgEndDialogistnichtmehrerforderlichMsgRequestSepa(for_konto_nr,callback)-LadeSEPAZusatzDaten(vorallemdieBIC)for_konto_nr-Konto-NrfürdasbetreffendeKonto,kannaberauchweggelassenwerden,dannfüralleKontencallback(function(error,recvMsg,sepa_list))-error==nullkeinFehler-recvMsg(Nachricht)-sepa_list[]arrayvonSepaDatenFormatsieheUPDKonten[].sepa_dataMsgGetKontoUmsaetze(konto,from_date,to_date,callback)-LädtdieKontenumsätzefüreinbestimmtesKontokonto-DasKontofürdasdieUmsätzegeladenwerdensollenfrom_date(Date)-vomDatum(könnenleer==nullgelassenwerdendannwirdallesverfügbaregeladen)to_date(Date)-zumDatumcallback(function(error,recvMsg,umsaetze))-error==nullkeinFehler-recvMsg(Nachricht)-umsaetze[]EnthältdieUmsatzDatenmitfolgendemFormat[{// pro Tag ein Objekt siehe MT490 SWIFT Format'refnr':"STARTUMS",// ReferenzNummer'bez_refnr':null,// BezugsreferenzNummer'konto_bez':"12345678/0000000001",// Kontobezeichnung BLZ/Kontonr'auszug_nr':"",// Auszugsnummer'anfangssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'schlusssaldo':{'isZwischensaldo':false,'soll_haben' : 'H','buchungsdatum' : Date,'currency':'EUR','value':150.22},'saetze':[// Die eigentlichen Buchungssätze{'datum':Date,'is_storno':false,'soll_haben':'S','value':150.22,'is_verwendungszweck_object':true,// Verwendungszweck ist Objekt?'verwendungszweck': "TEXT"// oder{'buchungstext':"",'primanoten_nr':"",'text':"",'bic_kontrahent':"",'iban_kontrahent':"",'name_kontrahent':"",'text_key_addion':""}}]}]MsgGetSaldo(konto,cb)-LädtdenSaldoeinesbestimmtenKontoskonto-DasKontofürdasderSaldogeladenwerdensollencallback(function(error,recvMsg,saldo))-error==nullkeinFehler-saldo{"desc":"Normalsparen","cur":"EUR","saldo":{"soll_haben":"H",// SALDO OBJECT"buchungsdatum":Date,"currency":"EUR","value":5},"saldo_vorgemerkt":null,// SALDO OBJECT"credit_line":{"currency":"EUR","value":5},// BETRAG OBJECT"avail_amount":null,// BETRAG OBJECT"used_amount":null,// BETRAG OBJECT"overdraft":null,// BETRAG OBJECT"booking_date":Date,"faelligkeit_date":Date}closeSecure()-Stelltsicher,dasskeineSensiblenInformationenwiediePINnochimRAMsind,sollteamEndeimmergerufenwerden

Besonders zu beachten ist, dass pro FinTS-Dialog, das heißt pro FinTSClient Objekt nur auf ein Callback gleichzeitig gewartet werden kann. Das liegt daran, dass das FinTS Protokoll ein sequenzielles Protokoll ist, welches nur eine Nachricht als Anfrage pro Dialog zur selben Zeit erlaubt. Der Nutzer der Client-Bibliothek hat sicherzustellen, dass nur eine Anfrage zur selben Zeit läuft. Wird dies nicht eingehalten führt die Client Library das Senden der Nachricht nicht aus und schmeißt eine Exception. Ein Beispiel das die falsche und richtige Verwendung der Client-Library zeigt:

// Achtung: Falsche Verwendung führt zu einer Exceptionclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){ ... dosomething...});client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissenclient.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){
... dosomething...});// Hier wird eine Exception geschmissen// Richtige Aufruf Hirarchie von Mehrfachen Aufträgen pro Dialogclient.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[2].sepa_data,null,null,function(error2,rMsg,data){//... do something ... client.MsgGetKontoUmsaetze(client.konten[3].sepa_data,null,null,function(error2,rMsg,data){//... do something ... // Done});});});});// Die Schachtelung von Callback-Funktionen führt hier zu sehr unleserlichen Code// Lösen können dies Biblitheken die speziell für dieses Problem entwickelt wurden// ein Beispiel ist ASYNC vgl. https://www.npmjs.com/package/asyncvarasync=require("async");async.series([function(callback){client.MsgGetKontoUmsaetze(client.konten[0].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_1');});},function(callback){client.MsgGetKontoUmsaetze(client.konten[1].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result_2');});},// ...],function(err,results){// results is now equal to ['result_1', 'result_2']// done});// bzw. als Schleifeasync.eachSeries([1,2,3,4],function(i,callback){client.MsgGetKontoUmsaetze(client.konten[i].sepa_data,null,null,function(error2,rMsg,data){//... do something ...callback(null,'result');});},function(err,results){// results is now equal to ['result', 'result']// done});

Entwickler-Tools

Das Projekt beinhaltet auch einen FinTS 3.0 Server. Unterstützt wird nur das Pin/Tan Verfahren. Der Server dient primär als Testserver für die Entwicklung der API, er wird auch für die TestCases verwendet.

Test-Cases

Um die Tests zu starten im Verzeichnis des Projekts folgenden Befehl in der Konsole ausführen:

npm test

Für die Tests wird der interne FinTS Server verwendet und die in der Datei credentials.js konfigurierten FinTS Real Server. credentials.js

module.exports={bankenliste:{'12345678':{'blz':12345678,'url':"http://localhost:3000/cgi-bin/hbciservlet"},"undefined":{'url':""}},blz:12345678,user:"maxmuster",pin:"12345",bunyan_live_logger:true};

Um ein Code-Coverage Test durchzuführen folgenden Befehl im Paket eingeben.

npm run coverage

Die Demos können auch direkt aus dem Paket ausgeführt werden.

npm run demo

Logging

Logs werden mit Bunyan erstellt. Standardmäßig werden keine Logs erstellt. Soll das Logging aktiviert werden muss wie folgt ein Logger beim Aufruf des Konstruktors mitgegeben werden.

varbunyan=require("bunyan");varlog=bunyan.createLogger({name: 'demo_fints_logger',stream: process.stdout,level: 'trace'});varclient=newFinTSClient(12345678,"test1","1234",bankenliste,log);

Für eine bessere Darstellung empfiehlt sich Bunyan CLI.

node examples/zeige_kontoumsaetze.js log | bunyan -l trace

Alternativ können mit dem Bunyan Live Logger die Logs live dargestellt werden. Für die TestCases kann durch credentials.js und dem Attribut bunyan_live_logger:true der Live Logger direkt gestartet werden.

Links

About

FinTS/HBCI Javascript Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages