@pereirajair/nfse-gov-br-node 0.0.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @pereirajair/nfse-gov-br-node
2
+
3
+ Unofficial Node.js library for interacting with the Brazilian **NFS-e Nacional** (Nota Fiscal de Serviço Eletrônica) API.
4
+
5
+ This library simplifies the process of generating, signing, and transmitting the DPS (Declaração Prévia de Serviço) using an A1 digital certificate.
6
+
7
+ ## Features
8
+
9
+ - **Full XML Generation**: Automatically builds the compliant XML structure for DPS.
10
+ - **XML Signing**: Handles XML Digital Signature (X509) using `xml-crypto` (RSA-SHA1 + Enveloped Signature), fully compatible with the national standard.
11
+ - **mTLS Authentication**: Easy configuration for A1 (PFX) certificates for mutual TLS authentication.
12
+ - **Homologation & Production**: toggle between environments easily.
13
+ - **Typed Interfaces**: TypeScript definitions for all major data structures (DPS, Prestador, Tomador, etc.).
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @pereirajair/nfse-gov-br-node
19
+ ```
20
+
21
+ ## Prerequisites
22
+
23
+ - **Node.js** >= 18
24
+ - **A1 Digital Certificate** (.pfx or .p12)
25
+ - **OpenSSL** (usually required by `node-forge` or `xml-crypto` under the hood for some operations, though mostly pure JS).
26
+
27
+ ## Usage
28
+
29
+ ### 1. Basic Setup
30
+
31
+ First, load your certificate and initialize the client:
32
+
33
+ ```javascript
34
+ const fs = require('fs');
35
+ const { loadA1Certificate, NfseClient } = require('@pereirajair/nfse-gov-br-node');
36
+
37
+ // Load Certificate
38
+ const pfxBuffer = fs.readFileSync('path/to/certificate.pfx');
39
+ const certificateData = loadA1Certificate(pfxBuffer, 'YOUR_PASSWORD');
40
+
41
+ // Initialize Client (homologacao or producao)
42
+ const client = new NfseClient({
43
+ environment: 'homologacao', // or 'producao'
44
+ certificate: certificateData,
45
+ });
46
+ ```
47
+
48
+ ### 2. Sending a DPS (Issue NFS-e)
49
+
50
+ ```javascript
51
+ const { enviaDps, generateDpsId } = require('@pereirajair/nfse-gov-br-node');
52
+
53
+ const dpsDataForId = {
54
+ municipioEmissao: '4125506',
55
+ prestador: { cnpj: '00000000000000' },
56
+ serie: '1',
57
+ numero: '10'
58
+ };
59
+ const idDps = generateDpsId(dpsDataForId);
60
+
61
+ const dps = {
62
+ id: idDps,
63
+ ambiente: 2, // 2-Homologação
64
+ dataEmissao: new Date(),
65
+ serie: '1',
66
+ numero: '10',
67
+ prestador: { ... },
68
+ tomador: { ... },
69
+ servico: { ... },
70
+ valores: { ... }
71
+ // See examples/enviar-dps.js for full object structure
72
+ };
73
+
74
+ enviaDps(dps, client, certificateData)
75
+ .then(response => {
76
+ console.log('NFS-e Issued!', response.chaveAcesso);
77
+ })
78
+ .catch(err => console.error(err));
79
+ ```
80
+
81
+ ## Examples
82
+
83
+ Check the `examples/` directory for complete working scripts:
84
+ - `enviar-dps.js`: Full example of issuing an NFS-e.
85
+ - `consultar-nfse.js`: Query an NFS-e by Access Key.
86
+ - `consultar-dps.js`: Query a DPS by ID.
87
+
88
+ ## License
89
+
90
+ MIT
@@ -5,6 +5,6 @@ exports.environmentUrls = void 0;
5
5
  * A mapping of environments to their respective API base URLs.
6
6
  */
7
7
  exports.environmentUrls = {
8
- producao: 'https://www.nfse.gov.br',
9
- homologacao: 'https://www.producaorestrita.nfse.gov.br',
8
+ producao: 'https://sefin.nfse.gov.br/SefinNacional',
9
+ homologacao: 'https://sefin.producaorestrita.nfse.gov.br/SefinNacional',
10
10
  };
@@ -50,29 +50,32 @@ function loadA1Certificate(pfxBuffer, password) {
50
50
  let privateKey = null;
51
51
  let certificate = null;
52
52
  const caCertificates = [];
53
- // Find the private key and certificate
54
- for (const safeContent of p12.safeContents) {
55
- for (const key in safeContent.safeBags) {
56
- const bags = safeContent.safeBags[key];
57
- if (Array.isArray(bags)) {
58
- for (const bag of bags) {
59
- if (bag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {
60
- privateKey = bag.key;
61
- }
62
- else if (bag.type === forge.pki.oids.certBag) {
63
- if (certificate === null) {
64
- certificate = bag.cert;
65
- }
66
- else {
67
- caCertificates.push(bag.cert);
68
- }
69
- }
70
- }
71
- }
53
+ // 1. Get Private Keys (check both encrypted and unencrypted bags)
54
+ const keyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag });
55
+ const unshroudedKeyBags = p12.getBags({ bagType: forge.pki.oids.keyBag });
56
+ const combinedKeyBags = [
57
+ ...(keyBags[forge.pki.oids.pkcs8ShroudedKeyBag] || []),
58
+ ...(unshroudedKeyBags[forge.pki.oids.keyBag] || [])
59
+ ];
60
+ if (combinedKeyBags.length > 0) {
61
+ privateKey = combinedKeyBags[0].key;
62
+ }
63
+ // 2. Get Certificates
64
+ const certBags = p12.getBags({ bagType: forge.pki.oids.certBag });
65
+ const certs = certBags[forge.pki.oids.certBag] || [];
66
+ for (const bag of certs) {
67
+ if (certificate === null) {
68
+ certificate = bag.cert;
69
+ }
70
+ else {
71
+ caCertificates.push(bag.cert);
72
72
  }
73
73
  }
74
- if (!privateKey || !certificate) {
75
- throw new Error('Could not find private key or certificate in PFX/P12 file.');
74
+ if (!privateKey) {
75
+ throw new Error('Could not find private key in PFX/P12 file.');
76
+ }
77
+ if (!certificate) {
78
+ throw new Error('Could not find certificate in PFX/P12 file.');
76
79
  }
77
80
  return { privateKey, certificate, caCertificates };
78
81
  }
@@ -84,6 +87,10 @@ function loadA1Certificate(pfxBuffer, password) {
84
87
  if (error.message.includes('Unable to parse PKCS#12')) {
85
88
  throw new Error('Unable to parse the certificate. The file may be corrupt or in an unsupported format.');
86
89
  }
90
+ // Re-throw our specific errors
91
+ if (error.message.includes('Could not find')) {
92
+ throw error;
93
+ }
87
94
  }
88
95
  throw new Error(`Failed to load A1 certificate: ${error}`);
89
96
  }
@@ -17,11 +17,26 @@ export declare class NfseClient {
17
17
  private readonly config;
18
18
  constructor(config: NfseClientConfig);
19
19
  /**
20
- * Sends a POST request to the specified path with the given XML payload.
20
+ * Sends a POST request to the specified path with the given payload.
21
21
  *
22
22
  * @param path The API endpoint path.
23
- * @param xmlPayload The XML string to send.
23
+ * @param data The data to send (object or string).
24
24
  * @returns A promise that resolves with the response data.
25
25
  */
26
- post(path: string, xmlPayload: string): Promise<any>;
26
+ post(path: string, data: any): Promise<any>;
27
+ /**
28
+ * Sends a GET request to the specified path.
29
+ *
30
+ * @param path The API endpoint path.
31
+ * @returns A promise that resolves with the response data.
32
+ */
33
+ get(path: string): Promise<any>;
34
+ /**
35
+ * Sends a HEAD request to the specified path.
36
+ *
37
+ * @param path The API endpoint path.
38
+ * @param params Optional query parameters.
39
+ * @returns A promise that resolves with the response headers.
40
+ */
41
+ head(path: string, params?: any): Promise<any>;
27
42
  }
@@ -66,20 +66,21 @@ class NfseClient {
66
66
  httpsAgent,
67
67
  timeout,
68
68
  headers: {
69
- 'Content-Type': 'application/xml;charset=utf-8',
69
+ 'Content-Type': 'application/json;charset=utf-8',
70
+ 'Accept': 'application/json',
70
71
  },
71
72
  });
72
73
  }
73
74
  /**
74
- * Sends a POST request to the specified path with the given XML payload.
75
+ * Sends a POST request to the specified path with the given payload.
75
76
  *
76
77
  * @param path The API endpoint path.
77
- * @param xmlPayload The XML string to send.
78
+ * @param data The data to send (object or string).
78
79
  * @returns A promise that resolves with the response data.
79
80
  */
80
- async post(path, xmlPayload) {
81
+ async post(path, data) {
81
82
  try {
82
- const response = await this.client.post(path, xmlPayload);
83
+ const response = await this.client.post(path, data);
83
84
  return response.data;
84
85
  }
85
86
  catch (error) {
@@ -87,7 +88,48 @@ class NfseClient {
87
88
  if (axios_1.default.isAxiosError(error)) {
88
89
  const { response } = error;
89
90
  const errorMessage = (response === null || response === void 0 ? void 0 : response.data) || error.message;
90
- throw new Error(`API request failed with status ${response === null || response === void 0 ? void 0 : response.status}: ${errorMessage}`);
91
+ throw new Error(`API request failed with status ${response === null || response === void 0 ? void 0 : response.status}: ${JSON.stringify(errorMessage)}`);
92
+ }
93
+ throw new Error(`An unexpected error occurred: ${error}`);
94
+ }
95
+ }
96
+ /**
97
+ * Sends a GET request to the specified path.
98
+ *
99
+ * @param path The API endpoint path.
100
+ * @returns A promise that resolves with the response data.
101
+ */
102
+ async get(path) {
103
+ try {
104
+ const response = await this.client.get(path);
105
+ return response.data;
106
+ }
107
+ catch (error) {
108
+ if (axios_1.default.isAxiosError(error)) {
109
+ const { response } = error;
110
+ const errorMessage = (response === null || response === void 0 ? void 0 : response.data) || error.message;
111
+ throw new Error(`API request failed with status ${response === null || response === void 0 ? void 0 : response.status}: ${JSON.stringify(errorMessage)}`);
112
+ }
113
+ throw new Error(`An unexpected error occurred: ${error}`);
114
+ }
115
+ }
116
+ /**
117
+ * Sends a HEAD request to the specified path.
118
+ *
119
+ * @param path The API endpoint path.
120
+ * @param params Optional query parameters.
121
+ * @returns A promise that resolves with the response headers.
122
+ */
123
+ async head(path, params) {
124
+ try {
125
+ const response = await this.client.head(path, { params });
126
+ return response.headers;
127
+ }
128
+ catch (error) {
129
+ if (axios_1.default.isAxiosError(error)) {
130
+ const { response } = error;
131
+ // For HEAD requests, we mostly care about the status code
132
+ throw new Error(`API request failed with status ${response === null || response === void 0 ? void 0 : response.status}`);
91
133
  }
92
134
  throw new Error(`An unexpected error occurred: ${error}`);
93
135
  }
@@ -1,6 +1,6 @@
1
1
  import { CertificateData } from './certificate';
2
2
  /**
3
- * Signs an XML string with the provided certificate data.
3
+ * Signs an XML string with the provided certificate data using xml-crypto.
4
4
  *
5
5
  * @param xml The XML string to sign.
6
6
  * @param certificateData The certificate data to use for signing.
@@ -35,8 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.signXml = signXml;
37
37
  const forge = __importStar(require("node-forge"));
38
+ const xml_crypto_1 = require("xml-crypto");
38
39
  /**
39
- * Signs an XML string with the provided certificate data.
40
+ * Signs an XML string with the provided certificate data using xml-crypto.
40
41
  *
41
42
  * @param xml The XML string to sign.
42
43
  * @param certificateData The certificate data to use for signing.
@@ -45,42 +46,55 @@ const forge = __importStar(require("node-forge"));
45
46
  */
46
47
  function signXml(xml, certificateData, tagToSign) {
47
48
  const { privateKey, certificate } = certificateData;
48
- // 1. Create the digest of the XML
49
- const md = forge.md.sha256.create();
50
- md.update(xml, 'utf8');
51
- const digest = md.digest().bytes();
52
- const digestB64 = forge.util.encode64(digest);
53
- // 2. Define the <SignedInfo> block. This is what actually gets signed.
54
- const signedInfo = `<SignedInfo>
55
- <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
56
- <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
57
- <Reference URI="">
58
- <Transforms>
59
- <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
60
- </Transforms>
61
- <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
62
- <DigestValue>${digestB64}</DigestValue>
63
- </Reference>
64
- </SignedInfo>`;
65
- // 3. Create the signature of the <SignedInfo> block
66
- const signatureMd = forge.md.sha256.create();
67
- signatureMd.update(signedInfo, 'utf8');
68
- const signature = privateKey.sign(signatureMd);
69
- const signatureB64 = forge.util.encode64(signature);
70
- // 4. Extract the certificate in Base64 format
49
+ // Convert keys to PEM format expected by xml-crypto
50
+ const privateKeyPem = forge.pki.privateKeyToPem(privateKey);
71
51
  const certPem = forge.pki.certificateToPem(certificate);
72
- const certB64 = forge.util.encode64(certPem.replace(/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\r|\n/g, ''));
73
- // 5. Assemble the final <Signature> block
74
- const signatureBlock = `<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
75
- ${signedInfo}
76
- <SignatureValue>${signatureB64}</SignatureValue>
77
- <KeyInfo>
78
- <X509Data>
79
- <X509Certificate>${certB64}</X509Certificate>
80
- </X509Data>
81
- </KeyInfo>
82
- </Signature>`;
83
- // 6. Inject the signature block into the original XML
84
- const signedXml = xml.replace(`</${tagToSign}>`, `${signatureBlock}</${tagToSign}>`);
52
+ // Helper to get pure base64 certificate (no headers)
53
+ const getCertB64 = (pem) => {
54
+ return pem.replace(/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\r|\n/g, '');
55
+ };
56
+ const sig = new xml_crypto_1.SignedXml({
57
+ privateKey: privateKeyPem,
58
+ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
59
+ canonicalizationAlgorithm: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
60
+ });
61
+ // Extract ID reference
62
+ // We assume the tagToSign matches the pattern <tagToSign ... Id="XYZ" ...>
63
+ const tagMatch = xml.match(new RegExp(`<${tagToSign}[^>]*Id="([^"]+)"[^>]*>`, 'i'));
64
+ const tagId = tagMatch ? tagMatch[1] : '';
65
+ const referenceUri = tagId ? `#${tagId}` : '';
66
+ if (!tagId) {
67
+ throw new Error(`Could not find Id attribute in tag '${tagToSign}' to sign.`);
68
+ }
69
+ // Add Reference
70
+ sig.addReference({
71
+ xpath: `//*[local-name(.)='${tagToSign}']`,
72
+ transforms: [
73
+ "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
74
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
75
+ ],
76
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
77
+ uri: referenceUri
78
+ });
79
+ // Custom KeyInfo provider
80
+ // Implementing exactly as nfse-brazil-national does to ensure compatibility
81
+ // They overwrite the getKeyInfoContent method on the instance.
82
+ sig.getKeyInfoContent = function ({ prefix }) {
83
+ const certB64 = getCertB64(certPem);
84
+ const p = prefix ? `${prefix}:` : "";
85
+ return `<${p}X509Data><${p}X509Certificate>${certB64}</${p}X509Certificate></${p}X509Data>`;
86
+ };
87
+ // Compute Signature
88
+ // xml-crypto expects the whole XML. By default it appends Signature to root.
89
+ // For DPS, root is <DPS>. <infDPS> is child.
90
+ // So Signature will become a sibling of <infDPS> (child of <DPS>).
91
+ // This matches the requirement "Signature invalid child of infDPS" -> means it must NOT be inside infDPS.
92
+ sig.computeSignature(xml, {
93
+ prefix: "", // No prefix for Signature namespace by default
94
+ attrs: {
95
+ xmlns: "http://www.w3.org/2000/09/xmldsig#"
96
+ }
97
+ });
98
+ let signedXml = sig.getSignedXml();
85
99
  return signedXml;
