mirror of
https://github.com/node-ebics/node-ebics-client.git
synced 2024-12-22 20:31:49 +00:00
initial commit
This commit is contained in:
parent
cd37de3895
commit
1f947ff148
32
.editorconfig
Normal file
32
.editorconfig
Normal file
@ -0,0 +1,32 @@
|
||||
# @see editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_size = 4
|
||||
|
||||
[*.tag]
|
||||
indent_size = 2
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
# Trailing whitespace is significant in markdown files.
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
max_line_length = 80
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
build/
|
||||
static/
|
||||
config/
|
||||
src/vue-router-custom-components
|
||||
src/directives/clickAway
|
34
.eslintrc
Normal file
34
.eslintrc
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "airbnb",
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"sourceType": "script",
|
||||
"ecmaFeatures": {
|
||||
"modules": false
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"max-len": 0,
|
||||
"linebreak-style": 0,
|
||||
"no-plusplus": [
|
||||
2,
|
||||
{
|
||||
"allowForLoopAfterthoughts": true
|
||||
}
|
||||
],
|
||||
"no-continue": 0,
|
||||
"indent": [2, "tab"],
|
||||
"no-tabs": 0,
|
||||
"strict": [2, "safe"],
|
||||
"curly": [2, "multi", "consistent"],
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-param-reassign": 0,
|
||||
"generator-star-spacing": 0,
|
||||
"jsx-a11y/href-no-hash": "off"
|
||||
}
|
||||
}
|
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
npm-debug.log
|
||||
node_modules
|
||||
.DS_Store
|
||||
*.local.json5
|
||||
yarn.lock
|
||||
/project.sublime-workspace
|
||||
/public/css/style.css.map
|
||||
/.idea
|
||||
/.vscode
|
||||
*.pid
|
||||
/coverage
|
||||
package-lock.json
|
||||
*.key
|
||||
*.html
|
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# node-ebics-client v0.0.3
|
||||
---
|
||||
|
||||
Pure node.js ( >=8 ) implementation of the [EBICS](https://en.wikipedia.org/wiki/Electronic_Banking_Internet_Communication_Standard) (Electronic Banking Internet Communication).
|
||||
|
||||
The client is aimed to be 100% ISO 20022 complient, and supports complete initilizations process ( INI, HIA, HPB orders ) and HTML letter generation.
|
||||
|
||||
## Supported Banks
|
||||
The client is tested and verified to work with the following banks:
|
||||
* Credit Suisse
|
||||
* Zürcher Kantonalbank
|
||||
* Raiffeisenbank
|
||||
|
||||
|
||||
## Inspiration
|
||||
|
||||
A lot of the concepts in this library are inspired from the [EPICS](https://github.com/railslove/epics) library.
|
||||
|
||||
## Copyright
|
||||
Copyright (c) 2017 eCollect.
|
4
index.js
Normal file
4
index.js
Normal file
@ -0,0 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
const Client = require('./lib/Client');
|
||||
module.exports = Client;
|
66
lib/BankLetter.js
Normal file
66
lib/BankLetter.js
Normal file
@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const moment = require('moment');
|
||||
const handlebars = require('handlebars');
|
||||
const BN = require("bn.js");
|
||||
|
||||
module.exports = class BankLetter {
|
||||
constructor(client, bankName) {
|
||||
this.client = client;
|
||||
this.bankName = bankName;
|
||||
this.pathToTemplate = './app/ebics/ini.hbs';
|
||||
};
|
||||
|
||||
_registerHelpers() {
|
||||
handlebars.registerHelper("today", () => {
|
||||
return moment().format('DD.MM.YYYY');
|
||||
});
|
||||
|
||||
handlebars.registerHelper("now", () => {
|
||||
return moment().format('HH:mm:ss');
|
||||
});
|
||||
|
||||
handlebars.registerHelper("keyExponentBits", (k) => {
|
||||
return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
|
||||
});
|
||||
|
||||
handlebars.registerHelper("keyModulusBits", (k) => {
|
||||
return k.key.getKeySize();
|
||||
// return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
|
||||
});
|
||||
|
||||
handlebars.registerHelper("keyExponent", (k) => {
|
||||
return k.e();
|
||||
});
|
||||
|
||||
handlebars.registerHelper("keyModulus", (k) => {
|
||||
return k.n().toUpperCase().match(/.{1,2}/g).join(' ');
|
||||
});
|
||||
|
||||
handlebars.registerHelper("sha256", (k) => {
|
||||
const digest = Buffer(k.publicDigest(), 'base64').toString('HEX');
|
||||
|
||||
return digest.toUpperCase().match(/.{1,2}/g).join(' ');
|
||||
});
|
||||
};
|
||||
|
||||
generate() {
|
||||
this._registerHelpers();
|
||||
|
||||
const str = fs.readFileSync(this.pathToTemplate).toString();
|
||||
const templ = handlebars.compile(str);
|
||||
|
||||
const data = {
|
||||
bankName : this.bankName,
|
||||
userId : this.client.userId,
|
||||
partnerId: this.client.partnerId,
|
||||
A006 : this.client.a(),
|
||||
X002 : this.client.x(),
|
||||
E002 : this.client.e(),
|
||||
};
|
||||
|
||||
return templ(data);
|
||||
}
|
||||
}
|
233
lib/Client.js
Normal file
233
lib/Client.js
Normal file
@ -0,0 +1,233 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
const $request = require("request");
|
||||
|
||||
const BN = require('bn.js');
|
||||
const xpath = require("xpath");
|
||||
const NodeRSA = require("node-rsa");
|
||||
|
||||
|
||||
const Key = require('./Key');
|
||||
const XMLSign = require('./middleware/XMLSign');
|
||||
const ParseResponse = require('./middleware/ParseResponse');
|
||||
const BankLetter = require('./BankLetter');
|
||||
const EBICSINI = require('./orders/INI');
|
||||
const EBICSHIA = require('./orders/HIA');
|
||||
const EBICSHPB = require('./orders/HPB');
|
||||
const EBICSHKD = require('./orders/HKD');
|
||||
const EBICSHAA = require('./orders/HAA');
|
||||
const EBICSHAC = require('./orders/HAC');
|
||||
const EBICSHTD = require('./orders/HTD');
|
||||
const EBICSC52 = require('./orders/C52');
|
||||
|
||||
const utils = {
|
||||
exponent: {
|
||||
// str = 65537 => AQAB
|
||||
toBase64(str) {
|
||||
return new BN(str).toBuffer().toString('base64');
|
||||
},
|
||||
// str = AQAB => 65537
|
||||
fromBase64(str) {
|
||||
return new BN(Buffer.from(str, 'base64'), 2).toNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = class Client {
|
||||
constructor(keysContent, passphrase, url, hostId, userId, partnerId) {
|
||||
this.keysContent = keysContent;
|
||||
this.passphrase = passphrase;
|
||||
this.url = url;
|
||||
this.hostId = hostId;
|
||||
this.userId = userId;
|
||||
this.partnerId = partnerId;
|
||||
this.encryptAlgorithm = 'aes-256-cbc';
|
||||
this.keys = keysContent ? this.extractKeys() : {};
|
||||
};
|
||||
|
||||
a() {
|
||||
return this.keys["A006"];
|
||||
};
|
||||
|
||||
e() {
|
||||
return this.keys["E002"];
|
||||
};
|
||||
|
||||
x() {
|
||||
return this.keys["X002"];
|
||||
}
|
||||
|
||||
bankX() {
|
||||
return this.keys[`${this.hostId}.X002`];
|
||||
}
|
||||
|
||||
bankE() {
|
||||
return this.keys[`${this.hostId}.E002`];
|
||||
}
|
||||
|
||||
encrypt(data) {
|
||||
const cipher = crypto.createCipher(this.encryptAlgorithm, this.passphrase);
|
||||
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
|
||||
|
||||
return Buffer.from(encrypted).toString('base64');
|
||||
};
|
||||
|
||||
decrypt(data) {
|
||||
data = (new Buffer(data, 'base64')).toString();
|
||||
|
||||
const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase);
|
||||
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) {
|
||||
const client = new Client(null, passphrase, url, hostId, userId, partnerId);
|
||||
|
||||
for (let key in {A006: '', X002: '', E002: ''}) {
|
||||
client.keys[key] = new Key(new NodeRSA({ b: keysize }));
|
||||
}
|
||||
|
||||
return client;
|
||||
};
|
||||
|
||||
saveIniLetter(bankName, path) {
|
||||
const letter = new BankLetter(this, bankName);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(path, letter.generate());
|
||||
console.log("Data written to file");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
saveKeys(path) {
|
||||
const data = {};
|
||||
|
||||
for (let key in this.keys) {
|
||||
data[key] = this.encrypt(this.keys[key].toPem());
|
||||
};
|
||||
|
||||
try {
|
||||
fs.writeFileSync(path, JSON.stringify(data));
|
||||
console.log("Data written to file");
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
extractKeys() {
|
||||
const keys = {};
|
||||
const jsonData = JSON.parse(this.keysContent);
|
||||
|
||||
for (let key in jsonData) {
|
||||
keys[key] = new Key(this.decrypt(jsonData[key]));
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
async download(order) {
|
||||
const res = await this.ebicsRequest(order.toXML());
|
||||
|
||||
const ttt = res.toXML(); // keep this for debugging purposes
|
||||
|
||||
order.transactionId = res.transactionId();
|
||||
|
||||
if (res.isSegmented() && res.isLastSegment()) {
|
||||
const receipt = await this.ebicsRequest(order.toReceiptXML());
|
||||
|
||||
const receiptXML = order.toReceiptXML(); // keep this for debugging purposes
|
||||
const rX = receipt.toXML(); // keep this for debugging purposes
|
||||
}
|
||||
|
||||
return res.orderData();
|
||||
};
|
||||
|
||||
async downloadAndUnzip(order) {
|
||||
|
||||
}
|
||||
|
||||
ebicsRequest(order) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bbb = XMLSign.go(this, order);
|
||||
$request.post({
|
||||
url : this.url,
|
||||
body : bbb,
|
||||
headers: { 'Content-Type': 'text/xml' }
|
||||
}, (err, res, data) => {
|
||||
const b = data; // keep this for debugging purposes
|
||||
const r = ParseResponse.go(this, data); // keep this for debugging purposes
|
||||
const rXML = r.toXML(); // keep this for debugging purposes
|
||||
|
||||
return err ? reject(err): resolve(ParseResponse.go(this, data));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async INI() {
|
||||
return this.ebicsRequest((new EBICSINI(this)).toXML());
|
||||
};
|
||||
|
||||
async HIA() {
|
||||
return this.ebicsRequest((new EBICSHIA(this)).toXML());
|
||||
};
|
||||
|
||||
async HPB() {
|
||||
const data = await this.download(new EBICSHPB(this));
|
||||
|
||||
const doc = new DOMParser().parseFromString(data, 'text/xml');
|
||||
const sel = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"});
|
||||
const keyNodes = sel("//xmlns:PubKeyValue", doc);
|
||||
// console.log(keyNodes);
|
||||
|
||||
function xmlLastChild (node) {
|
||||
let y = node.lastChild;
|
||||
|
||||
while (y.nodeType != 1) y = y.previousSibling;
|
||||
|
||||
return y;
|
||||
};
|
||||
|
||||
for (let i = 0; i < keyNodes.length; i++) {
|
||||
const type = xmlLastChild(keyNodes[i].parentNode).textContent;
|
||||
const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent;
|
||||
const exponent = xpath.select("//*[local-name(.)='Exponent']", keyNodes[i])[0].textContent;
|
||||
|
||||
const mod = new BN(Buffer.from(modulus, 'base64'), 2).toBuffer();
|
||||
const exp = utils.exponent.fromBase64(exponent);
|
||||
|
||||
const bank = new NodeRSA();
|
||||
|
||||
bank.importKey({ n: mod, e: exp }, 'components-public');
|
||||
|
||||
this.keys[`${this.hostId}.${type}`] = new Key(bank);
|
||||
}
|
||||
|
||||
return [this.bankX(), this.bankE()];
|
||||
};
|
||||
|
||||
HKD() {
|
||||
return this.download(new EBICSHKD(this));
|
||||
};
|
||||
|
||||
HAA() {
|
||||
return this.download(new EBICSHAA(this));
|
||||
};
|
||||
|
||||
HTD() {
|
||||
return this.download(new EBICSHTD(this));
|
||||
};
|
||||
|
||||
HAC(from = null, to = null) {
|
||||
return this.download(new EBICSHAC(this, from, to));
|
||||
};
|
||||
|
||||
C52(from, to) {
|
||||
return this.downloadAndUnzip(new EBICSC52(this, from, to));
|
||||
}
|
||||
};
|
38
lib/Key.js
Normal file
38
lib/Key.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
const NodeRSA = require("node-rsa");
|
||||
const BN = require('bn.js');
|
||||
|
||||
module.exports = class Key {
|
||||
constructor(encodedKey, passphrase = null) {
|
||||
if (encodedKey instanceof NodeRSA) {
|
||||
this.key = encodedKey
|
||||
} else {
|
||||
this.key = new NodeRSA(encodedKey);
|
||||
}
|
||||
};
|
||||
|
||||
publicDigest() {
|
||||
const str = [this.e().replace(/^(0+)/g, ''), this.n().replace(/^(0+)/g, '')].map((x) => x.toLowerCase()).join(' ');
|
||||
|
||||
return crypto.createHash('sha256').update(str).digest('base64').trim();
|
||||
};
|
||||
|
||||
publicEncrypt(str) {
|
||||
return this.key.encrypt(str);
|
||||
}
|
||||
|
||||
n() {
|
||||
return this.key.exportKey("components-public").n.toString("hex", 1);
|
||||
};
|
||||
|
||||
e() {
|
||||
return new BN(this.key.exportKey("components-public").e).toBuffer().toString('hex');
|
||||
};
|
||||
|
||||
toPem() {
|
||||
return this.key.isPrivate() ? this.key.exportKey("pkcs1-private-pem") : this.key.exportKey("pkcs8-public-pem");
|
||||
}
|
||||
};
|
57
lib/Response.js
Normal file
57
lib/Response.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
const crypto = require("crypto");
|
||||
|
||||
const DOMParser = require("xmldom").DOMParser;
|
||||
const XMLSerializer = require("xmldom").XMLSerializer;
|
||||
const xpath = require("xpath");
|
||||
|
||||
module.exports = class Response {
|
||||
constructor(client, data) {
|
||||
this.client = client;
|
||||
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
||||
};
|
||||
|
||||
isSegmented() {
|
||||
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"});
|
||||
const node = select("//xmlns:header/xmlns:mutable/xmlns:SegmentNumber", this.doc);
|
||||
|
||||
return node.length ? true: false;
|
||||
}
|
||||
|
||||
isLastSegment() {
|
||||
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"});
|
||||
const node = select("//xmlns:header/xmlns:mutable/*[@lastSegment='true']", this.doc);
|
||||
|
||||
return node.length ? true: false;
|
||||
}
|
||||
|
||||
orderData() {
|
||||
const orderData = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "OrderData")[0].textContent;
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false);
|
||||
const data = Buffer.from(decipher.update(orderData, 'base64', 'binary') + decipher.final('binary'), 'binary');
|
||||
|
||||
return zlib.inflateSync(data).toString();
|
||||
}
|
||||
|
||||
transactionKey() {
|
||||
const keyNodeText = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "TransactionKey")[0].textContent;
|
||||
const tkEncrypted = Buffer.from(keyNodeText, 'base64');
|
||||
|
||||
this.client.e().key.setOptions({encryptionScheme: 'pkcs1'});
|
||||
|
||||
return this.client.e().key.decrypt(tkEncrypted);
|
||||
}
|
||||
|
||||
transactionId() {
|
||||
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"});
|
||||
const node = select("//xmlns:header/xmlns:static/xmlns:TransactionID", this.doc);
|
||||
|
||||
return node.length ? node[0].textContent : '';
|
||||
}
|
||||
|
||||
toXML() {
|
||||
return new XMLSerializer().serializeToString(this.doc);
|
||||
}
|
||||
};
|
103
lib/Signer.js
Normal file
103
lib/Signer.js
Normal file
@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
const DOMParser = require("xmldom").DOMParser;
|
||||
const XMLSerializer = require("xmldom").XMLSerializer;
|
||||
const select = require("xpath.js");
|
||||
const c14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization;
|
||||
|
||||
|
||||
module.exports = class Signer {
|
||||
/**
|
||||
* Contructor.
|
||||
*
|
||||
* @param {Client} client
|
||||
* @param {String} data
|
||||
*/
|
||||
constructor(client, data) {
|
||||
/**
|
||||
* The main client
|
||||
*
|
||||
* @type {Signer}
|
||||
*/
|
||||
this.client = client;
|
||||
|
||||
/**
|
||||
* Request data - generated xml
|
||||
*
|
||||
* @type {...}
|
||||
*/
|
||||
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
||||
}
|
||||
|
||||
_junk() {
|
||||
this.digest();
|
||||
this.sign();
|
||||
// console.log(this.toXML());
|
||||
/* const headerSet = select(this.doc, "//*[@authenticate='true']").map(x => {
|
||||
// x.setAttribute('xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
|
||||
return new c14n().process(x);
|
||||
}).join();
|
||||
const can = headerSet.replace('xmlns="urn:org:ebics:H004"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
|
||||
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(can);
|
||||
const digester = hash.digest('base64').trim();
|
||||
if ( this.doc.getElementsByTagName("ds:DigestValue")[0] )
|
||||
this.doc.getElementsByTagName("ds:DigestValue")[0].textContent = digester; */
|
||||
|
||||
/* const nodeSet = select(this.doc, "//ds:SignedInfo");
|
||||
const canonicalized = nodeSet.map(x => {
|
||||
const g = x.toString();
|
||||
const res = new c14n().process(x);
|
||||
return res;
|
||||
}).join();
|
||||
|
||||
const canonicalizedString = canonicalized.replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
|
||||
|
||||
// const SIGN = crypto.createSign('RSA-SHA256');
|
||||
// SIGN.update(canonicalizedString);
|
||||
// const key = SIGN.sign(this.client.x().key.exportKey("pkcs1-private-pem"), 'base64');
|
||||
const f = this.client.x().key.sign(canonicalizedString, 'base64');
|
||||
if ( this.doc.getElementsByTagName("ds:SignatureValue")[0] ) {
|
||||
this.doc.getElementsByTagName("ds:SignatureValue")[0].textContent = f;
|
||||
} */
|
||||
}
|
||||
|
||||
digest() {
|
||||
// get the xml node, where the digested value is supposed to be
|
||||
const nodeDigestValue = this.doc.getElementsByTagName("ds:DigestValue")[0];
|
||||
|
||||
const nodes = select(this.doc, "//*[@authenticate='true']");
|
||||
|
||||
// canonicalize the node that has authenticate='true' attribute
|
||||
const contentToDigest = select(this.doc, "//*[@authenticate='true']")
|
||||
.map(x => {
|
||||
const aaaa = x.toString();
|
||||
return new c14n().process(x)
|
||||
}).join("");
|
||||
|
||||
// fix the canonicalization
|
||||
const fixedContent = contentToDigest.replace(/xmlns="urn:org:ebics:H004"/g, 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
|
||||
|
||||
if (nodeDigestValue) {
|
||||
nodeDigestValue.textContent = crypto.createHash('sha256').update(fixedContent).digest('base64').trim();
|
||||
}
|
||||
};
|
||||
|
||||
sign() {
|
||||
const nodeSignatureValue = this.doc.getElementsByTagName("ds:SignatureValue")[0];
|
||||
|
||||
if (nodeSignatureValue) {
|
||||
const contentToSign = (new c14n().process(select(this.doc, "//ds:SignedInfo")[0])).replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
|
||||
|
||||
nodeSignatureValue.textContent = this.client.x().key.sign(contentToSign, 'base64');
|
||||
}
|
||||
}
|
||||
|
||||
toXML() {
|
||||
return new XMLSerializer().serializeToString(this.doc);
|
||||
}
|
||||
|
||||
};
|
12
lib/consts.js
Normal file
12
lib/consts.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
const name = 'eCollect Node Ebics Client';
|
||||
const version = packageJson.version;
|
||||
|
||||
module.exports = {
|
||||
name,
|
||||
version,
|
||||
productString: `${name} ${version}`,
|
||||
};
|
164
lib/ini.hbs
Normal file
164
lib/ini.hbs
Normal file
@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>EBICS ini</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h2>EBICS-Initialisierungsbrief (INI)</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Datum</td>
|
||||
<td>{{ today }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uhrzeit</td>
|
||||
<td>{{ now }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Empfänger</td>
|
||||
<td>{{ bankName }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User-ID</td>
|
||||
<td>{{ userId }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kunden-ID</td>
|
||||
<td>{{ partnerId }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Öffentlicher Schlüssel für die elektronische Unterschrift (A006)</p>
|
||||
<p>Exponent ({{ keyExponentBits A006 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyExponent A006 }}</code>
|
||||
</p>
|
||||
<p>Modulus ({{ keyModulusBits A006 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyModulus A006 }}</code>
|
||||
</p>
|
||||
<p>Hash (SHA-256):</p>
|
||||
<p>
|
||||
<code>{{ sha256 A006 }}</code>
|
||||
</p>
|
||||
<p>Ich bestätige hiermit den obigen öffentlichen Schlüssel für meine elektronische Unterschrift.</p>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>_________________________</td>
|
||||
<td>_________________________</td>
|
||||
<td>_________________________</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ort/Datum</td>
|
||||
<td>Name/Firma</td>
|
||||
<td>Unterschrift</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="page-break-after:always"></div>
|
||||
<h2>EBICS-Initialisierungsbrief (HIA) - Seite 1/2</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Datum</td>
|
||||
<td>{{ today }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uhrzeit</td>
|
||||
<td>{{ now }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Empfänger</td>
|
||||
<td>{{ bankName }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User-ID</td>
|
||||
<td>{{ userId }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kunden-ID</td>
|
||||
<td>{{ partnerId }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
<p>Öffentlicher Authentifikationsschlüssel (X002)</p>
|
||||
<p>Exponent ({{ keyExponentBits X002 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyExponent X002 }}</code>
|
||||
</p>
|
||||
<p>Modulus ({{ keyModulusBits X002 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyModulus X002 }}</code>
|
||||
</p>
|
||||
<p>Hash (SHA-256):</p>
|
||||
<p>
|
||||
<code>{{ sha256 X002 }}</code>
|
||||
</p>
|
||||
<p> Fortsetzung auf Seite 2 ...</p>
|
||||
<div style="page-break-after:always"></div>
|
||||
<h2>EBICS-Initialisierungsbrief (HIA) - Seite 2/2</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Datum</td>
|
||||
<td>{{ today }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uhrzeit</td>
|
||||
<td>{{ now }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Empfänger</td>
|
||||
<td>{{ bankName }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User-ID</td>
|
||||
<td>{{ userId }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kunden-ID</td>
|
||||
<td>{{ partnerId }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<p>Öffentlicher Verschlüsselungsschlüssel (E002)</p>
|
||||
<p>Exponent ({{ keyExponentBits E002 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyExponent E002 }}</code>
|
||||
</p>
|
||||
<p>Modulus ({{ keyModulusBits E002 }} Bit):</p>
|
||||
<p>
|
||||
<code>{{ keyModulus E002 }}</code>
|
||||
</p>
|
||||
<p>Hash (SHA-256):</p>
|
||||
<p>
|
||||
<code>{{ sha256 E002 }}</code>
|
||||
</p>
|
||||
<p>Ich bestätige hiermit die obigen öffentlichen Schlüssel für meinen EBICS-Zugang.</p>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>_________________________</td>
|
||||
<td>_________________________</td>
|
||||
<td>_________________________</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ort/Datum</td>
|
||||
<td>Name/Firma</td>
|
||||
<td>Unterschrift</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
22
lib/middleware/ParseResponse.js
Normal file
22
lib/middleware/ParseResponse.js
Normal file
@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const Response = require('../Response');
|
||||
|
||||
module.exports = class ParseResponse {
|
||||
constructor(client, data) {
|
||||
this.client = client;
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
static go (client, data) {
|
||||
const parseRensponse = new ParseResponse(client, data);
|
||||
const response = new Response(client, data);
|
||||
|
||||
// TODO:
|
||||
// raise error if any
|
||||
|
||||
this.data = response.doc;
|
||||
|
||||
return response;
|
||||
}
|
||||
};
|
22
lib/middleware/XMLSign.js
Normal file
22
lib/middleware/XMLSign.js
Normal file
@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const Signer = require('../Signer');
|
||||
|
||||
module.exports = class XMLSign {
|
||||
constructor(client, data) {
|
||||
this.client = client;
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
static go (client, data) {
|
||||
const xmlSigner = new XMLSign(client, data);
|
||||
const signer = new Signer(client, data);
|
||||
|
||||
signer.digest();
|
||||
signer.sign();
|
||||
|
||||
this.data = signer.toXML();
|
||||
|
||||
return this.data;
|
||||
}
|
||||
};
|
50
lib/orders/C52.js
Normal file
50
lib/orders/C52.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class C52 extends GenericOrder {
|
||||
constructor (client, from, to) {
|
||||
super(client);
|
||||
this._from = from;
|
||||
this._to = to;
|
||||
|
||||
this._schema.header = {
|
||||
"@" : { authenticate: true },
|
||||
static: {
|
||||
HostID : this.hostId,
|
||||
Nonce : this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID : this.userId,
|
||||
Product : {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType : "C52",
|
||||
OrderAttribute : "DZHNN",
|
||||
StandardOrderParams: {
|
||||
DateRange: {
|
||||
Start: this._from,
|
||||
End : this._to
|
||||
}
|
||||
},
|
||||
},
|
||||
BankPubKeyDigests: {
|
||||
Authentication: {
|
||||
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankX().publicDigest()
|
||||
},
|
||||
Encryption: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
}
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: "Initialisation"
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
143
lib/orders/GenericOrder.js
Normal file
143
lib/orders/GenericOrder.js
Normal file
@ -0,0 +1,143 @@
|
||||
'use strict';
|
||||
|
||||
// const randHex = require('../../lib/utils').randHex;
|
||||
const crypto = require("crypto");
|
||||
const js2xmlparser = require('js2xmlparser');
|
||||
const consts = require('../consts');
|
||||
|
||||
module.exports = class GenericOrder {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
|
||||
this.hostId = client.hostId;
|
||||
this.userId = client.userId;
|
||||
this.partnerId = client.partnerId;
|
||||
|
||||
this.transactionId = '';
|
||||
|
||||
this.xmlOptions = {
|
||||
declaration: {
|
||||
include: true,
|
||||
encoding: "utf-8"
|
||||
},
|
||||
format: {
|
||||
doubleQuotes: true,
|
||||
indent: '',
|
||||
newline: '',
|
||||
// indent: "\t",
|
||||
// newline: "\r\n",
|
||||
pretty: true
|
||||
}
|
||||
};
|
||||
|
||||
this._schema = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "urn:org:ebics:H004",
|
||||
Version: "H004",
|
||||
Revision: "1"
|
||||
},
|
||||
|
||||
header: {},
|
||||
|
||||
AuthSignature: this.authSignature(),
|
||||
|
||||
body: {}
|
||||
};
|
||||
}
|
||||
|
||||
authSignature() {
|
||||
return {
|
||||
"ds:SignedInfo": {
|
||||
"ds:CanonicalizationMethod": {
|
||||
"@": {
|
||||
Algorithm:
|
||||
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|
||||
}
|
||||
},
|
||||
"ds:SignatureMethod": {
|
||||
"@": {
|
||||
Algorithm:
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
|
||||
}
|
||||
},
|
||||
"ds:Reference": {
|
||||
"@": { URI: "#xpointer(//*[@authenticate='true'])" },
|
||||
"ds:Transforms": {
|
||||
"ds:Transform": {
|
||||
"@": {
|
||||
Algorithm:
|
||||
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ds:DigestMethod": {
|
||||
"@": {
|
||||
Algorithm:
|
||||
"http://www.w3.org/2001/04/xmlenc#sha256"
|
||||
}
|
||||
},
|
||||
"ds:DigestValue": {}
|
||||
}
|
||||
},
|
||||
"ds:SignatureValue": {}
|
||||
};
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return this._schema;
|
||||
}
|
||||
|
||||
get productString() {
|
||||
return consts.productString;
|
||||
}
|
||||
|
||||
nonce() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
root() {
|
||||
return "ebicsRequest";
|
||||
}
|
||||
|
||||
toReceiptXML() {
|
||||
const xmlObj = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "urn:org:ebics:H004",
|
||||
Version: "H004",
|
||||
Revision: "1"
|
||||
},
|
||||
|
||||
header: {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
TransactionID: this.transactionId
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: 'Receipt',
|
||||
}
|
||||
},
|
||||
|
||||
AuthSignature: this.authSignature(),
|
||||
|
||||
body: {
|
||||
TransferReceipt: {
|
||||
"@": { authenticate: true },
|
||||
ReceiptCode: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions);
|
||||
}
|
||||
|
||||
toXML() {
|
||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
||||
}
|
||||
};
|
63
lib/orders/GenericUploadOrder.js
Normal file
63
lib/orders/GenericUploadOrder.js
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
const crypto = require("crypto");
|
||||
const js2xmlparser = require('js2xmlparser');
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class GenericUploadOrder extends GenericOrder {
|
||||
constructor(client, document) {
|
||||
super(client);
|
||||
|
||||
this._document = document;
|
||||
this._key = crypto.randomBytes(16);
|
||||
|
||||
this._schema.body = {
|
||||
DataTransfer: {
|
||||
DataEncryptionInfo: {
|
||||
"@": { authenticate: true },
|
||||
EncryptionPubKeyDigest: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
},
|
||||
TransactionKey: Buffer.from(this.client.bankE().publicEncrypt(this._key)).toString('base64'),
|
||||
},
|
||||
SignatureData: {
|
||||
"@": { authenticate: true },
|
||||
"#": this.encryptedOrderSignature()
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
orderSignature() {
|
||||
const xmlObj = {
|
||||
"@": {
|
||||
xmlns: "http://www.ebics.org/S001",
|
||||
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||
"xsi:schemaLocation": "http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd"
|
||||
},
|
||||
OrderSignatureData: {
|
||||
SignatureVersion: "A006",
|
||||
SignatureValue: this.signatureValue(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId
|
||||
}
|
||||
};
|
||||
|
||||
return js2xmlparser.parse('UserSignatureData', xmlObj, this.xmlOptions);
|
||||
};
|
||||
|
||||
signatureValue() {
|
||||
const digested = crypto.createHash('sha256').update(this._document).digest();
|
||||
};
|
||||
|
||||
encryptedOrderSignature() {
|
||||
const dst = zlib.deflateSync(this.orderSignature());
|
||||
const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false);
|
||||
const encrypted = cipher.update(dst) + cipher.final();
|
||||
|
||||
return Buffer.from(encrypted).toString('base64');
|
||||
};
|
||||
};
|
43
lib/orders/HAA.js
Normal file
43
lib/orders/HAA.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HAA extends GenericOrder {
|
||||
constructor (client) {
|
||||
super(client);
|
||||
|
||||
this._schema.header = {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
Nonce: this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "HAA",
|
||||
OrderAttribute: "DZHNN",
|
||||
StandardOrderParams: ""
|
||||
},
|
||||
BankPubKeyDigests: {
|
||||
Authentication: {
|
||||
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankX().publicDigest()
|
||||
},
|
||||
Encryption: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
}
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: "Initialisation"
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
54
lib/orders/HAC.js
Normal file
54
lib/orders/HAC.js
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HAC extends GenericOrder {
|
||||
constructor (client, from = null, to = null) {
|
||||
super(client);
|
||||
this._from = from;
|
||||
this._to = to;
|
||||
|
||||
this._schema.header = {
|
||||
"@" : { authenticate: true },
|
||||
static: {
|
||||
HostID : this.hostId,
|
||||
Nonce : this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID : this.userId,
|
||||
Product : {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType : "HAC",
|
||||
OrderAttribute : "DZHNN",
|
||||
StandardOrderParams: this._hasDateRange() ? {
|
||||
DateRange: {
|
||||
Start: this._from,
|
||||
End : this._to
|
||||
}
|
||||
} : ""
|
||||
},
|
||||
BankPubKeyDigests: {
|
||||
Authentication: {
|
||||
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankX().publicDigest()
|
||||
},
|
||||
Encryption: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
}
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: "Initialisation"
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
_hasDateRange() {
|
||||
return this._from && this._to;
|
||||
}
|
||||
};
|
85
lib/orders/HIA.js
Normal file
85
lib/orders/HIA.js
Normal file
@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
const js2xmlparser = require('js2xmlparser');
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HIA extends GenericOrder {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
|
||||
this._schema = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "urn:org:ebics:H004",
|
||||
Version: "H004",
|
||||
Revision: "1"
|
||||
},
|
||||
|
||||
header: {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "HIA",
|
||||
OrderAttribute: "DZNNN"
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {}
|
||||
},
|
||||
|
||||
body: {
|
||||
DataTransfer: {
|
||||
OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
root() {
|
||||
return "ebicsUnsecuredRequest";
|
||||
};
|
||||
|
||||
orderData() {
|
||||
const xmlOrderData = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "urn:org:ebics:H004"
|
||||
},
|
||||
AuthenticationPubKeyInfo: {
|
||||
PubKeyValue: {
|
||||
"ds:RSAKeyValue": {
|
||||
"ds:Modulus": Buffer.from(this.client.x().n(), 'HEX').toString('base64'),
|
||||
"ds:Exponent": "AQAB"
|
||||
},
|
||||
},
|
||||
AuthenticationVersion: "X002"
|
||||
},
|
||||
EncryptionPubKeyInfo: {
|
||||
PubKeyValue: {
|
||||
"ds:RSAKeyValue": {
|
||||
"ds:Modulus": Buffer.from(this.client.e().n(), 'HEX').toString('base64'),
|
||||
"ds:Exponent": "AQAB"
|
||||
},
|
||||
},
|
||||
EncryptionVersion: "E002"
|
||||
},
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId
|
||||
};
|
||||
|
||||
return js2xmlparser.parse("HIARequestOrderData", xmlOrderData, this.xmlOptions);
|
||||
};
|
||||
|
||||
toXML() {
|
||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
||||
};
|
||||
};
|
43
lib/orders/HKD.js
Normal file
43
lib/orders/HKD.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HKD extends GenericOrder {
|
||||
constructor (client) {
|
||||
super(client);
|
||||
|
||||
this._schema.header = {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
Nonce: this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "HKD",
|
||||
OrderAttribute: "DZHNN",
|
||||
StandardOrderParams: ""
|
||||
},
|
||||
BankPubKeyDigests: {
|
||||
Authentication: {
|
||||
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankX().publicDigest()
|
||||
},
|
||||
Encryption: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
}
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: "Initialisation"
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
34
lib/orders/HPB.js
Normal file
34
lib/orders/HPB.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HPB extends GenericOrder {
|
||||
constructor (client) {
|
||||
super(client);
|
||||
|
||||
this._schema.header = {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
Nonce: this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "HPB",
|
||||
OrderAttribute: "DZHNN"
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {}
|
||||
};
|
||||
};
|
||||
|
||||
root() {
|
||||
return "ebicsNoPubKeyDigestsRequest";
|
||||
};
|
||||
};
|
43
lib/orders/HTD.js
Normal file
43
lib/orders/HTD.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class HTD extends GenericOrder {
|
||||
constructor (client) {
|
||||
super(client);
|
||||
|
||||
this._schema.header = {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
Nonce: this.nonce(),
|
||||
Timestamp: this.timestamp(),
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "HTD",
|
||||
OrderAttribute: "DZHNN",
|
||||
StandardOrderParams: ""
|
||||
},
|
||||
BankPubKeyDigests: {
|
||||
Authentication: {
|
||||
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankX().publicDigest()
|
||||
},
|
||||
Encryption: {
|
||||
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" },
|
||||
"#": this.client.bankE().publicDigest()
|
||||
}
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {
|
||||
TransactionPhase: "Initialisation"
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
77
lib/orders/INI.js
Normal file
77
lib/orders/INI.js
Normal file
@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
const js2xmlparser = require('js2xmlparser');
|
||||
|
||||
const GenericOrder = require('./GenericOrder');
|
||||
|
||||
module.exports = class INI extends GenericOrder {
|
||||
constructor (client) {
|
||||
super(client);
|
||||
|
||||
this._schema = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "urn:org:ebics:H004",
|
||||
Version: "H004",
|
||||
Revision: "1"
|
||||
},
|
||||
|
||||
header: {
|
||||
"@": { authenticate: true },
|
||||
static: {
|
||||
HostID: this.hostId,
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId,
|
||||
Product: {
|
||||
"@": { Language: "de" },
|
||||
"#": this.productString,
|
||||
},
|
||||
OrderDetails: {
|
||||
OrderType: "INI",
|
||||
OrderAttribute: "DZNNN"
|
||||
},
|
||||
SecurityMedium: "0000"
|
||||
},
|
||||
mutable: {}
|
||||
},
|
||||
|
||||
body: {
|
||||
DataTransfer: {
|
||||
OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
root() {
|
||||
return "ebicsUnsecuredRequest";
|
||||
};
|
||||
|
||||
keySignature() {
|
||||
const xmlOrderData = {
|
||||
"@": {
|
||||
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
xmlns: "http://www.ebics.org/S001"
|
||||
},
|
||||
SignaturePubKeyInfo: {
|
||||
PubKeyValue: {
|
||||
"ds:RSAKeyValue": {
|
||||
"ds:Modulus": Buffer.from(this.client.a().n(), 'HEX').toString('base64'),
|
||||
"ds:Exponent": "AQAB"
|
||||
},
|
||||
TimeStamp: this.timestamp()
|
||||
},
|
||||
SignatureVersion: "A006"
|
||||
},
|
||||
PartnerID: this.partnerId,
|
||||
UserID: this.userId
|
||||
};
|
||||
|
||||
return js2xmlparser.parse("SignaturePubKeyOrderData", xmlOrderData, this.xmlOptions);
|
||||
};
|
||||
|
||||
toXML() {
|
||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
||||
}
|
||||
};
|
36
package.json
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "node-ebics-client",
|
||||
"version": "0.0.3",
|
||||
"description": "Node.js ISO 20022 Compliant EBICS Client",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/eCollect/node-ebics-client"
|
||||
},
|
||||
"keywords": [
|
||||
"EBICS",
|
||||
"ISO20022",
|
||||
"nodejs",
|
||||
"api"
|
||||
],
|
||||
"author": "eCollect Sofia Tech Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.11.8",
|
||||
"handlebars": "^4.0.11",
|
||||
"js2xmlparser": "^3.0.0",
|
||||
"moment": "^2.22.1",
|
||||
"node-rsa": "^0.4.2",
|
||||
"xml-c14n": "0.0.6",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.27"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.12.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user