UNPKG

@alexotano/cfdi-sat

Version:

Libreria para la generación y sellado de XML para facturación electrónica 4.0 en México (SAT)

254 lines (217 loc) 5.44 kB
'use strict' const Node = require('./Node/Node') const FileSystem = require('./Utils/FileSystem') const xsltproc = require('xsltproc') const JXON = require('jxon') const fs = require('fs') const crypto = require('crypto') class CFDIPago extends Node { /** * * @returns {*} */ getXml() { return this._makeXml() } /** * * @returns {*} * @private */ _makeXml(internal = false) { if (internal) { this.nodes = []; } this.makeAllNodes() // this.nodes.forEach(node => node.makeAllNodes()); this.nodes.forEach(node => { node.makeAllNodes() if (node.nodes.length > 0) { node.nodes.forEach(n => { n.makeAllNodes() if (n.nodes.length > 0) { n.nodes.forEach(n2 => { n2.makeAllNodes() if (n2.nodes.length > 0) { n2.nodes.forEach(n3 => { n3.makeAllNodes() }) } }) } }) } }); const Cfdi = JXON.unbuild(this.getAttributes(), null, this.nodeName); let xmlString = JXON.xmlToString(Cfdi); xmlString = xmlString.replace(' xmlns:cfdi=""', ''); let xmlObject = JXON.stringToXml(xmlString); let jsObject = JXON.xmlToJs(xmlObject); if (!this.withOutCerts && !internal) { xmlObject = JXON.jsToXml(jsObject) xmlString = JXON.xmlToString(xmlObject); return this.getCadenaOriginal() .then(cadenaOriginal => this.getSello(cadenaOriginal)) .then(sello => { jsObject['cfdi:Comprobante']['$Sello'] = sello; xmlObject = JXON.jsToXml(jsObject); xmlString = JXON.xmlToString(xmlObject); xmlString = `<?xml version="1.0" encoding="UTF-8"?>${xmlString}`; return Promise.resolve(xmlString) }) } else { xmlString = `<?xml version="1.0" encoding="UTF-8"?>${xmlString}`; return Promise.resolve(xmlString) } } /** * * @returns {{xmlns:cfdi: string}} */ getDefaultAttributes() { let attr = { '$xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '$xmlns:pago20': 'http://www.sat.gob.mx/Pagos20', '$xmlns:cfdi': 'http://www.sat.gob.mx/cfd/4', '$xsi:schemaLocation': `http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd http://www.sat.gob.mx/Pagos20 http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd`, '$Version': '4.0' } if (!this.withOutCerts) { attr['$Certificado'] = this.getCertificado() } return attr } /** * * @returns {Promise} */ getCadenaOriginal() { return new Promise((resolve, reject) => { FileSystem.manageDirectoryTemp('create') const fullPath = `./tmp/${FileSystem.generateNameTemp()}.xml` this._makeXml(true) .then(xml => { fs.writeFile(fullPath, xml, 'utf8', (err) => { if (err) { return reject(err) } const rutaCadenaOriginal = __dirname + '/../resources/cadenaoriginal_4_0.xslt'; try { if (!fs.existsSync( rutaCadenaOriginal )){ console.log('[@alexotano/cfdiSAT] No se puede encontrar el archivo para la cadena original!....'); reject('No se puede encontrar el archivo para la cadena original!.'); return; } } catch (e) { console.log('[@alexotano/cfdiSAT] No se puede encontrar el archivo para la cadena original!....'); reject('No se puede encontrar el archivo para la cadena original!.'); return; } const xslt = xsltproc.transform( rutaCadenaOriginal, fullPath ); xslt.stdout.on('data', (data) => { try { FileSystem.manageDirectoryTemp('delete'); } catch (e) { console.log('[@alexotano/cfdiSAT] Error en: FileSystem.manageDirectoryTemp', e); } resolve(data); return; }) xslt.stderr.on('data', (data, err) => { console.log('@alexotano/cfdi33 error... ', err) FileSystem.manageDirectoryTemp('delete') reject(data) }) xslt.on('exit', (code) => { reject(code) }) }) }) }) } /** * * @param cadenaOriginal * @returns {Promise<any>} */ getSello(cadenaOriginal) { return new Promise((resolve, reject) => { try { const pem = fs.readFileSync(this.key) const key = pem.toString('ascii') const sign = crypto.createSign('RSA-SHA256') sign.update(cadenaOriginal.toString('utf8')) return resolve(sign.sign(key, 'base64')) } catch (err) { return reject(err) } }); } /** * * @returns {*} */ getCertificado() { let cer = FileSystem.readFileSync(this.cer) cer = cer.replace(/(-+[^-]+-+)/g, ''); cer = cer.replace(/\s+/g, ''); return cer; } /** * * @returns {string} */ static get version() { return '4.0' } /** * * @returns {string} */ get nodeName() { return 'cfdi:Comprobante' } /** * * @param value */ set withOutCerts(value) { this._withOutCerts = value } /** * * @param value */ set cer(value) { this._cer = value } /** * * @param value */ set key(value) { this._key = value } /** * * @returns {*} */ get cer() { return this._cer } /** * * @returns {*} */ get key() { return this._key } /** * * @returns {*} */ get withOutCerts() { return this._withOutCerts || false } } module.exports = CFDIPago