86
100
  }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { NfseClient } from './core/client';
2
2
  export type { NfseClientConfig } from './core/client';
3
3
  export { loadA1Certificate } from './core/certificate';
4
+ export { buildDpsXml, generateDpsId } from './utils/xml';
5
+ export { signXml } from './core/signer';
4
6
  export type { CertificateData } from './core/certificate';
5
7
  export { enviaDps } from './services/dps';
6
- export { consultarNfseChave, consultarDpsChave } from './services/consultas';
8
+ export { consultarNfseChave, consultarDpsId, verificarNfsePorDpsId } from './services/consultas';
7
9
  export { environmentUrls } from './config/environments';
8
10
  export type { NfseEnvironment } from './config/environments';
9
- export type { DPS, Endereco, Contato, NFSeResponse } from './models/dps';
11
+ export type { DPS, Endereco, Contato, NFSeResponse, DPSResponse } from './models/dps';
10
12
  export type { NFSe, DPSConsultaResponse } from './models/responses';
package/dist/index.js CHANGED
@@ -1,14 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.environmentUrls = exports.consultarDpsChave = exports.consultarNfseChave = exports.enviaDps = exports.loadA1Certificate = exports.NfseClient = void 0;
3
+ exports.environmentUrls = exports.verificarNfsePorDpsId = exports.consultarDpsId = exports.consultarNfseChave = exports.enviaDps = exports.signXml = exports.generateDpsId = exports.buildDpsXml = exports.loadA1Certificate = exports.NfseClient = void 0;
4
4
  var client_1 = require("./core/client");
