@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 +90 -0
- package/dist/config/environments.js +2 -2
- package/dist/core/certificate.js +28 -21
- package/dist/core/client.d.ts +18 -3
- package/dist/core/client.js +48 -6
- package/dist/core/signer.d.ts +1 -1
- package/dist/core/signer.js +51 -37
- package/dist/index.d.ts +4 -2
- package/dist/index.js +8 -2
- package/dist/models/dps.d.ts +76 -43
- package/dist/services/consultas.d.ts +14 -6
- package/dist/services/consultas.js +32 -32
- package/dist/services/dps.js +7 -23
- package/dist/utils/xml.d.ts +6 -5
- package/dist/utils/xml.js +144 -54
- package/package.json +7 -2
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://
|
|
9
|
-
homologacao: 'https://
|
|
8
|
+
producao: 'https://sefin.nfse.gov.br/SefinNacional',
|
|
9
|
+
homologacao: 'https://sefin.producaorestrita.nfse.gov.br/SefinNacional',
|
|
10
10
|
};
|
package/dist/core/certificate.js
CHANGED
|
@@ -50,29 +50,32 @@ function loadA1Certificate(pfxBuffer, password) {
|
|
|
50
50
|
let privateKey = null;
|
|
51
51
|
let certificate = null;
|
|
52
52
|
const caCertificates = [];
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
75
|
-
throw new Error('Could not find private key
|
|
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
|
}
|
package/dist/core/client.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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,
|
|
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
|
}
|
package/dist/core/client.js
CHANGED
|
@@ -66,20 +66,21 @@ class NfseClient {
|
|
|
66
66
|
httpsAgent,
|
|
67
67
|
timeout,
|
|
68
68
|
headers: {
|
|
69
|
-
'Content-Type': 'application/
|
|
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
|
|
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
|
|
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,
|
|
81
|
+
async post(path, data) {
|
|
81
82
|
try {
|
|
82
|
-
const response = await this.client.post(path,
|
|
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
|
}
|
package/dist/core/signer.d.ts
CHANGED
|
@@ -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.
|
package/dist/core/signer.js
CHANGED
|
@@ -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
|
-
//
|
|
49
|
-
const
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
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,
|
|
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.
|
|
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, "
|
|
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; } });
|
package/dist/models/dps.d.ts
CHANGED
|
@@ -15,69 +15,102 @@ export interface Contato {
|
|
|
15
15
|
email?: string;
|
|
16
16
|
}
|
|
17
17
|
export interface DPS {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
33
|
+
telefone?: string;
|
|
34
|
+
email?: string;
|
|
29
35
|
};
|
|
30
36
|
tomador: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
contato?: Contato;
|
|
50
|
+
telefone?: string;
|
|
51
|
+
email?: string;
|
|
38
52
|
};
|
|
39
53
|
servico: {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 {
|
|
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<
|
|
10
|
+
export declare function consultarNfseChave(chaveAcesso: string, client: NfseClient): Promise<NFSeResponse>;
|
|
11
11
|
/**
|
|
12
|
-
* Consults a DPS by its
|
|
12
|
+
* Consults a DPS by its Id.
|
|
13
13
|
*
|
|
14
|
-
* @param
|
|
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
|
|
16
|
+
* @returns A promise that resolves with the DPS response data.
|
|
17
17
|
*/
|
|
18
|
-
export declare function
|
|
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.
|
|
5
|
-
|
|
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
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
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
|
|
19
|
+
* Consults a DPS by its Id.
|
|
31
20
|
*
|
|
32
|
-
* @param
|
|
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
|
|
23
|
+
* @returns A promise that resolves with the DPS response data.
|
|
35
24
|
*/
|
|
36
|
-
async function
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
}
|
package/dist/services/dps.js
CHANGED
|
@@ -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 <
|
|
24
|
-
const signedDpsXml = (0, signer_1.signXml)(dpsXml, certificate, '
|
|
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
|
|
27
|
-
// 4.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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);
|
package/dist/utils/xml.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
15
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
+
"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
|
}
|