Skip to content

Commit

Permalink
Primer versión del API
Browse files Browse the repository at this point in the history
  • Loading branch information
sarriaroman committed May 12, 2016
0 parents commit fd0abb3
Show file tree
Hide file tree
Showing 13 changed files with 611 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# http://editorconfig.org
root = true

[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
lib-cov
*.seed
*.log
*.dat
*.out
*.pid
*.gz

pids
logs
results

npm-debug.log
node_modules

.DS_Store

keys
24 changes: 24 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": false,
"curly": true,
"eqeqeq": false,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"regexp": true,
"undef": true,
"unused": true,
"trailing": true,
"smarttabs": true,
"white": true,
"globals": {},
"predef": [
"global",
"module"
]
}
48 changes: 48 additions & 0 deletions helpers/SignHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
var util = require('util');
var spawn = require('child_process').execSync;

// Expose methods.
exports.sign = sign;

/**
* Sign a file.
*
* @param {object} options Options
* @param {string} options.key Key path
* @param {string} options.cert Cert path
* @param {string} [options.password] Key password
* @returns {Promise} result Result
*/

function sign(options) {
return new Promise(function (resolve, reject) {
options = options || {};

if (!options.content)
reject('Invalid content.');

if (!options.key)
reject('Invalid key.');

if (!options.cert)
reject('Invalid certificate.');

var command = util.format(
'echo "%s" | openssl smime -sign -signer %s -inkey %s -outform DER -nodetach',
options.content.replace(/["']/g, '\\"'),
options.cert,
options.key
);

if (options.password)
command += util.format(' -passin pass:%s', options.password);

//console.info(command);

var child = spawn(command);

var enc = child.toString('base64');

resolve(enc);
});
}
32 changes: 32 additions & 0 deletions helpers/urls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

const afip_urls = {
HOMO: {
wsaa: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl',
service: 'https://wswhomo.afip.gov.ar/{service}/service.asmx?wsdl' //wsfev1
},
PROD: {
wsaa: 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl',
service: 'https://servicios1.afip.gov.ar/{service}/service.asmx?WSDL' //wsfev1
}
};

class AfipUrls {
constructor() {
this.urls = afip_urls.HOMO;

if(!process.env.HOMO) {
this.urls = afip_urls.PROD;
}
}

getWSAA() {
return this.urls.wsaa;
}

getService(service) {
return this.urls.service.replace('{service}', service);
}
}

module.exports = new AfipUrls();
189 changes: 189 additions & 0 deletions helpers/wsaa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
'use strict';

var fs = require('fs'),
soap = require('soap'),
keystone = require('keystone'),
moment = require('moment'),
xml2js = require('xml2js'),
parseString = xml2js.parseString,
XmlBuild = require('xml'),
ntpClient = require('ntp-client'),
SignHelper = require('./SignHelper'),
AfipURLs = require('./urls');

class Tokens {
constructor() {
this.privateKey = fs.readFileSync(global.keys.private, 'utf8');
this.publicKey = fs.readFileSync(global.keys.public, 'utf8');

this.client = false;

this.cache = {};
}

createClient() {
return new Promise((resolve, reject) => {
if (this.client) {
resolve(this.client);
} else {
soap.createClient(AfipURLs.getWSAA(), (err, client) => {
if (err && !client) {
reject();
} else {
this.client = client;

resolve(this.client);
}

});
}
});
}

isExpired(service) {
try {
if (this.cache[service] && this.cache[service].date) {
var hours = Math.abs((new Date()) - this.cache[service].date) / 36e5;

return (hours > 23);
} else {
return true;
}
} catch (e) {
return true;
}
}

getCurrentTime() {
return new Promise((resolve, reject) => {
ntpClient.getNetworkTime("time.afip.gov.ar", 123, function (err, date) {
if (err) {
reject(err);
} else {
console.log("Current time: ", date);
resolve(date);
}
});
});
}

openssl_pkcs7_sign(data, callback) {
SignHelper.sign({
content: data,
key: global.keys.private, //keystone.get('keys_private') + 'cas.key',
cert: global.keys.public //keystone.get('keys') + 'cas.pem'
}).catch(function (err) {
callback(err);
}).then(function (result) {
callback(null, result);
});
}

encryptXML(xml) {
return new Promise((resolve) => {
this.openssl_pkcs7_sign(xml, (err, enc) => {
resolve(enc);
});
});
}

parseXML(data) {
return new Promise((resolve, reject) => {
parseString(data, {
normalizeTags: true,
normalize: true,
explicitArray: false,
attrkey: 'header',
tagNameProcessors: [(key) => { return key.replace('soapenv:', ''); }]
}, (err, res) => {
if (err) reject(err);
else resolve(res);
});
});
}

formatDate(date) {
return moment(date).format().replace('-03:00', '');
}

generateCMS(service) {
return new Promise((resolve, reject) => {
this.getCurrentTime().then((date) => {
var tomorrow = new Date();

// add a day
tomorrow.setDate(date.getDate() + 1);

tomorrow.setMinutes(date.getMinutes());

var data = [{
loginTicketRequest: [
{ _attr: { version: '1.0' } }, {
header: [
{ uniqueId: moment().format('X') },
{ generationTime: this.formatDate(date) },
{ expirationTime: this.formatDate(tomorrow) }
]
}, {
service: service
}
]
}];

var xml = XmlBuild(data, { declaration: true });

this.encryptXML(xml).then(resolve).catch(reject);
});
});
}

generateToken(service) {
// Parse some of the Services
if(service == 'wsfev1') {
service = 'wsfe';
}

return new Promise((resolve, reject) => {

if (this.isExpired(service)) {

this.createClient().then((client) => {

this.generateCMS(service).then((data) => {
client.loginCms({
in0: data
}, (err, result, raw, soapHeader) => {
this.parseXML(raw).then((res) => {
//console.info(res.envelope.body);
var xml_response = res.envelope.body.logincmsresponse.logincmsreturn;

if (xml_response) {
this.parseXML(xml_response).then((res) => {
//console.info(res.loginticketresponse.header);
var credentials = res.loginticketresponse.credentials;

this.cache[service] = {
date: new Date(),
credentials: credentials
};

resolve(credentials);
}).catch(reject);
} else {
reject(res.envelope.body.fault);
}
}).catch(reject);
});
});

});

} else {
resolve(this.cache[service].credentials);
}

});
}
}

module.exports = new Tokens();
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "afipapi",
"version": "0.5.0",
"dependencies": {
"cors": "*",
"express": "^4.13.4",
"lodash": "4.12.0",
"moment": "^2.13.0",
"ntp-client": "^0.5.3",
"soap": "^0.14.0",
"xml": "^1.0.1",
"xml2js": "^0.4.16"
},
"engines": {
"node": ">=4.0.0",
"npm": ">=2.0.0"
},
"scripts": {
"start": "server.js"
}
}
Loading

0 comments on commit fd0abb3

Please sign in to comment.