5
5
  Object.defineProperty(exports, "NfseClient", { enumerable: true, get: function () { return client_1.NfseClient; } });
6
6
  var certificate_1 = require("./core/certificate");
7
7
  Object.defineProperty(exports, "loadA1Certificate", { enumerable: true, get: function () { return certificate_1.loadA1Certificate; } });
8
+ var xml_1 = require("./utils/xml");
9
+ Object.defineProperty(exports, "buildDpsXml", { enumerable: true, get: function () { return xml_1.buildDpsXml; } });
10
+ Object.defineProperty(exports, "generateDpsId", { enumerable: true, get: function () { return xml_1.generateDpsId; } });
11
+ var signer_1 = require("./core/signer");
12
+ Object.defineProperty(exports, "signXml", { enumerable: true, get: function () { return signer_1.signXml; } });
8
13
  var dps_1 = require("./services/dps");
9
14
  Object.defineProperty(exports, "enviaDps", { enumerable: true, get: function () { return dps_1.enviaDps; } });
10
15
  var consultas_1 = require("./services/consultas");
11
16
  Object.defineProperty(exports, "consultarNfseChave", { enumerable: true, get: function () { return consultas_1.consultarNfseChave; } });
12
- Object.defineProperty(exports, "consultarDpsChave", { enumerable: true, get: function () { return consultas_1.consultarDpsChave; } });
17
+ Object.defineProperty(exports, "consultarDpsId", { enumerable: true, get: function () { return consultas_1.consultarDpsId; } });
18
+ Object.defineProperty(exports, "verificarNfsePorDpsId", { enumerable: true, get: function () { return consultas_1.verificarNfsePorDpsId; } });
13
19
  var environments_1 = require("./config/environments");
