consultame-sdk 1.0.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.
Files changed (50) hide show
  1. package/README.md +585 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +324 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +298 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/shared/dto/responseDigitalSign.dto.d.ts +5 -0
  9. package/dist/shared/dto/responseDigitalSign.dto.d.ts.map +1 -0
  10. package/dist/shared/functions/functions.utils.d.ts +10 -0
  11. package/dist/shared/functions/functions.utils.d.ts.map +1 -0
  12. package/dist/shared/utils/dictionary.utils.d.ts +9 -0
  13. package/dist/shared/utils/dictionary.utils.d.ts.map +1 -0
  14. package/dist/sifen/applications/dtos/cancelDocument.dto.d.ts +6 -0
  15. package/dist/sifen/applications/dtos/cancelDocument.dto.d.ts.map +1 -0
  16. package/dist/sifen/applications/dtos/consultBatch.dto.d.ts +5 -0
  17. package/dist/sifen/applications/dtos/consultBatch.dto.d.ts.map +1 -0
  18. package/dist/sifen/applications/dtos/consultDocument.dto.d.ts +6 -0
  19. package/dist/sifen/applications/dtos/consultDocument.dto.d.ts.map +1 -0
  20. package/dist/sifen/applications/dtos/consultRuc.dto.d.ts +5 -0
  21. package/dist/sifen/applications/dtos/consultRuc.dto.d.ts.map +1 -0
  22. package/dist/sifen/applications/dtos/digitalSign.dto.d.ts +5 -0
  23. package/dist/sifen/applications/dtos/digitalSign.dto.d.ts.map +1 -0
  24. package/dist/sifen/applications/dtos/generateId.dto.d.ts +4 -0
  25. package/dist/sifen/applications/dtos/generateId.dto.d.ts.map +1 -0
  26. package/dist/sifen/applications/dtos/generationQr.dto.d.ts +7 -0
  27. package/dist/sifen/applications/dtos/generationQr.dto.d.ts.map +1 -0
  28. package/dist/sifen/applications/dtos/index.d.ts +6 -0
  29. package/dist/sifen/applications/dtos/index.d.ts.map +1 -0
  30. package/dist/sifen/applications/dtos/invalidationInvoice.dto.d.ts +11 -0
  31. package/dist/sifen/applications/dtos/invalidationInvoice.dto.d.ts.map +1 -0
  32. package/dist/sifen/applications/services/sifen.service.d.ts +16 -0
  33. package/dist/sifen/applications/services/sifen.service.d.ts.map +1 -0
  34. package/dist/sifen/domain/repository/sifen.repository.d.ts +14 -0
  35. package/dist/sifen/domain/repository/sifen.repository.d.ts.map +1 -0
  36. package/dist/sifen/domain/useCases/consultBatch.useCase.d.ts +8 -0
  37. package/dist/sifen/domain/useCases/consultBatch.useCase.d.ts.map +1 -0
  38. package/dist/sifen/domain/useCases/consultDocument.useCase.d.ts +8 -0
  39. package/dist/sifen/domain/useCases/consultDocument.useCase.d.ts.map +1 -0
  40. package/dist/sifen/domain/useCases/consultRuc.useCase.d.ts +8 -0
  41. package/dist/sifen/domain/useCases/consultRuc.useCase.d.ts.map +1 -0
  42. package/dist/sifen/domain/useCases/generateQr.useCase.d.ts +8 -0
  43. package/dist/sifen/domain/useCases/generateQr.useCase.d.ts.map +1 -0
  44. package/dist/sifen/domain/useCases/index.d.ts +6 -0
  45. package/dist/sifen/domain/useCases/index.d.ts.map +1 -0
  46. package/dist/sifen/domain/useCases/obtainDigitalSign.useCase.d.ts +8 -0
  47. package/dist/sifen/domain/useCases/obtainDigitalSign.useCase.d.ts.map +1 -0
  48. package/dist/sifen/infrastructure/persistence/sifen.persistence.d.ts +15 -0
  49. package/dist/sifen/infrastructure/persistence/sifen.persistence.d.ts.map +1 -0
  50. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,585 @@
