sunat-engine-nest 1.1.1 → 2.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.
- package/README.md +170 -40
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/licensing/pdf-license.service.d.ts +11 -0
- package/dist/licensing/pdf-license.service.js +51 -0
- package/dist/licensing/pdf-license.service.js.map +1 -0
- package/dist/licensing/public-key.d.ts +1 -0
- package/dist/licensing/public-key.js +8 -0
- package/dist/licensing/public-key.js.map +1 -0
- package/dist/pdf/document-pdf.service.d.ts +33 -4
- package/dist/pdf/document-pdf.service.js +369 -113
- package/dist/pdf/document-pdf.service.js.map +1 -1
- package/dist/soap/fake-sunat-soap.client.js +12 -12
- package/dist/soap/sunat-soap.client.js +34 -34
- package/dist/sunat-engine.module.d.ts +4 -0
- package/dist/sunat-engine.module.js +2 -0
- package/dist/sunat-engine.module.js.map +1 -1
- package/package.json +50 -50
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
# sunat-engine-nest
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
3
6
|
Motor de Facturación Electrónica SUNAT UBL 2.1 para NestJS.
|
|
4
7
|
|
|
5
|
-
Soporta Facturas, Boletas, Notas de Crédito/Débito, Guías de Remisión (GRE), Resúmenes Diarios y Comunicaciones de Baja.
|
|
8
|
+
Soporta Facturas, Boletas, Notas de Crédito/Débito, Guías de Remisión (GRE), Resúmenes Diarios y Comunicaciones de Baja — firma XML, envío a SUNAT/OSE, parseo de CDR y generación de PDF.
|
|
9
|
+
|
|
10
|
+
## Índice
|
|
11
|
+
|
|
12
|
+
- [Instalación](#instalación)
|
|
13
|
+
- [Configuración](#configuración)
|
|
14
|
+
- [Registro síncrono](#registro-síncrono)
|
|
15
|
+
- [Registro asíncrono](#registro-asíncrono)
|
|
16
|
+
- [Proveedor: SUNAT directo vs OSE](#proveedor-sunat-directo-vs-ose)
|
|
17
|
+
- [Certificado digital](#certificado-digital)
|
|
18
|
+
- [Variables de entorno](#variables-de-entorno)
|
|
19
|
+
- [Uso](#uso)
|
|
20
|
+
- [Enviar un comprobante](#enviar-un-comprobante)
|
|
21
|
+
- [Documentos soportados](#documentos-soportados)
|
|
22
|
+
- [Generación de PDF (función paga)](#generación-de-pdf-función-paga)
|
|
23
|
+
- [Testing / Modo simulado](#testing--modo-simulado)
|
|
24
|
+
- [Respuesta del motor](#respuesta-del-motor)
|
|
25
|
+
- [Endpoints SUNAT](#endpoints-sunat)
|
|
26
|
+
- [Soporte y actualizaciones](#soporte-y-actualizaciones)
|
|
27
|
+
- [Licencia](#licencia)
|
|
6
28
|
|
|
7
29
|
## Instalación
|
|
8
30
|
|
|
@@ -16,10 +38,11 @@ pnpm add sunat-engine-nest
|
|
|
16
38
|
|
|
17
39
|
Registra el módulo en tu `AppModule`. Puedes definir las credenciales de tu empresa **una sola vez** en el módulo usando el campo `sunat`, evitando repetirlas en cada llamada.
|
|
18
40
|
|
|
41
|
+
### Registro síncrono
|
|
42
|
+
|
|
19
43
|
```typescript
|
|
20
44
|
import { SunatEngineModule } from 'sunat-engine-nest';
|
|
21
45
|
|
|
22
|
-
// Registro síncrono
|
|
23
46
|
@Module({
|
|
24
47
|
imports: [
|
|
25
48
|
SunatEngineModule.forRoot({
|
|
@@ -42,11 +65,14 @@ import { SunatEngineModule } from 'sunat-engine-nest';
|
|
|
42
65
|
export class AppModule {}
|
|
43
66
|
```
|
|
44
67
|
|
|
68
|
+
### Registro asíncrono
|
|
69
|
+
|
|
70
|
+
Recomendado cuando las credenciales dependen de variables de entorno.
|
|
71
|
+
|
|
45
72
|
```typescript
|
|
46
73
|
import { SunatEngineModule } from 'sunat-engine-nest';
|
|
47
74
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
48
75
|
|
|
49
|
-
// Registro asíncrono (recomendado con variables de entorno)
|
|
50
76
|
@Module({
|
|
51
77
|
imports: [
|
|
52
78
|
ConfigModule.forRoot({ isGlobal: true }),
|
|
@@ -79,29 +105,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
|
79
105
|
export class AppModule {}
|
|
80
106
|
```
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```env
|
|
85
|
-
# Credenciales SUNAT
|
|
86
|
-
SUNAT_RUC=20123456789
|
|
87
|
-
SUNAT_SOL_USER=MODDATOS
|
|
88
|
-
SUNAT_SOL_PASS=moddatos
|
|
89
|
-
SUNAT_CERT_PEM=BASE64_DEL_P12
|
|
90
|
-
SUNAT_MODE=beta
|
|
91
|
-
|
|
92
|
-
# GRE (Guía de Remisión Electrónica)
|
|
93
|
-
GRE_AUTH_URL=https://gre-test.nubefact.com/v1
|
|
94
|
-
GRE_API_URL=https://gre-test.nubefact.com/v1
|
|
95
|
-
GRE_CLIENT_ID=tu_client_id
|
|
96
|
-
GRE_CLIENT_SECRET=tu_client_secret
|
|
97
|
-
|
|
98
|
-
# OSE (solo si provider=ose)
|
|
99
|
-
PROVIDER=ose
|
|
100
|
-
OSE_URL=https://api.tuose.com/v1/documents
|
|
101
|
-
OSE_TOKEN=tu_token_aqui
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Proveedor: SUNAT directo vs OSE
|
|
108
|
+
### Proveedor: SUNAT directo vs OSE
|
|
105
109
|
|
|
106
110
|
Puedes elegir enviar tus comprobantes **directamente a SUNAT** o a través de un **OSE (Operador de Servicios Electrónicos)** autorizado como Nubefact, EFACT, DigiFlow, Bizlinks, entre otros.
|
|
107
111
|
|
|
@@ -128,7 +132,7 @@ SunatEngineModule.forRoot({
|
|
|
128
132
|
|
|
129
133
|
> El `OseClient` es genérico y compatible con cualquier OSE que acepte `{ fileName, contentFile }` (ZIP en base64) vía POST con autenticación Bearer o Basic.
|
|
130
134
|
|
|
131
|
-
|
|
135
|
+
### Certificado digital
|
|
132
136
|
|
|
133
137
|
El motor acepta el certificado en tres formatos:
|
|
134
138
|
|
|
@@ -150,8 +154,32 @@ const credentials: CompanyCredentials = {
|
|
|
150
154
|
};
|
|
151
155
|
```
|
|
152
156
|
|
|
157
|
+
### Variables de entorno
|
|
158
|
+
|
|
159
|
+
```env
|
|
160
|
+
# Credenciales SUNAT
|
|
161
|
+
SUNAT_RUC=20123456789
|
|
162
|
+
SUNAT_SOL_USER=MODDATOS
|
|
163
|
+
SUNAT_SOL_PASS=moddatos
|
|
164
|
+
SUNAT_CERT_PEM=BASE64_DEL_P12
|
|
165
|
+
SUNAT_MODE=beta
|
|
166
|
+
|
|
167
|
+
# GRE (Guía de Remisión Electrónica)
|
|
168
|
+
GRE_AUTH_URL=https://gre-test.nubefact.com/v1
|
|
169
|
+
GRE_API_URL=https://gre-test.nubefact.com/v1
|
|
170
|
+
GRE_CLIENT_ID=tu_client_id
|
|
171
|
+
GRE_CLIENT_SECRET=tu_client_secret
|
|
172
|
+
|
|
173
|
+
# OSE (solo si provider=ose)
|
|
174
|
+
PROVIDER=ose
|
|
175
|
+
OSE_URL=https://api.tuose.com/v1/documents
|
|
176
|
+
OSE_TOKEN=tu_token_aqui
|
|
177
|
+
```
|
|
178
|
+
|
|
153
179
|
## Uso
|
|
154
180
|
|
|
181
|
+
### Enviar un comprobante
|
|
182
|
+
|
|
155
183
|
Inyecta `SunatEngineService` en cualquier servicio o controlador. Si configuraste `sunat:{}` en el módulo, **no necesitas pasar credenciales en cada llamada**:
|
|
156
184
|
|
|
157
185
|
```typescript
|
|
@@ -222,20 +250,33 @@ return this.engine.sendInvoice(payload, {
|
|
|
222
250
|
});
|
|
223
251
|
```
|
|
224
252
|
|
|
225
|
-
|
|
253
|
+
### Documentos soportados
|
|
254
|
+
|
|
255
|
+
| Tipo | Método de envío | Descripción | PDF |
|
|
256
|
+
|------|------------------|-------------|-----|
|
|
257
|
+
| Factura (01) | `sendInvoice()` | Factura electrónica — SOAP síncrono | ✅ `generateInvoice()` |
|
|
258
|
+
| Boleta (03) | `sendInvoice()` | Boleta de venta electrónica — SOAP síncrono | ✅ `generateInvoice()` |
|
|
259
|
+
| Nota de Crédito (07) | `sendNote()` | NC electrónica — SOAP síncrono | ✅ `generateNote()` |
|
|
260
|
+
| Nota de Débito (08) | `sendNote()` | ND electrónica — SOAP síncrono | ✅ `generateNote()` |
|
|
261
|
+
| Guía de Remisión (09) | `sendDespatch()` | GRE 2022 — REST/OAuth asíncrono | ✅ `generateDespatch()` |
|
|
262
|
+
| Resumen Diario (RC) | `sendSummary()` | Resumen de boletas — SOAP asíncrono | — (comunicación interna a SUNAT) |
|
|
263
|
+
| Comunicación de Baja (RA) | `sendVoided()` | Baja de comprobantes — SOAP asíncrono | — (comunicación interna a SUNAT) |
|
|
264
|
+
| Consulta de ticket | `getTicketStatus()` | Estado de procesos asíncronos | — |
|
|
226
265
|
|
|
227
|
-
|
|
228
|
-
|------|--------|-------------|
|
|
229
|
-
| Factura (01) | `sendInvoice()` | Factura electrónica — SOAP síncrono |
|
|
230
|
-
| Boleta (03) | `sendInvoice()` | Boleta de venta electrónica — SOAP síncrono |
|
|
231
|
-
| Nota de Crédito (07) | `sendNote()` | NC electrónica — SOAP síncrono |
|
|
232
|
-
| Nota de Débito (08) | `sendNote()` | ND electrónica — SOAP síncrono |
|
|
233
|
-
| Guía de Remisión (09) | `sendDespatch()` | GRE 2022 — REST/OAuth asíncrono |
|
|
234
|
-
| Resumen Diario (RC) | `sendSummary()` | Resumen de boletas — SOAP asíncrono |
|
|
235
|
-
| Comunicación de Baja (RA) | `sendVoided()` | Baja de comprobantes — SOAP asíncrono |
|
|
236
|
-
| Consulta de ticket | `getTicketStatus()` | Estado de procesos asíncronos |
|
|
266
|
+
## Generación de PDF (función paga)
|
|
237
267
|
|
|
238
|
-
|
|
268
|
+
El envío a SUNAT (XML, firma, CDR, GRE) sigue siendo **MIT / gratuito**. La generación de PDF es una función **paga**: necesitas una license key para usarla — [escríbeme por WhatsApp](https://wa.me/51907596305) para adquirir una.
|
|
269
|
+
|
|
270
|
+
La licencia se valida **localmente** (firma Ed25519), sin llamadas a ningún servidor externo — tus datos de facturación nunca salen de tu infraestructura.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
SunatEngineModule.forRoot({
|
|
274
|
+
sunat: { ruc: '...', solUser: '...', solPass: '...', certPem: '...' },
|
|
275
|
+
pdf: {
|
|
276
|
+
licenseKey: 'LA_LICENSE_KEY_QUE_TE_ENTREGO', // o desde tu ConfigService/env
|
|
277
|
+
},
|
|
278
|
+
})
|
|
279
|
+
```
|
|
239
280
|
|
|
240
281
|
```typescript
|
|
241
282
|
import { DocumentPdfService } from 'sunat-engine-nest';
|
|
@@ -253,6 +294,95 @@ export class InvoicesService {
|
|
|
253
294
|
}
|
|
254
295
|
```
|
|
255
296
|
|
|
297
|
+
Sin `pdf.licenseKey` configurada (o con una key inválida/expirada), los métodos de PDF lanzan un `PdfLicenseError`.
|
|
298
|
+
|
|
299
|
+
### Personalizar diseño (logo y color de marca)
|
|
300
|
+
|
|
301
|
+
El formato `A4` acepta branding opcional. Si no se especifica, usa un azul corporativo por defecto.
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
return this.pdf.generateInvoice(payload, 'A4', {
|
|
305
|
+
licenseKey: 'OTRA_LICENSE_KEY', // opcional — tiene prioridad sobre la del módulo
|
|
306
|
+
branding: {
|
|
307
|
+
color: '#0f766e', // hex, opcional
|
|
308
|
+
logoBase64: 'data:image/png;base64,...', // opcional
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Nota de Crédito / Débito
|
|
314
|
+
|
|
315
|
+
Mismo diseño y misma licencia que las facturas.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
await this.pdf.generateNote(notePayload, 'A4'); // 'A4' | 'TICKET_80MM' | 'STICKER_A6'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Guía de Remisión (GRE)
|
|
322
|
+
|
|
323
|
+
Solo formato `A4` — incluye destinatario, datos de traslado, punto de partida/llegada, transportista/vehículo/conductor.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
await this.pdf.generateDespatch(despatchPayload, {
|
|
327
|
+
branding: { color: '#0f766e' }, // opcional
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> Resumen Diario (RC) y Comunicación de Baja (RA) no tienen representación en PDF — son comunicaciones internas a SUNAT, no documentos que se entreguen a un cliente ni que viajen con la mercadería.
|
|
332
|
+
|
|
333
|
+
## Testing / Modo simulado
|
|
334
|
+
|
|
335
|
+
SUNAT beta rechaza `sendSummary` (RC/RA) con el RUC genérico de prueba `20000000001` — ese RUC solo funciona de forma confiable para `sendBill` (facturas y boletas). Para probar el flujo completo sin depender del entorno beta, el módulo incluye un **cliente SOAP simulado** que devuelve respuestas válidas sin realizar ninguna llamada HTTP.
|
|
336
|
+
|
|
337
|
+
### Opción 1 — `useFake: true` en el módulo (recomendado)
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// app.module.ts (para tests)
|
|
341
|
+
SunatEngineModule.forRoot({
|
|
342
|
+
useFake: true, // ← activa FakeSunatSoapClient automáticamente
|
|
343
|
+
sunat: {
|
|
344
|
+
ruc: '20000000001',
|
|
345
|
+
solUser: 'MODDATOS',
|
|
346
|
+
solPass: 'moddatos',
|
|
347
|
+
certPem: 'BASE64_DEL_P12',
|
|
348
|
+
},
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// También con forRootAsync:
|
|
352
|
+
SunatEngineModule.forRootAsync({
|
|
353
|
+
imports: [ConfigModule],
|
|
354
|
+
inject: [ConfigService],
|
|
355
|
+
useFactory: (config: ConfigService) => ({
|
|
356
|
+
useFake: config.get('NODE_ENV') === 'test',
|
|
357
|
+
sunat: { ruc: config.get('SUNAT_RUC'), ... },
|
|
358
|
+
}),
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Opción 2 — `overrideProvider` en NestJS Testing
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { Test } from '@nestjs/testing';
|
|
366
|
+
import { SunatSoapClient, FakeSunatSoapClient } from 'sunat-engine-nest';
|
|
367
|
+
|
|
368
|
+
const moduleRef = await Test.createTestingModule({
|
|
369
|
+
imports: [SunatEngineModule.forRoot({ sunat: { ... } })],
|
|
370
|
+
})
|
|
371
|
+
.overrideProvider(SunatSoapClient)
|
|
372
|
+
.useClass(FakeSunatSoapClient)
|
|
373
|
+
.compile();
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Comportamiento del cliente simulado
|
|
377
|
+
|
|
378
|
+
| Método | Respuesta simulada |
|
|
379
|
+
|--------|-------------------|
|
|
380
|
+
| `sendBill` | CDR ZIP con `ResponseCode=0` (ACEPTADA) |
|
|
381
|
+
| `sendSummary` | Ticket numérico incremental (`1000000001`, `1000000002`, ...) |
|
|
382
|
+
| `getStatus` | CDR ZIP con `ResponseCode=0` (ACEPTADA) |
|
|
383
|
+
|
|
384
|
+
No se realiza ninguna llamada HTTP — el XML se genera y firma normalmente, solo el envío a SUNAT es simulado.
|
|
385
|
+
|
|
256
386
|
## Respuesta del motor
|
|
257
387
|
|
|
258
388
|
```typescript
|
|
@@ -296,4 +426,4 @@ Cualquier aporte, por pequeño que sea, ayuda a mantener la librería actualizad
|
|
|
296
426
|
|
|
297
427
|
## Licencia
|
|
298
428
|
|
|
299
|
-
MIT
|
|
429
|
+
MIT — excepto la generación de PDF (`DocumentPdfService`), que requiere una license key comercial. Ver [Generación de PDF](#generación-de-pdf-función-paga).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { SunatEngineModule, SUNAT_ENGINE_OPTIONS } from './sunat-engine.module';
|
|
2
|
-
export type { SunatEngineOptions, SunatEngineAsyncOptions, SunatEngineGreOptions, SunatEngineCredentialsOptions, SunatEngineOseOptions } from './sunat-engine.module';
|
|
2
|
+
export type { SunatEngineOptions, SunatEngineAsyncOptions, SunatEngineGreOptions, SunatEngineCredentialsOptions, SunatEngineOseOptions, SunatEnginePdfOptions } from './sunat-engine.module';
|
|
3
3
|
export { SunatEngineService } from './sunat-engine.service';
|
|
4
4
|
export { XmlSignerService } from './signer/xml-signer.service';
|
|
5
5
|
export { SunatSoapClient } from './soap/sunat-soap.client';
|
|
@@ -10,7 +10,9 @@ export { OseClient } from './ose/ose.client';
|
|
|
10
10
|
export type { OseClientOptions } from './ose/ose.client';
|
|
11
11
|
export { CdrParserService } from './cdr/cdr-parser.service';
|
|
12
12
|
export { DocumentPdfService } from './pdf/document-pdf.service';
|
|
13
|
-
export type { PdfFormat } from './pdf/document-pdf.service';
|
|
13
|
+
export type { PdfFormat, PdfBranding, PdfGenerationOptions } from './pdf/document-pdf.service';
|
|
14
|
+
export { PdfLicenseError } from './licensing/pdf-license.service';
|
|
15
|
+
export type { PdfLicensePayload } from './licensing/pdf-license.service';
|
|
14
16
|
export { buildInvoiceXml } from './xml/invoice.builder';
|
|
15
17
|
export { buildNoteXml } from './xml/note.builder';
|
|
16
18
|
export { buildDespatchXml } from './xml/despatch.builder';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildVoidedXml = exports.buildSummaryXml = exports.buildDespatchXml = exports.buildNoteXml = exports.buildInvoiceXml = exports.DocumentPdfService = exports.CdrParserService = exports.OseClient = exports.SunatGreClient = exports.FakeSunatSoapClient = exports.SunatSoapClient = exports.XmlSignerService = exports.SunatEngineService = exports.SUNAT_ENGINE_OPTIONS = exports.SunatEngineModule = void 0;
|
|
3
|
+
exports.buildVoidedXml = exports.buildSummaryXml = exports.buildDespatchXml = exports.buildNoteXml = exports.buildInvoiceXml = exports.PdfLicenseError = exports.DocumentPdfService = exports.CdrParserService = exports.OseClient = exports.SunatGreClient = exports.FakeSunatSoapClient = exports.SunatSoapClient = exports.XmlSignerService = exports.SunatEngineService = exports.SUNAT_ENGINE_OPTIONS = exports.SunatEngineModule = void 0;
|
|
4
4
|
var sunat_engine_module_1 = require("./sunat-engine.module");
|
|
5
5
|
Object.defineProperty(exports, "SunatEngineModule", { enumerable: true, get: function () { return sunat_engine_module_1.SunatEngineModule; } });
|
|
6
6
|
Object.defineProperty(exports, "SUNAT_ENGINE_OPTIONS", { enumerable: true, get: function () { return sunat_engine_module_1.SUNAT_ENGINE_OPTIONS; } });
|
|
@@ -20,6 +20,8 @@ var cdr_parser_service_1 = require("./cdr/cdr-parser.service");
|
|
|
20
20
|
Object.defineProperty(exports, "CdrParserService", { enumerable: true, get: function () { return cdr_parser_service_1.CdrParserService; } });
|
|
21
21
|
var document_pdf_service_1 = require("./pdf/document-pdf.service");
|
|
22
22
|
Object.defineProperty(exports, "DocumentPdfService", { enumerable: true, get: function () { return document_pdf_service_1.DocumentPdfService; } });
|
|
23
|
+
var pdf_license_service_1 = require("./licensing/pdf-license.service");
|
|
24
|
+
Object.defineProperty(exports, "PdfLicenseError", { enumerable: true, get: function () { return pdf_license_service_1.PdfLicenseError; } });
|
|
23
25
|
var invoice_builder_1 = require("./xml/invoice.builder");
|
|
24
26
|
Object.defineProperty(exports, "buildInvoiceXml", { enumerable: true, get: function () { return invoice_builder_1.buildInvoiceXml; } });
|
|
25
27
|
var note_builder_1 = require("./xml/note.builder");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,6DAAgF;AAAvE,wHAAA,iBAAiB,OAAA;AAAE,2HAAA,oBAAoB,OAAA;AAIhD,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAG3B,kEAA+D;AAAtD,sHAAA,gBAAgB,OAAA;AACzB,8DAA2D;AAAlD,oHAAA,eAAe,OAAA;AAExB,wEAAoE;AAA3D,6HAAA,mBAAmB,OAAA;AAC5B,2DAAwD;AAA/C,kHAAA,cAAc,OAAA;AACvB,+CAA6C;AAApC,uGAAA,SAAS,OAAA;AAElB,+DAA4D;AAAnD,sHAAA,gBAAgB,OAAA;AACzB,mEAAgE;AAAvD,0HAAA,kBAAkB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,6DAAgF;AAAvE,wHAAA,iBAAiB,OAAA;AAAE,2HAAA,oBAAoB,OAAA;AAIhD,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAG3B,kEAA+D;AAAtD,sHAAA,gBAAgB,OAAA;AACzB,8DAA2D;AAAlD,oHAAA,eAAe,OAAA;AAExB,wEAAoE;AAA3D,6HAAA,mBAAmB,OAAA;AAC5B,2DAAwD;AAA/C,kHAAA,cAAc,OAAA;AACvB,+CAA6C;AAApC,uGAAA,SAAS,OAAA;AAElB,+DAA4D;AAAnD,sHAAA,gBAAgB,OAAA;AACzB,mEAAgE;AAAvD,0HAAA,kBAAkB,OAAA;AAE3B,uEAAkE;AAAzD,sHAAA,eAAe,OAAA;AAIxB,yDAAwD;AAA/C,kHAAA,eAAe,OAAA;AACxB,mDAAkD;AAAzC,4GAAA,YAAY,OAAA;AACrB,2DAA0D;AAAjD,oHAAA,gBAAgB,OAAA;AACzB,yDAAwD;AAA/C,kHAAA,eAAe,OAAA;AACxB,uDAAsD;AAA7C,gHAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PdfLicensePayload {
|
|
2
|
+
sub: string;
|
|
3
|
+
scope: string[];
|
|
4
|
+
iat: number;
|
|
5
|
+
exp?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class PdfLicenseError extends Error {
|
|
8
|
+
}
|
|
9
|
+
export declare class PdfLicenseService {
|
|
10
|
+
assertValid(licenseKey: string | undefined): PdfLicensePayload;
|
|
11
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PdfLicenseService = exports.PdfLicenseError = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const crypto_1 = require("crypto");
|
|
12
|
+
const public_key_1 = require("./public-key");
|
|
13
|
+
class PdfLicenseError extends Error {
|
|
14
|
+
}
|
|
15
|
+
exports.PdfLicenseError = PdfLicenseError;
|
|
16
|
+
const CONTACT = 'https://wa.me/51907596305';
|
|
17
|
+
let PdfLicenseService = class PdfLicenseService {
|
|
18
|
+
assertValid(licenseKey) {
|
|
19
|
+
if (!licenseKey) {
|
|
20
|
+
throw new PdfLicenseError(`La generación de PDF requiere una licencia. Configura "pdf.licenseKey" en SunatEngineModule.forRoot() ` +
|
|
21
|
+
`o adquiere una en ${CONTACT}.`);
|
|
22
|
+
}
|
|
23
|
+
const [payloadB64, sigB64] = licenseKey.split('.');
|
|
24
|
+
if (!payloadB64 || !sigB64) {
|
|
25
|
+
throw new PdfLicenseError('Formato de licencia inválido.');
|
|
26
|
+
}
|
|
27
|
+
let payload;
|
|
28
|
+
try {
|
|
29
|
+
payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf8'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new PdfLicenseError('Licencia inválida: el payload está corrupto.');
|
|
33
|
+
}
|
|
34
|
+
const signatureValid = (0, crypto_1.verify)(null, Buffer.from(payloadB64), public_key_1.PDF_LICENSE_PUBLIC_KEY, Buffer.from(sigB64, 'base64url'));
|
|
35
|
+
if (!signatureValid) {
|
|
36
|
+
throw new PdfLicenseError('Licencia inválida: la firma no coincide.');
|
|
37
|
+
}
|
|
38
|
+
if (!payload.scope?.includes('pdf')) {
|
|
39
|
+
throw new PdfLicenseError('Esta licencia no incluye el módulo de PDF.');
|
|
40
|
+
}
|
|
41
|
+
if (payload.exp && Date.now() / 1000 > payload.exp) {
|
|
42
|
+
throw new PdfLicenseError(`La licencia de PDF expiró. Renueva en ${CONTACT}.`);
|
|
43
|
+
}
|
|
44
|
+
return payload;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.PdfLicenseService = PdfLicenseService;
|
|
48
|
+
exports.PdfLicenseService = PdfLicenseService = __decorate([
|
|
49
|
+
(0, common_1.Injectable)()
|
|
50
|
+
], PdfLicenseService);
|
|
51
|
+
//# sourceMappingURL=pdf-license.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-license.service.js","sourceRoot":"","sources":["../../src/licensing/pdf-license.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAC5C,mCAAmD;AACnD,6CAAsD;AAatD,MAAa,eAAgB,SAAQ,KAAK;CAAG;AAA7C,0CAA6C;AAE7C,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAGrC,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAM5B,WAAW,CAAC,UAA8B;QACxC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CACvB,wGAAwG;gBACxG,qBAAqB,OAAO,GAAG,CAChC,CAAC;QACJ,CAAC;QAED,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3B,MAAM,IAAI,eAAe,CAAC,+BAA+B,CAAC,CAAC;QAC7D,CAAC;QAED,IAAI,OAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9E,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,eAAe,CAAC,8CAA8C,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,cAAc,GAAG,IAAA,eAAe,EACpC,IAAI,EACJ,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EACvB,mCAAsB,EACtB,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CACjC,CAAC;QACF,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,eAAe,CAAC,0CAA0C,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,eAAe,CAAC,4CAA4C,CAAC,CAAC;QAC1E,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YACnD,MAAM,IAAI,eAAe,CAAC,yCAAyC,OAAO,GAAG,CAAC,CAAC;QACjF,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AA9CY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;GACA,iBAAiB,CA8C7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PDF_LICENSE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAD05kwpP38o2RE03c5mS66KDa+KGN3UPUtiAngewbzPI=\n-----END PUBLIC KEY-----\n";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PDF_LICENSE_PUBLIC_KEY = void 0;
|
|
4
|
+
exports.PDF_LICENSE_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
5
|
+
MCowBQYDK2VwAyEAD05kwpP38o2RE03c5mS66KDa+KGN3UPUtiAngewbzPI=
|
|
6
|
+
-----END PUBLIC KEY-----
|
|
7
|
+
`;
|
|
8
|
+
//# sourceMappingURL=public-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-key.js","sourceRoot":"","sources":["../../src/licensing/public-key.ts"],"names":[],"mappings":";;;AAEa,QAAA,sBAAsB,GAAG;;;CAGrC,CAAC"}
|
|
@@ -1,11 +1,40 @@
|
|
|
1
|
-
import { InvoicePayload } from '../types/sunat.types';
|
|
1
|
+
import { InvoicePayload, NotePayload, DespatchPayload } from '../types/sunat.types';
|
|
2
|
+
import { SunatEngineOptions } from '../sunat-engine.module';
|
|
3
|
+
import { PdfLicenseService } from '../licensing/pdf-license.service';
|
|
2
4
|
export type PdfFormat = 'A4' | 'TICKET_80MM' | 'STICKER_A6';
|
|
5
|
+
export interface PdfBranding {
|
|
6
|
+
logoBase64?: string;
|
|
7
|
+
color?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PdfGenerationOptions {
|
|
10
|
+
licenseKey?: string;
|
|
11
|
+
branding?: PdfBranding;
|
|
12
|
+
}
|
|
3
13
|
export declare class DocumentPdfService {
|
|
4
|
-
|
|
5
|
-
private
|
|
6
|
-
|
|
14
|
+
private readonly options;
|
|
15
|
+
private readonly license;
|
|
16
|
+
constructor(options?: SunatEngineOptions, license?: PdfLicenseService);
|
|
17
|
+
generateInvoice(payload: InvoicePayload, format?: PdfFormat, opts?: PdfGenerationOptions): Promise<Buffer>;
|
|
18
|
+
private generateInvoiceA4;
|
|
19
|
+
private generateInvoiceTicket;
|
|
20
|
+
generateNote(payload: NotePayload, format?: PdfFormat, opts?: PdfGenerationOptions): Promise<Buffer>;
|
|
21
|
+
private generateNoteA4;
|
|
22
|
+
private generateNoteTicket;
|
|
23
|
+
generateDespatch(payload: DespatchPayload, opts?: PdfGenerationOptions): Promise<Buffer>;
|
|
24
|
+
private buildHeader;
|
|
25
|
+
private buildItemsTable;
|
|
26
|
+
private buildTotalsQrBlock;
|
|
27
|
+
private cardLayout;
|
|
28
|
+
private zebraTableLayout;
|
|
29
|
+
private baseStyles;
|
|
30
|
+
private resolveBranding;
|
|
31
|
+
private ticketHeader;
|
|
32
|
+
private ticketDivider;
|
|
33
|
+
private ticketDetailRow;
|
|
7
34
|
private totRow;
|
|
8
35
|
private buildQrContent;
|
|
36
|
+
private qr;
|
|
9
37
|
private fmt;
|
|
38
|
+
private lighten;
|
|
10
39
|
private renderPdf;
|
|
11
40
|
}
|