14
20
  Object.defineProperty(exports, "environmentUrls", { enumerable: true, get: function () { return environments_1.environmentUrls; } });
@@ -15,69 +15,102 @@ export interface Contato {
15
15
  email?: string;
16
16
  }
17
17
  export interface DPS {
18
- identificacao: {
19
- numero: string;
20
- serie: string;
21
- dataEmissao: Date;
22
- competencia: Date;
23
- tipoTributacao: 'T' | 'F' | 'A' | 'B' | 'M' | 'N' | 'X' | 'V' | 'P' | 'S';
24
- };
18
+ id?: string;
19
+ ambiente: 1 | 2;
20
+ versaoAplicacao: string;
21
+ dataEmissao: string | Date;
22
+ competencia: string | Date;
23
+ serie: string;
24
+ numero: string;
25
+ tipoEmitente: number;
26
+ municipioEmissao: string;
25
27
  prestador: {
26
28
  cnpj: string;
29
+ optanteSimplesNacional: 1 | 2 | 3;
30
+ regimeEspecialTributacao?: number;
31
+ regimeApuracaoTributacaoSN?: number;
27
32
  inscricaoMunicipal?: string;
28
- regimeEspecialTributacao?: string;
33
+ telefone?: string;
34
+ email?: string;
29
35
  };
30
36
  tomador: {
31
- identificacao: {
32
- cnpjCpf: string;
33
- inscricaoMunicipal?: string;
37
+ cpf?: string;
38
+ cnpj?: string;
39
+ inscricaoMunicipal?: string;
40
+ nome: string;
41
+ endereco?: {
42
+ logradouro: string;
43
+ numero: string;
44
+ complemento?: string;
45
+ bairro: string;
46
+ codigoMunicipio: string;
47
+ uf: string;
48
+ cep: string;
34
49
  };
35
- razaoSocial: string;
36
- endereco: Endereco;
37
- contato?: Contato;
50
+ telefone?: string;
51
+ email?: string;
38
52
  };
39
53
  servico: {
40
- codigoNbs: string;
41
- discriminacao: string;
42
- valorServicos: number;
54
+ municipioPrestacao: string;
55
+ codigoTributacaoNacional: string;
56
+ codigoTributacaoMunicipal?: string;
57
+ descricao: string;
58
+ codigoNbs?: string;
59
+ codigoInterno?: string;
60
+ paisPrestacao?: string;
61
+ };
62
+ valores: {
63
+ valorServicos: number | string;
64
+ tributacaoIssqn: 1 | 2 | 3;
65
+ tipoRetencaoIssqn: 1 | 2 | 3;
66
+ percentualTotalTributosSN?: number;
67
+ valorIss?: number;
68
+ aliquotaIssqn?: number;
43
69
  valorDeducoes?: number;
44
70
  valorPis?: number;
45
71
  valorCofins?: number;
46
72
  valorInss?: number;
47
73
  valorIr?: number;
48
74
  valorCsll?: number;
49
- issRetido: boolean;
50
- valorIss?: number;
51
- aliquota?: number;
52
- descontoIncondicionado?: number;
53
- descontoCondicionado?: number;
54
- itemListaServico: string;
55
- codigoCnae?: string;
56
- codigoTributacaoMunicipio?: string;
57
- municipioPrestacao: string;
58
- paisPrestacao?: string;
75
+ issRetido?: boolean;
76
+ descontos?: {
77
+ incondicionado?: number;
78
+ condicionado?: number;
79
+ };
80
+ deducaoReducao?: {
81
+ percentual?: number;
82
+ valor?: number;
83
+ };
84
+ tributosDetalhado?: {
85
+ federal: number;
86
+ estadual: number;
87
+ municipal: number;
88
+ };
59
89
  };
90
+ infosComp?: string;
60
91
  construcaoCivil?: {
61
92
  codigoObra?: string;
62
93
  art?: string;
63
- inscricaoImobiliaria?: string;
64
- endereco?: Endereco;
65
- };
66
- atividadesEvento?: {
67
- codigoEvento: string;
68
- nomeEvento: string;
69
- dataInicio: Date;
70
- dataFim: Date;
71
- endereco: Endereco;
72
- };
73
- ibsCbs?: {
74
- situacaoTributaria: string;
75
- classificacaoTributaria: string;
76
- valorAliquota?: number;
77
- valorIBS?: number;
78
- valorCBS?: number;
79
94
  };
80
95
  }
96
+ export interface MensagemProcessamento {
97
+ codigo?: string;
98
+ descricao?: string;
99
+ correcao?: string;
100
+ }
81
101
  export interface NFSeResponse {
102
+ tipoAmbiente: number;
103
+ versaoAplicativo: string;
104
+ dataHoraProcessamento: string;
105
+ idDps: string;
106
+ chaveAcesso: string;
107
+ nfseXmlGZipB64: string;
108
+ alertas?: MensagemProcessamento[];
109
+ }
110
+ export interface DPSResponse {
111
+ tipoAmbiente: number;
112
+ versaoAplicativo: string;
113
+ dataHoraProcessamento: string;
114
+ idDps: string;
82
115
  chaveAcesso: string;
83
116
  }
@@ -1,5 +1,5 @@
1
1
  import { NfseClient } from '../core/client';
2
- import { NFSe, DPSConsultaResponse } from '../models/responses';
2
+ import { NFSeResponse, DPSResponse } from '../models/dps';
3
3
  /**
4
4
  * Consults an NFSe by its access key.
5
5
  *
@@ -7,12 +7,20 @@ import { NFSe, DPSConsultaResponse } from '../models/responses';
7
7
  * @param client An instance of the configured NfseClient.
8
8
  * @returns A promise that resolves with the NFSe data.
9
9
  */
10
- export declare function consultarNfseChave(chaveAcesso: string, client: NfseClient): Promise<NFSe>;
10
+ export declare function consultarNfseChave(chaveAcesso: string, client: NfseClient): Promise<NFSeResponse>;
11
11
  /**
12
- * Consults a DPS by its access key.
12
+ * Consults a DPS by its Id.
13
13
  *
14
- * @param chaveAcesso The access key of the DPS.
14
+ * @param id The identifier of the DPS.
15
15
  * @param client An instance of the configured NfseClient.
16
- * @returns A promise that resolves with the DPS consultation data.
16
+ * @returns A promise that resolves with the DPS response data.
17
17
  */
18
- export declare function consultarDpsChave(chaveAcesso: string, client: NfseClient): Promise<DPSConsultaResponse>;
18
+ export declare function consultarDpsId(id: string, client: NfseClient): Promise<DPSResponse>;
19
+ /**
20
+ * Verifies if an NFSe was emitted from a DPS Id.
21
+ *
22
+ * @param id The identifier of the DPS.
23
+ * @param client An instance of the configured NfseClient.
24
+ * @returns A promise that resolves to true if the NFSe exists, false otherwise.
25
+ */
26
+ export declare function verificarNfsePorDpsId(id: string, client: NfseClient): Promise<boolean>;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.consultarNfseChave = consultarNfseChave;
4
- exports.consultarDpsChave = consultarDpsChave;
5
- const fast_xml_parser_1 = require("fast-xml-parser");
4
+ exports.consultarDpsId = consultarDpsId;
5
+ exports.verificarNfsePorDpsId = verificarNfsePorDpsId;
6
6
  /**
7
7
  * Consults an NFSe by its access key.
8
8
  *
@@ -11,40 +11,40 @@ const fast_xml_parser_1 = require("fast-xml-parser");
11
11
  * @returns A promise that resolves with the NFSe data.
12
12
  */
13
13
  async function consultarNfseChave(chaveAcesso, client) {
14
- var _a;
15
- const xmlPayload = `<ConsultarNfse>
16
- <chaveAcesso>${chaveAcesso}</chaveAcesso>
17
- </ConsultarNfse>`;
18
- // Placeholder for the actual API endpoint
19
- const responseXml = await client.post('/ws/consultarNfse', xmlPayload);
20
- const parser = new fast_xml_parser_1.XMLParser();
21
- const parsedResponse = parser.parse(responseXml);
22
- // The exact path to the data needs to be verified against the API's response
23
- const nfseData = (_a = parsedResponse === null || parsedResponse === void 0 ? void 0 : parsedResponse.retornoConsultaNfse) === null || _a === void 0 ? void 0 : _a.nfse;
24
- if (!nfseData) {
25
- throw new Error('Could not find NFSe data in the API response.');
26
- }
27
- return nfseData;
14
+ // Use GET /nfse/{chaveAcesso} as per the documentation
15
+ const response = await client.get(`/nfse/${chaveAcesso}`);
16
+ return response;
28
17
  }
29
18
  /**
30
- * Consults a DPS by its access key.
19
+ * Consults a DPS by its Id.
31
20
  *
32
- * @param chaveAcesso The access key of the DPS.
21
+ * @param id The identifier of the DPS.
33
22
  * @param client An instance of the configured NfseClient.
34
- * @returns A promise that resolves with the DPS consultation data.
23
+ * @returns A promise that resolves with the DPS response data.
35
24
  */
36
- async function consultarDpsChave(chaveAcesso, client) {
37
- const xmlPayload = `<ConsultarDps>
38
- <chaveAcesso>${chaveAcesso}</chaveAcesso>
39
- </ConsultarDps>`;
40
- // Placeholder for the actual API endpoint
41
- const responseXml = await client.post('/ws/consultarDps', xmlPayload);
42
- const parser = new fast_xml_parser_1.XMLParser();
43
- const parsedResponse = parser.parse(responseXml);
44
- // The exact path to the data needs to be verified against the API's response
45
- const dpsData = parsedResponse === null || parsedResponse === void 0 ? void 0 : parsedResponse.retornoConsultaDps;
46
- if (!dpsData) {
47
- throw new Error('Could not find DPS data in the API response.');
25
+ async function consultarDpsId(id, client) {
26
+ const response = await client.get(`/dps/${id}`);
27
+ return response;
28
+ }
29
+ /**
30
+ * Verifies if an NFSe was emitted from a DPS Id.
31
+ *
32
+ * @param id The identifier of the DPS.
33
+ * @param client An instance of the configured NfseClient.
34
+ * @returns A promise that resolves to true if the NFSe exists, false otherwise.
35
+ */
36
+ async function verificarNfsePorDpsId(id, client) {
37
+ try {
38
+ // Verified by HEAD /nfse?id={id} according to some interpretations,
39
+ // but the doc says "HEAD /nfse" path parameters or query?
40
+ // doc: head /nfse, path parameters id required string
41
+ await client.head(`/nfse/${id}`);
42
+ return true;
43
+ }
44
+ catch (error) {
45
+ if (error.message.includes('status 404')) {
46
+ return false;
47
+ }
48
+ throw error;
48
49
  }
49
- return dpsData;
50
50
  }
@@ -4,7 +4,6 @@ exports.enviaDps = enviaDps;
4
4
  const xml_1 = require("../utils/xml");
5
5
  const signer_1 = require("../core/signer");
6
6
  const compression_1 = require("../utils/compression");
7
- const fast_xml_parser_1 = require("fast-xml-parser");
8
7
  /**
9
8
  * Orchestrates the process of sending a DPS to the NFSe Nacional API.
10
9
  *
@@ -15,33 +14,18 @@ const fast_xml_parser_1 = require("fast-xml-parser");
15
14
  * @throws Will throw an error if any step of the process fails.
16
15
  */
17
16
  async function enviaDps(dps, client, certificate) {
18
- var _a;
19
17
  try {
20
18
  // 1. Build the DPS XML from the object
21
19
  const dpsXml = (0, xml_1.buildDpsXml)(dps);
22
20
  // 2. Sign the DPS XML document
23
- // The signature must be applied to the <DPS> tag
24
- const signedDpsXml = (0, signer_1.signXml)(dpsXml, certificate, 'DPS');
21
+ // The signature must be applied to the <infDPS> tag
22
+ const signedDpsXml = (0, signer_1.signXml)(dpsXml, certificate, 'infDPS');
25
23
  // 3. Compress and Base64 encode the signed XML
26
- const compressedPayload = await (0, compression_1.compressAndEncodeXml)(signedDpsXml);
27
- // 4. Wrap the payload in the required structure for the API
28
- const finalXml = `<EnviarDeclaracaoServico><dps_compactado>${compressedPayload}</dps_compactado></EnviarDeclaracaoServico>`;
29
- // 5. Send the request using the mTLS client
30
- // The specific endpoint path needs to be confirmed from the official documentation,
31
- // '/ws/enviarDps' is a placeholder based on common patterns.
32
- const responseXml = await client.post('/ws/enviarDps', finalXml);
33
- // 6. Parse the XML response
34
- const parser = new fast_xml_parser_1.XMLParser();
35
- const parsedResponse = parser.parse(responseXml);
36
- // The exact path to the response data needs to be verified against the API's actual response structure.
37
- const chaveAcesso = (_a = parsedResponse === null || parsedResponse === void 0 ? void 0 : parsedResponse.retornoEnvioDps) === null || _a === void 0 ? void 0 : _a.chaveAcesso;
38
- if (!chaveAcesso) {
39
- throw new Error('Could not find chaveAcesso in the API response.');
40
- }
41
- return {
42
- chaveAcesso,
43
- ...parsedResponse.retornoEnvioDps,
44
- };
24
+ const dpsXmlGZipB64 = await (0, compression_1.compressAndEncodeXml)(signedDpsXml);
25
+ // 4. Send the request using the mTLS client
26
+ // Updated to use the synchronous JSON API endpoint
27
+ const response = await client.post('/nfse', { dpsXmlGZipB64 });
28
+ return response;
45
29
  }
46
30
  catch (error) {
47
31
  console.error('Failed to send DPS:', error);
@@ -1,10 +1,11 @@
1
1
  import { DPS } from '../models/dps';
2
+ /**
3
+ * Generates the DPS ID according to the pattern:
4
+ * "DPS" + Cód.Mun (7) + Tipo de Inscrição Federal (1) + Inscrição Federal (14) + Série DPS (5) + Núm. DPS (15)
5
+ */
6
+ export declare function generateDpsId(dps: DPS): string;
2
7
  /**
3
8
  * Converts a DPS object to its XML string representation.
4
- * Note: This is a simplified builder for the MVP. A more robust XML library
5
- * might be used in the future for better maintainability.
6
- *
7
- * @param dps The DPS object.
8
- * @returns The XML string representation of the DPS.
9
+ * Matches the structure of the nfse-brazil-national template.
9
10
  */
10
11
  export declare function buildDpsXml(dps: DPS): string;
package/dist/utils/xml.js CHANGED
@@ -1,66 +1,156 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDpsId = generateDpsId;
3
4
  exports.buildDpsXml = buildDpsXml;
4
5
  const date_fns_1 = require("date-fns");
6
+ /**
7
+ * Generates the DPS ID according to the pattern:
8
+ * "DPS" + Cód.Mun (7) + Tipo de Inscrição Federal (1) + Inscrição Federal (14) + Série DPS (5) + Núm. DPS (15)
9
+ */
10
+ function generateDpsId(dps) {
11
+ const codMun = dps.municipioEmissao.padStart(7, '0');
12
+ const tpInsc = '2'; // 2=CNPJ (Fixed for now as we only support CNPJ prestador)
13
+ const cnpj = dps.prestador.cnpj.replace(/\D/g, '').padStart(14, '0');
14
+ const serie = dps.serie.padStart(5, '0');
15
+ const numero = dps.numero.padStart(15, '0');
16
+ return `DPS${codMun}${tpInsc}${cnpj}${serie}${numero}`;
17
+ }
5
18
  /**
6
19
  * Converts a DPS object to its XML string representation.
7
- * Note: This is a simplified builder for the MVP. A more robust XML library
8
- * might be used in the future for better maintainability.
9
- *
10
- * @param dps The DPS object.
11
- * @returns The XML string representation of the DPS.
20
+ * Matches the structure of the nfse-brazil-national template.
12
21
  */
13
22
  function buildDpsXml(dps) {
14
- // Helper to format dates to 'yyyy-MM-dd'
15
- const formatDate = (date) => (0, date_fns_1.format)(date, 'yyyy-MM-dd');
23
+ var _a;
24
+ // Helper to format dates to 'yyyy-MM-dd' or ISO date-time
25
+ const formatDate = (date) => {
26
+ if (date instanceof Date)
27
+ return (0, date_fns_1.format)(date, 'yyyy-MM-dd');
28
+ return date.split('T')[0]; // simple handling for ISO string
29
+ };
30
+ const formatDateTime = (date) => {
31
+ if (typeof date === 'string')
32
+ return date;
33
+ // Format: YYYY-MM-DDThh:mm:ss-03:00
34
+ const pad = (n) => n.toString().padStart(2, '0');
35
+ const yyyy = date.getFullYear();
36
+ const mm = pad(date.getMonth() + 1);
37
+ const dd = pad(date.getDate());
38
+ const hh = pad(date.getHours());
39
+ const min = pad(date.getMinutes());
40
+ const ss = pad(date.getSeconds());
41
+ // Offset handling could be improved, but for "homologacao" usually -03:00 is expected/safe
42
+ return `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}-03:00`;
43
+ };
16
44
  // Helper to safely add optional elements
17
45
  const addOptional = (tag, value) => (value ? `<${tag}>${value}</${tag}>` : '');
18
- const addOptionalFloat = (tag, value) => (value ? `<${tag}>${value.toFixed(2)}</${tag}>` : '');
19
- return `<?xml version="1.0" encoding="UTF-8"?>
20
- <DPS xmlns="http://www.nfse.gov.br/nfse">
21
- <identificacao>
22
- <numero>${dps.identificacao.numero}</numero>
23
- <serie>${dps.identificacao.serie}</serie>
24
- <dataEmissao>${formatDate(dps.identificacao.dataEmissao)}</dataEmissao>
25
- <competencia>${formatDate(dps.identificacao.competencia)}</competencia>
26
- <tipoTributacao>${dps.identificacao.tipoTributacao}</tipoTributacao>
27
- </identificacao>
28
- <prestador>
29
- <cnpj>${dps.prestador.cnpj}</cnpj>
30
- ${addOptional('inscricaoMunicipal', dps.prestador.inscricaoMunicipal)}
31
- ${addOptional('regimeEspecialTributacao', dps.prestador.regimeEspecialTributacao)}
32
- </prestador>
33
- <tomador>
34
- <identificacao>
35
- <cnpjCpf>${dps.tomador.identificacao.cnpjCpf}</cnpjCpf>
36
- ${addOptional('inscricaoMunicipal', dps.tomador.identificacao.inscricaoMunicipal)}
37
- </identificacao>
38
- <razaoSocial>${dps.tomador.razaoSocial}</razaoSocial>
39
- <endereco>
40
- <logradouro>${dps.tomador.endereco.logradouro}</logradouro>
41
- <numero>${dps.tomador.endereco.numero}</numero>
42
- ${addOptional('complemento', dps.tomador.endereco.complemento)}
43
- <bairro>${dps.tomador.endereco.bairro}</bairro>
44
- <codigoMunicipio>${dps.tomador.endereco.codigoMunicipio}</codigoMunicipio>
45
- <uf>${dps.tomador.endereco.uf}</uf>
46
- <cep>${dps.tomador.endereco.cep}</cep>
47
- </endereco>
48
- </tomador>
49
- <servico>
50
- <codigoNbs>${dps.servico.codigoNbs}</codigoNbs>
51
- <discriminacao>${dps.servico.discriminacao}</discriminacao>
52
- <valorServicos>${dps.servico.valorServicos.toFixed(2)}</valorServicos>
53
- ${addOptionalFloat('valorDeducoes', dps.servico.valorDeducoes)}
54
- ${addOptionalFloat('valorPis', dps.servico.valorPis)}
55
- ${addOptionalFloat('valorCofins', dps.servico.valorCofins)}
56
- ${addOptionalFloat('valorInss', dps.servico.valorInss)}
57
- ${addOptionalFloat('valorIr', dps.servico.valorIr)}
58
- ${addOptionalFloat('valorCsll', dps.servico.valorCsll)}
59
- <issRetido>${dps.servico.issRetido}</issRetido>
60
- ${addOptionalFloat('valorIss', dps.servico.valorIss)}
61
- ${addOptionalFloat('aliquota', dps.servico.aliquota)}
62
- <itemListaServico>${dps.servico.itemListaServico}</itemListaServico>
63
- <municipioPrestacao>${dps.servico.municipioPrestacao}</municipioPrestacao>
64
- </servico>
46
+ const addOptionalFloat = (tag, value) => (value !== undefined && value !== null ? `<${tag}>${value}</${tag}>` : '');
47
+ const dpsId = dps.id || generateDpsId(dps);
48
+ // Determine tomador identification tag (CNPJ or CPF)
49
+ const tomadorIdTag = dps.tomador.cnpj ?
50
+ `<CNPJ>${dps.tomador.cnpj.replace(/\D/g, '')}</CNPJ>` :
51
+ (dps.tomador.cpf ? `<CPF>${dps.tomador.cpf.replace(/\D/g, '')}</CPF>` : '');
52
+ // Determine discounts/deductions blocks
53
+ let valoresHtml = `<vServPrest><vServ>${dps.valores.valorServicos}</vServ></vServPrest>`;
54
+ if (dps.valores.descontos) {
55
+ valoresHtml += `<vDescCondIncond>`;
56
+ if (dps.valores.descontos.incondicionado)
57
+ valoresHtml += `<vDescIncond>${dps.valores.descontos.incondicionado}</vDescIncond>`;
58
+ if (dps.valores.descontos.condicionado)
59
+ valoresHtml += `<vDescCond>${dps.valores.descontos.condicionado}</vDescCond>`;
60
+ valoresHtml += `</vDescCondIncond>`;
61
+ }
62
+ if (dps.valores.deducaoReducao) {
63
+ valoresHtml += `<vDedRed>`;
64
+ if (dps.valores.deducaoReducao.percentual)
65
+ valoresHtml += `<pDR>${dps.valores.deducaoReducao.percentual}</pDR>`;
66
+ if (dps.valores.deducaoReducao.valor)
67
+ valoresHtml += `<vDR>${dps.valores.deducaoReducao.valor}</vDR>`;
68
+ valoresHtml += `</vDedRed>`;
69
+ }
70
+ let tribMunHtml = `
71
+ <tribISSQN>${dps.valores.tributacaoIssqn}</tribISSQN>
72
+ <tpRetISSQN>${dps.valores.tipoRetencaoIssqn}</tpRetISSQN>`;
73
+ if (dps.valores.aliquotaIssqn)
74
+ tribMunHtml += `<pAliq>${dps.valores.aliquotaIssqn}</pAliq>`;
75
+ if (dps.valores.valorIss)
76
+ tribMunHtml += `<vISSQN>${dps.valores.valorIss}</vISSQN>`;
77
+ let totTribHtml = '';
78
+ if (dps.valores.tributosDetalhado) {
79
+ totTribHtml = `<pTotTrib>
80
+ <pTotTribFed>${dps.valores.tributosDetalhado.federal}</pTotTribFed>
81
+ <pTotTribEst>${dps.valores.tributosDetalhado.estadual}</pTotTribEst>
82
+ <pTotTribMun>${dps.valores.tributosDetalhado.municipal}</pTotTribMun>
83
+ </pTotTrib>`;
84
+ }
85
+ else if (dps.valores.percentualTotalTributosSN !== undefined) {
86
+ totTribHtml = `<pTotTribSN>${dps.valores.percentualTotalTributosSN}</pTotTribSN>`;
87
+ }
88
+ else {
89
+ totTribHtml = `<indTotTrib>0</indTotTrib>`;
90
+ }
91
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
92
+ <DPS xmlns="http://www.sped.fazenda.gov.br/nfse" versao="1.00">
93
+ <infDPS Id="${dpsId}">
94
+ <tpAmb>${dps.ambiente}</tpAmb>
95
+ <dhEmi>${formatDateTime(dps.dataEmissao)}</dhEmi>
96
+ <verAplic>${dps.versaoAplicacao}</verAplic>
97
+ <serie>${dps.serie}</serie>
98
+ <nDPS>${dps.numero}</nDPS>
99
+ <dCompet>${formatDate(dps.competencia)}</dCompet>
100
+ <tpEmit>${dps.tipoEmitente}</tpEmit>
101
+ <cLocEmi>${dps.municipioEmissao}</cLocEmi>
102
+ <prest>
103
+ <CNPJ>${dps.prestador.cnpj.replace(/\D/g, '')}</CNPJ>
104
+ ${addOptional('fone', dps.prestador.telefone)}
105
+ <regTrib>
106
+ <opSimpNac>${dps.prestador.optanteSimplesNacional}</opSimpNac>
107
+ ${addOptional('regApTribSN', dps.prestador.regimeApuracaoTributacaoSN)}
108
+ <regEspTrib>${(_a = dps.prestador.regimeEspecialTributacao) !== null && _a !== void 0 ? _a : 0}</regEspTrib>
109
+ </regTrib>
110
+ </prest>
111
+ <toma>
112
+ ${tomadorIdTag}
113
+ ${addOptional('IM', dps.tomador.inscricaoMunicipal)}
114
+ <xNome>${dps.tomador.nome}</xNome>
115
+ ${dps.tomador.endereco ? `<end>
116
+ ${dps.tomador.endereco.cep ? `<endNac>
117
+ <cMun>${dps.tomador.endereco.codigoMunicipio}</cMun>
118
+ <CEP>${dps.tomador.endereco.cep.replace(/\D/g, '')}</CEP>
119
+ </endNac>` : ''}
120
+ <xLgr>${dps.tomador.endereco.logradouro}</xLgr>
121
+ <nro>${dps.tomador.endereco.numero}</nro>
122
+ ${addOptional('xCpl', dps.tomador.endereco.complemento)}
123
+ <xBairro>${dps.tomador.endereco.bairro}</xBairro>
124
+ </end>` : ''}
125
+ ${addOptional('fone', dps.tomador.telefone)}
126
+ ${addOptional('email', dps.tomador.email)}
127
+ </toma>
128
+ <serv>
129
+ <locPrest>
130
+ <cLocPrestacao>${dps.servico.municipioPrestacao}</cLocPrestacao>
131
+ ${addOptional('cPaisPrestacao', dps.servico.paisPrestacao)}
132
+ </locPrest>
133
+ <cServ>
134
+ <cTribNac>${dps.servico.codigoTributacaoNacional}</cTribNac>
135
+ ${addOptional('cTribMun', dps.servico.codigoTributacaoMunicipal)}
136
+ <xDescServ>${dps.servico.descricao}</xDescServ>
137
+ ${addOptional('cNBS', dps.servico.codigoNbs ? dps.servico.codigoNbs.replace(/\D/g, '') : undefined)}
138
+ ${addOptional('cIntContrib', dps.servico.codigoInterno)}
139
+ </cServ>
140
+ </serv>
141
+ <valores>
142
+ ${valoresHtml}
143
+ <trib>
144
+ <tribMun>
145
+ ${tribMunHtml}
146
+ </tribMun>
147
+ <totTrib>
148
+ ${totTribHtml}
149
+ </totTrib>
150
+ </trib>
151
+ </valores>
152
+ </infDPS>
65
153
  </DPS>`;
154
+ // Minify XML to remove spaces between tags
155
+ return xml.replace(/>\s+</g, '><').trim();
66
156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pereirajair/nfse-gov-br-node",
3
- "version": "0.0.3",
3
+ "version": "0.3.0",
4
4
  "description": "Library for interacting with the Brazilian NFSe Nacional API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,20 +9,25 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "build": "tsc -p tsconfig.build.json",
12
- "test": "jest"
12
+ "test": "jest",
13
+ "ship:minor": "git add . && git commit -m \"feat: atualização de rotina\" && npm version minor && git push --follow-tags"
13
14
  },
14
15
  "devDependencies": {
15
16
  "@types/jest": "^30.0.0",
16
17
  "@types/node": "^25.0.10",
17
18
  "@types/node-forge": "^1.3.14",
19
+ "@types/xml-crypto": "^1.4.6",
20
+ "@types/xmldom": "^0.1.34",
18
21
  "ts-jest": "^29.4.6",
19
22
  "typescript": "^5.9.3"
20
23
  },
21
24
  "dependencies": {
25
+ "@xmldom/xmldom": "^0.8.11",
22
26
  "axios": "^1.13.4",
23
27
  "date-fns": "^2.30.0",
24
28
  "fast-xml-parser": "^4.5.3",
25
29
  "node-forge": "^1.3.3",
30
+ "xml-crypto": "^6.1.2",
26
31
  "zod": "^3.25.76"
27
32
  }
28
33
  }