1
+ # consultame-sdk
2
+
3
+ Librería en **TypeScript** para generar los mensajes XML (SOAP) e insumos criptográficos
4
+ que se necesitan para interactuar con **SIFEN** (Sistema Integrado de Facturación Electrónica
5
+ Nacional de Paraguay – SET).
6
+
7
+ La librería **no** hace las peticiones HTTP al web service de SIFEN: su responsabilidad es
8
+ **construir los sobres XML** (consulta de RUC, consulta de lote, consulta de documento),
9
+ **extraer el certificado y la llave privada** desde un archivo de firma digital `.p12`/`.pfx`
10
+ y **generar el código QR** que se incrusta en el documento electrónico firmado.
11
+ Con esa salida, la aplicación consumidora firma y envía los mensajes por su cuenta.
12
+
13
+ ---
14
+
15
+ ## Tabla de contenido
16
+
17
+ - [Instalación](#instalación)
18
+ - [Uso rápido](#uso-rápido)
19
+ - [API pública](#api-pública)
20
+ - [`consultRuc`](#consultrucconsultrucdto)
21
+ - [`consultDocument`](#consultdocumentconsultdocumentdto)
22
+ - [`consultBatch`](#consultbatchconsultbatchdto)
23
+ - [`obtainDigitalSign`](#obtaindigitalsigndigitalsigndto)
24
+ - [`generateQr`](#generateqrgenerationqrdto)
25
+ - [DTOs (contratos de entrada)](#dtos-contratos-de-entrada)
26
+ - [Funciones utilitarias internas](#funciones-utilitarias-internas)
27
+ - [Arquitectura del proyecto](#arquitectura-del-proyecto)
28
+ - [Estructura de carpetas](#estructura-de-carpetas)
29
+ - [Scripts de desarrollo](#scripts-de-desarrollo)
30
+ - [Estado / roadmap](#estado--roadmap)
31
+
32
+ ---
33
+
34
+ ## Instalación
35
+
36
+ ```bash
37
+ pnpm add consultame-sdk
38
+ # o
39
+ npm install consultame-sdk
40
+ ```
41
+
42
+ Dependencias en tiempo de ejecución:
43
+
44
+ | Paquete | Para qué se usa |
45
+ | -------------------------------------------------------- | ------------------------------------------------------------ |
46
+ | [`node-forge`](https://www.npmjs.com/package/node-forge) | Leer los certificados `.p12`/`.pfx` y exportarlos a PEM. |
47
+ | [`xml2js`](https://www.npmjs.com/package/xml2js) | Parsear y reconstruir el XML del documento al generar el QR. |
48
+ | [`crypto-js`](https://www.npmjs.com/package/crypto-js) | Calcular el hash SHA-256 (`cHashQR`) del código QR. |
49
+
50
+ La librería se publica en formato dual **CommonJS + ESM** con tipos incluidos:
51
+
52
+ ```ts
53
+ // ESM
54
+ import { SifenService } from "consultame-sdk";
55
+
56
+ // CommonJS
57
+ const { SifenService } = require("consultame-sdk");
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Uso rápido
63
+
64
+ Todo el punto de entrada público es la clase **`SifenService`**. Se instancia sin
65
+ argumentos y ya arma internamente sus casos de uso y dependencias.
66
+
67
+ ```ts
68
+ import { SifenService } from "consultame-skd";
69
+
70
+ const sifen = new SifenService();
71
+
72
+ // 1) Consultar un RUC → devuelve el XML SOAP listo, normalizado en una sola línea
73
+ const xmlRuc = sifen.consultRuc({
74
+ id: 1,
75
+ ruc: "80012345-6",
76
+ });
77
+
78
+ // 2) Consultar un documento electrónico por su CDC
79
+ const xmlDocumento = sifen.consultDocument({
80
+ id: 2,
81
+ cdc: "01800123456001001000000012023052212345678901",
82
+ });
83
+
84
+ // 3) Consultar el estado de un lote enviado
85
+ const xmlLote = sifen.consultBatch({
86
+ id: 3,
87
+ codigoLote: "1234567890",
88
+ });
89
+
90
+ // 4) Extraer certificado + llave privada desde la firma digital (.p12)
91
+ const firma = sifen.obtainDigitalSign({
92
+ camino: "./certificados/firma.p12",
93
+ contraseña: "mi-clave-secreta",
94
+ });
95
+
96
+ console.log(firma.certificado); // PEM del certificado
97
+ console.log(firma.llavePrivada); // PEM de la llave privada
98
+
99
+ // 5) Generar el QR e incrustarlo en un documento electrónico ya firmado (asíncrono)
100
+ const xmlConQr = await sifen.generateQr({
101
+ xml: xmlFirmado, // XML del rDE ya firmado digitalmente
102
+ csc: "ABCD0000000000000000000000000000",
103
+ idCsc: "0001",
104
+ ambiente: "test", // "test" | "produccion"
105
+ });
106
+ ```
107
+
108
+ ---
109
+
110
+ ## API pública
111
+
112
+ Todos los métodos viven en la clase `SifenService`. Son **síncronos**, salvo
113
+ [`generateQr`](#generateqrgenerationqrdto), que es **asíncrono** y devuelve una `Promise`.
114
+
115
+ ### `consultRuc(consultRucDto)`
116
+
117
+ Genera el sobre SOAP `rEnviConsRUC` para consultar los datos de un contribuyente por RUC.
118
+
119
+ | Parámetro | Tipo | Descripción |
120
+ | --------- | -------- | ------------------------------------- |
121
+ | `id` | `number` | Identificador de la petición (`dId`). |
122
+ | `ruc` | `string` | RUC del contribuyente a consultar. |
123
+
124
+ **Retorna:** `string` — XML SOAP normalizado (sin saltos de línea ni tabulaciones).
125
+
126
+ ```ts
127
+ const xml = sifen.consultRuc({ id: 1, ruc: "80012345-6" });
128
+ ```
129
+
130
+ <details>
131
+ <summary>Ejemplo de salida (formateada para lectura)</summary>
132
+
133
+ ```xml
134
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
135
+ <env:Header/>
136
+ <env:Body>
137
+ <rEnviConsRUC xmlns="http://ekuatia.set.gov.py/sifen/xsd">
138
+ <dId>1</dId>
139
+ <dRUCCons>80012345-6</dRUCCons>
140
+ </rEnviConsRUC>
141
+ </env:Body>
142
+ </env:Envelope>
143
+ ```
144
+
145
+ </details>
146
+
147
+ ---
148
+
149
+ ### `consultDocument(consultDocumentDto)`
150
+
151
+ Genera el sobre SOAP `rEnviConsDeRequest` para consultar un documento electrónico
152
+ (factura, nota, etc.) mediante su **CDC** (Código de Control).
153
+
154
+ | Parámetro | Tipo | Descripción |
155
+ | --------- | -------- | ------------------------------------- |
156
+ | `id` | `number` | Identificador de la petición (`dId`). |
157
+ | `cdc` | `string` | CDC de 44 dígitos del documento. |
158
+
159
+ **Retorna:** `string` — XML SOAP normalizado.
160
+
161
+ ```ts
162
+ const xml = sifen.consultDocument({
163
+ id: 2,
164
+ cdc: "01800123456001001000000012023052212345678901",
165
+ });
166
+ ```
167
+
168
+ <details>
169
+ <summary>Ejemplo de salida (formateada para lectura)</summary>
170
+
171
+ ```xml
172
+ <?xml version="1.0" encoding="UTF-8"?>
173
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
174
+ <env:Header/>
175
+ <env:Body>
176
+ <rEnviConsDeRequest xmlns="http://ekuatia.set.gov.py/sifen/xsd">
177
+ <dId>2</dId>
178
+ <dCDC>01800123456001001000000012023052212345678901</dCDC>
179
+ </rEnviConsDeRequest>
180
+ </env:Body>
181
+ </env:Envelope>
182
+ ```
183
+
184
+ </details>
185
+
186
+ ---
187
+
188
+ ### `consultBatch(consultBatchDto)`
189
+
190
+ Genera el sobre SOAP `rEnviConsLoteDe` para consultar el estado de un **lote**
191
+ previamente enviado a SIFEN.
192
+
193
+ | Parámetro | Tipo | Descripción |
194
+ | ------------ | -------- | ----------------------------------------------- |
195
+ | `id` | `number` | Identificador de la petición (`dId`). |
196
+ | `codigoLote` | `string` | Número de protocolo del lote (`dProtConsLote`). |
197
+
198
+ **Retorna:** `string` — XML SOAP normalizado.
199
+
200
+ ```ts
201
+ const xml = sifen.consultBatch({ id: 3, codigoLote: "1234567890" });
202
+ ```
203
+
204
+ <details>
205
+ <summary>Ejemplo de salida (formateada para lectura)</summary>
206
+
207
+ ```xml
208
+ <?xml version="1.0" encoding="UTF-8"?>
209
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
210
+ <env:Header/>
211
+ <env:Body>
212
+ <rEnviConsLoteDe xmlns="http://ekuatia.set.gov.py/sifen/xsd">
213
+ <dId>3</dId>
214
+ <dProtConsLote>1234567890</dProtConsLote>
215
+ </rEnviConsLoteDe>
216
+ </env:Body>
217
+ </env:Envelope>
218
+ ```
219
+
220
+ </details>
221
+
222
+ ---
223
+
224
+ ### `obtainDigitalSign(digitalSignDto)`
225
+
226
+ Lee un archivo de firma digital `.p12`/`.pfx`, valida que contenga llave privada y
227
+ certificado, y los devuelve en formato **PEM**.
228
+
229
+ | Parámetro | Tipo | Descripción |
230
+ | ------------ | -------- | ---------------------------------------- |
231
+ | `camino` | `string` | Ruta al archivo `.p12`/`.pfx`. |
232
+ | `contraseña` | `string` | Contraseña que protege la firma digital. |
233
+
234
+ **Retorna:** `ResponseDigitalSign`
235
+
236
+ ```ts
237
+ interface ResponseDigitalSign {
238
+ certificado: string; // certificado en formato PEM
239
+ llavePrivada: string; // llave privada en formato PEM
240
+ }
241
+ ```
242
+
243
+ **Errores que puede lanzar:**
244
+
245
+ - `"La firma digital no contiene una llave privada"` — el `.p12` no tiene llave.
246
+ - `"La firma digital no contiene un certificado"` — el `.p12` no tiene certificado.
247
+ - Errores de `node-forge` si la contraseña es incorrecta o el archivo está corrupto.
248
+
249
+ ```ts
250
+ try {
251
+ const { certificado, llavePrivada } = sifen.obtainDigitalSign({
252
+ camino: "./certificados/firma.p12",
253
+ contraseña: "clave",
254
+ });
255
+ } catch (error) {
256
+ // contraseña incorrecta, archivo inválido o sin llave/certificado
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ### `generateQr(generationQrDto)`
263
+
264
+ Toma un documento electrónico (`rDE`) **ya firmado digitalmente**, calcula el enlace del
265
+ código QR exigido por SIFEN y devuelve el **mismo XML con el nodo `<gCamFuFD><dCarQR>`
266
+ incrustado**.
267
+
268
+ > ⚠️ Este es el único método **asíncrono** de la librería: devuelve `Promise<string>`.
269
+
270
+ | Parámetro | Tipo | Descripción |
271
+ | ---------- | -------- | ---------------------------------------------------------------------- |
272
+ | `xml` | `string` | XML del `rDE` **firmado** (debe contener el nodo `Signature`). |
273
+ | `csc` | `string` | Código de Seguridad del Contribuyente (secreto, entregado por la SET). |
274
+ | `idCsc` | `string` | Identificador del CSC (ej. `"0001"`). |
275
+ | `ambiente` | `string` | `"test"` o `"produccion"`. Define el dominio base del enlace del QR. |
276
+
277
+ **Retorna:** `Promise<string>` — el XML reconstruido con el nodo `dCarQR`.
278
+
279
+ ```ts
280
+ const xmlConQr = await sifen.generateQr({
281
+ xml: xmlFirmado,
282
+ csc: "ABCD0000000000000000000000000000",
283
+ idCsc: "0001",
284
+ ambiente: "test",
285
+ });
286
+ ```
287
+
288
+ #### Cómo se construye el QR
289
+
290
+ 1. Se parsea el XML y se **valida que exista el nodo `Signature`**; si no está, lanza error.
291
+ 2. Se elige el dominio base según el ambiente:
292
+ - `test` → `https://ekuatia.set.gov.py/consultas-test`
293
+ - `produccion` → `https://ekuatia.set.gov.py/consultas`
294
+ 3. Se le concatena `/qr?` y los parámetros extraídos del documento, **en este orden**:
295
+
296
+ | Parámetro | Origen en el XML | Notas |
297
+ | ------------------------- | -------------------------------------------------------- | ----------------------------------------------- |
298
+ | `nVersion` | `rDE > dVerFor` | Versión del formato. |
299
+ | `Id` | `rDE > DE[@Id]` | El CDC del documento. |
300
+ | `dFeEmiDE` | `rDE > DE > gDatGralOpe > dFeEmiDE` | Convertido a **hexadecimal**. |
301
+ | `dRucRec` _o_ `dNumIDRec` | `... > gDatRec` | `dRucRec` si `iNatRec == 1`, si no `dNumIDRec`. |
302
+ | `dTotGralOpe` | `rDE > DE > gTotSub > dTotGralOpe` | `0` si no existe. |
303
+ | `dTotIVA` | `rDE > DE > gTotSub > dTotIVA` | `0` si no existe. |
304
+ | `cItems` | Cantidad de `gDtipDE > gCamItem` | `0` si no hay ítems. |
305
+ | `DigestValue` | `rDE > Signature > SignedInfo > Reference > DigestValue` | Convertido a **hexadecimal**. |
306
+ | `IdCSC` | Parámetro `idCsc` | Último antes del hash. |
307
+ | `cHashQR` | `SHA256(cadena_de_parámetros + csc)` | Se agrega al final. |
308
+
309
+ 4. El enlace resultante se escribe en `rDE > gCamFuFD > dCarQR` y se reconstruye el XML.
310
+
311
+ <details>
312
+ <summary>Ejemplo del enlace generado dentro de <code>dCarQR</code></summary>
313
+
314
+ ```
315
+ https://ekuatia.set.gov.py/consultas-test/qr?nVersion=150&Id=01800123456001001000000012023052212345678901&dFeEmiDE=323032332d30352d32325431303a30303a3030&dRucRec=80012345&dTotGralOpe=550000&dTotIVA=50000&cItems=3&DigestValue=6a4b2c...&IdCSC=0001&cHashQR=9f2a7c1e4b8d...
316
+ ```
317
+
318
+ Y así queda incrustado en el XML de salida:
319
+
320
+ ```xml
321
+ <rDE>
322
+ <dVerFor>150</dVerFor>
323
+ <DE Id="01800123456001001000000012023052212345678901">
324
+ ...
325
+ </DE>
326
+ <Signature>...</Signature>
327
+ <gCamFuFD>
328
+ <dCarQR>https://ekuatia.set.gov.py/consultas-test/qr?nVersion=150&amp;Id=...&amp;cHashQR=...</dCarQR>
329
+ </gCamFuFD>
330
+ </rDE>
331
+ ```
332
+
333
+ </details>
334
+
335
+ **Errores que puede lanzar:**
336
+
337
+ - `"XML debe estar firmado digitalmente para generar el QR"` — el XML no tiene nodo `Signature`.
338
+ - Errores de parseo de `xml2js` si el XML está malformado.
339
+ - `TypeError` si el documento no respeta la estructura esperada del `rDE` (por ejemplo,
340
+ falta `gDatRec` o `gDtipDE`).
341
+
342
+ ```ts
343
+ try {
344
+ const xmlConQr = await sifen.generateQr({
345
+ xml: xmlFirmado,
346
+ csc: process.env.SIFEN_CSC!,
347
+ idCsc: "0001",
348
+ ambiente: "produccion",
349
+ });
350
+ } catch (error) {
351
+ // XML sin firmar, malformado o con estructura inesperada
352
+ }
353
+ ```
354
+
355
+ > **Nota sobre el `ambiente`:** internamente se compara contra el enum `SIFEN_ENVIRONMENT`
356
+ > (`"test"` / `"produccion"`). Cualquier valor distinto de `"test"` cae en el enlace de
357
+ > **producción**, así que conviene pasar el valor exacto.
358
+
359
+ ---
360
+
361
+ ## DTOs (contratos de entrada)
362
+
363
+ Todos los DTOs de consulta extienden `GenerateIdDto`, que aporta el campo `id`.
364
+
365
+ ```ts
366
+ interface GenerateIdDto {
367
+ id: number;
368
+ }
369
+
370
+ interface ConsultRucDto extends GenerateIdDto {
371
+ ruc: string;
372
+ }
373
+
374
+ interface ConsultDocumentDto extends GenerateIdDto {
375
+ cdc: string;
376
+ }
377
+
378
+ interface ConsultBatchDto extends GenerateIdDto {
379
+ codigoLote: string;
380
+ }
381
+
382
+ interface DigitalSignDto {
383
+ camino: string; // ruta al .p12
384
+ contraseña: string; // clave del .p12
385
+ }
386
+
387
+ interface GenerationQrDto {
388
+ xml: string; // XML del rDE ya firmado
389
+ csc: string; // Código de Seguridad del Contribuyente
390
+ idCsc: string; // Identificador del CSC
391
+ ambiente: string; // "test" | "produccion"
392
+ }
393
+ ```
394
+
395
+ Enums disponibles en [`src/shared/utils/dictionary.utils.ts`](src/shared/utils/dictionary.utils.ts):
396
+
397
+ ```ts
398
+ enum SIFEN_ENVIRONMENT {
399
+ TEST = "test",
400
+ PRODUCCION = "produccion",
401
+ }
402
+
403
+ enum LINK_SIFEN {
404
+ TEST = "https://ekuatia.set.gov.py/consultas-test",
405
+ PRODUCCION = "https://ekuatia.set.gov.py/consultas",
406
+ }
407
+ ```
408
+
409
+ DTOs ya definidos pero **aún sin implementación** en la persistencia (ver
410
+ [roadmap](#estado--roadmap)):
411
+
412
+ ```ts
413
+ interface CancelInvoiceDto extends GenerateIdDto {
414
+ cdc: string;
415
+ motivo: string;
416
+ }
417
+
418
+ interface InvalidationInvoiceDto extends GenerateIdDto {
419
+ timbrado: string;
420
+ establecimiento: number;
421
+ puntoExpedicion: number;
422
+ desde: number;
423
+ hasta: number;
424
+ tipoDocumento: number;
425
+ motivo: string;
426
+ }
427
+ ```
428
+
429
+ ---
430
+
431
+ ## Funciones utilitarias internas
432
+
433
+ Viven en [`src/shared/functions/functions.utils.ts`](src/shared/functions/functions.utils.ts).
434
+ No forman parte de la API pública, pero es útil conocerlas:
435
+
436
+ | Función | Qué hace |
437
+ | ---------------------------- | --------------------------------------------------------------------------------------------- |
438
+ | `generateConsultRucXml` | Arma el string XML de consulta de RUC. |
439
+ | `generateConsultDocumentXml` | Arma el string XML de consulta de documento por CDC. |
440
+ | `generateConsultBatchXml` | Arma el string XML de consulta de lote. |
441
+ | `normalizeXML` | Colapsa el XML a una sola línea: elimina saltos, tabulaciones y espacios entre etiquetas. |
442
+ | `readDigitalSign` | Lee el `.p12` con `node-forge` y extrae certificado + llave privada en PEM. |
443
+ | `generateQr` | Arma el enlace del QR (params + hash SHA-256) y lo incrusta en `gCamFuFD > dCarQR`. _(async)_ |
444
+ | `calculateDigitVerification` | Calcula el dígito verificador (DV) de una cédula/RUC según el algoritmo módulo 11. |
445
+
446
+ ---
447
+
448
+ ## Arquitectura del proyecto
449
+
450
+ El proyecto sigue una **arquitectura hexagonal (puertos y adaptadores)** combinada con
451
+ principios de **Clean Architecture**, organizada por el módulo de negocio `sifen`.
452
+
453
+ ```
454
+ ┌──────────────────────────────────────────────┐
455
+ │ applications │
456
+ │ SifenService (punto de entrada / fachada) │
457
+ │ DTOs (contratos de entrada) │
458
+ └───────────────────────┬──────────────────────┘
459
+ │ orquesta
460
+ ▼
461
+ ┌──────────────────────────────────────────────┐
462
+ │ domain │
463
+ │ useCases (ConsultRuc, ConsultBatch, ...) │
464
+ │ repository (SifenRepository = PUERTO) │
465
+ └───────────────────────┬──────────────────────┘
466
+ │ implementado por
467
+ ▼
468
+ ┌──────────────────────────────────────────────┐
469
+ │ infrastructure │
470
+ │ SifenPersistence (ADAPTADOR del puerto) │
471
+ │ → usa funciones utilitarias (XML, forge) │
472
+ └──────────────────────────────────────────────┘
473
+ ```
474
+
475
+ **Flujo de una llamada** (ej. `consultRuc`):
476
+
477
+ 1. La app llama a `SifenService.consultRuc(dto)` — capa **applications**.
478
+ 2. El servicio delega en `ConsultRucUseCase.execute(dto)` — capa **domain**.
479
+ 3. El caso de uso invoca la interfaz `SifenRepository` (**puerto**), sin conocer la implementación.
480
+ 4. En runtime el puerto lo resuelve `SifenPersistence` (**adaptador**) — capa **infrastructure**.
481
+ 5. La persistencia usa las funciones utilitarias para generar y normalizar el XML.
482
+
483
+ **Por qué esta separación:**
484
+
485
+ - **`domain`** no depende de nada externo: define _qué_ se hace (casos de uso) y el
486
+ contrato `SifenRepository`. Es el núcleo estable.
487
+ - **`infrastructure`** contiene los detalles concretos (armado de XML, `node-forge`, `fs`).
488
+ Se puede reemplazar sin tocar el dominio.
489
+ - **`applications`** expone la fachada y los DTOs que consume el mundo exterior.
490
+ - **`shared`** agrupa utilidades y DTOs de respuesta transversales a los módulos.
491
+
492
+ La inversión de dependencias es clave: `SifenPersistence implements SifenRepository`, de
493
+ modo que el dominio depende de la **abstracción**, no de la implementación.
494
+
495
+ ---
496
+
497
+ ## Estructura de carpetas
498
+
499
+ ```
500
+ consultame-sdk/
501
+ ├── src/
502
+ │ ├── index.ts # Punto de entrada público → exporta SifenService
503
+ │ │
504
+ │ ├── shared/ # Código transversal
505
+ │ │ ├── dto/
506
+ │ │ │ └── responseDigitalSign.dto.ts # ResponseDigitalSign { certificado, llavePrivada }
507
+ │ │ ├── functions/
508
+ │ │ │ └── functions.utils.ts # XML, normalizeXML, readDigitalSign, generateQr, DV
509
+ │ │ └── utils/
510
+ │ │ └── dictionary.utils.ts # Enums SIFEN_ENVIRONMENT y LINK_SIFEN
511
+ │ │
512
+ │ └── sifen/ # Módulo de negocio SIFEN
513
+ │ ├── applications/ # Capa de aplicación (fachada + contratos)
514
+ │ │ ├── dtos/
515
+ │ │ │ ├── index.ts # Barrel de DTOs
516
+ │ │ │ ├── generateId.dto.ts # GenerateIdDto (base)
517
+ │ │ │ ├── consultRuc.dto.ts
518
+ │ │ │ ├── consultDocument.dto.ts
519
+ │ │ │ ├── consultBatch.dto.ts
520
+ │ │ │ ├── digitalSign.dto.ts
521
+ │ │ │ ├── generationQr.dto.ts
522
+ │ │ │ ├── cancelDocument.dto.ts
523
+ │ │ │ └── invalidationInvoice.dto.ts
524
+ │ │ └── services/
525
+ │ │ └── sifen.service.ts # SifenService (fachada pública)
526
+ │ │
527
+ │ ├── domain/ # Núcleo de negocio
528
+ │ │ ├── repository/
529
+ │ │ │ └── sifen.repository.ts # SifenRepository (PUERTO / interfaz)
530
+ │ │ └── useCases/
531
+ │ │ ├── index.ts # Barrel de casos de uso
532
+ │ │ ├── consultRuc.useCase.ts
533
+ │ │ ├── consultDocument.useCase.ts
534
+ │ │ ├── consultBatch.useCase.ts
535
+ │ │ ├── obtainDigitalSign.useCase.ts
536
+ │ │ └── generateQr.useCase.ts
537
+ │ │
538
+ │ └── infrastructure/ # Detalles concretos (ADAPTADORES)
539
+ │ └── persistence/
540
+ │ └── sifen.persistence.ts # SifenPersistence implements SifenRepository
541
+ │
542
+ ├── package.json
543
+ ├── tsconfig.json
544
+ ├── tsup.config.ts # Build dual CJS + ESM (target node22)
545
+ ├── pnpm-workspace.yaml
546
+ └── README.md
547
+ ```
548
+
549
+ ---
550
+
551
+ ## Scripts de desarrollo
552
+
553
+ | Script | Comando | Descripción |
554
+ | ------------------ | --------------------------- | --------------------------------------------------- |
555
+ | `pnpm build` | `tsup && build:types` | Compila a `dist/` (CJS + ESM) y genera los `.d.ts`. |
556
+ | `pnpm build:types` | `tsc --emitDeclarationOnly` | Solo emite las declaraciones de tipos. |
557
+ | `pnpm dev` | `tsup --watch` | Build en modo watch. |
558
+ | `pnpm typecheck` | `tsc --noEmit` | Verifica tipos sin emitir archivos. |
559
+ | `pnpm play` | `tsx src/index.ts` | Ejecuta `src/index.ts` para pruebas manuales. |
560
+ | `pnpm play:watch` | `tsx watch src/index.ts` | Igual que `play` pero en modo watch. |
561
+
562
+ ---
563
+
564
+ ## Estado / roadmap
565
+
566
+ Funcionalidades **implementadas**:
567
+
568
+ - ✅ Consulta de RUC (`consultRuc`)
569
+ - ✅ Consulta de documento por CDC (`consultDocument`)
570
+ - ✅ Consulta de lote (`consultBatch`)
571
+ - ✅ Lectura de firma digital `.p12` → PEM (`obtainDigitalSign`)
572
+ - ✅ Generación e incrustación del código QR (`generateQr`)
573
+
574
+ Definidas pero **pendientes de implementación** (hoy lanzan `Method not implemented`
575
+ o aún no están expuestas en `SifenService`):
576
+
577
+ - ⏳ Cancelación de documento (`cancelationInvoice` / `CancelInvoiceDto`)
578
+ - ⏳ Inutilización de documentos (`invalidationInvoice` / `InvalidationInvoiceDto`)
579
+
580
+ ---
581
+
582
+ > **Nota:** esta librería genera insumos (XML, credenciales y el QR) para SIFEN, pero no
583
+ > realiza el envío HTTP ni la firma XMLDSig de los documentos. Esa parte queda a cargo de
584
+ > la aplicación consumidora — de hecho, `generateQr` **requiere** que el XML ya venga
585
+ > firmado, porque toma el `DigestValue` de la firma para calcular el hash del QR.
@@ -0,0 +1,2 @@
1
+ export { SifenService } from "./sifen/applications/services/sifen.service";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC"}