@dwast/verifactu-lib 0.1.0-beta.0 → 0.1.0-beta.2
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/dist/cjs/aeat/client.d.ts +162 -0
- package/dist/cjs/aeat/client.d.ts.map +1 -0
- package/dist/cjs/aeat/client.js +267 -0
- package/dist/cjs/aeat/client.js.map +1 -0
- package/dist/cjs/aeat/index.d.ts +3 -0
- package/dist/cjs/aeat/index.d.ts.map +1 -0
- package/dist/cjs/aeat/index.js +19 -0
- package/dist/cjs/aeat/index.js.map +1 -0
- package/dist/cjs/aeat/response.d.ts +78 -0
- package/dist/cjs/aeat/response.d.ts.map +1 -0
- package/dist/cjs/aeat/response.js +117 -0
- package/dist/cjs/aeat/response.js.map +1 -0
- package/dist/{core → cjs/core}/hash/chain.d.ts +42 -0
- package/dist/cjs/core/hash/chain.d.ts.map +1 -0
- package/dist/cjs/core/hash/chain.js +167 -0
- package/dist/cjs/core/hash/chain.js.map +1 -0
- package/dist/cjs/core/hash/index.d.ts.map +1 -0
- package/dist/cjs/core/hash/index.js +18 -0
- package/dist/cjs/core/hash/index.js.map +1 -0
- package/dist/{core → cjs/core}/index.d.ts +2 -0
- package/dist/cjs/core/index.d.ts.map +1 -0
- package/dist/cjs/core/index.js +23 -0
- package/dist/cjs/core/index.js.map +1 -0
- package/dist/cjs/core/models/event.d.ts.map +1 -0
- package/dist/cjs/core/models/event.js +3 -0
- package/dist/{core → cjs/core}/models/event.js.map +1 -1
- package/dist/cjs/core/models/index.d.ts.map +1 -0
- package/dist/cjs/core/models/index.js +19 -0
- package/dist/cjs/core/models/index.js.map +1 -0
- package/dist/{core → cjs/core}/models/invoice.d.ts +42 -17
- package/dist/cjs/core/models/invoice.d.ts.map +1 -0
- package/dist/cjs/core/models/invoice.js +3 -0
- package/dist/{core → cjs/core}/models/invoice.js.map +1 -1
- package/dist/cjs/core/qr/index.d.ts.map +1 -0
- package/dist/cjs/core/qr/index.js +18 -0
- package/dist/cjs/core/qr/index.js.map +1 -0
- package/dist/cjs/core/qr/qr.d.ts.map +1 -0
- package/dist/cjs/core/qr/qr.js +19 -0
- package/dist/cjs/core/qr/qr.js.map +1 -0
- package/dist/cjs/core/utils/date.d.ts +16 -0
- package/dist/cjs/core/utils/date.d.ts.map +1 -0
- package/dist/cjs/core/utils/date.js +27 -0
- package/dist/cjs/core/utils/date.js.map +1 -0
- package/dist/cjs/core/utils/index.d.ts +2 -0
- package/dist/cjs/core/utils/index.d.ts.map +1 -0
- package/dist/cjs/core/utils/index.js +18 -0
- package/dist/cjs/core/utils/index.js.map +1 -0
- package/dist/cjs/core/validation/index.d.ts +2 -0
- package/dist/cjs/core/validation/index.d.ts.map +1 -0
- package/dist/cjs/core/validation/index.js +18 -0
- package/dist/cjs/core/validation/index.js.map +1 -0
- package/dist/cjs/core/validation/validate.d.ts +18 -0
- package/dist/cjs/core/validation/validate.d.ts.map +1 -0
- package/dist/cjs/core/validation/validate.js +65 -0
- package/dist/cjs/core/validation/validate.js.map +1 -0
- package/dist/cjs/core/xml/generator.d.ts +140 -0
- package/dist/cjs/core/xml/generator.d.ts.map +1 -0
- package/dist/cjs/core/xml/generator.js +269 -0
- package/dist/cjs/core/xml/generator.js.map +1 -0
- package/dist/cjs/core/xml/index.d.ts.map +1 -0
- package/dist/cjs/core/xml/index.js +18 -0
- package/dist/cjs/core/xml/index.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/integration/accountability.d.ts +41 -0
- package/dist/cjs/integration/accountability.d.ts.map +1 -0
- package/dist/cjs/integration/accountability.js +92 -0
- package/dist/cjs/integration/accountability.js.map +1 -0
- package/dist/cjs/integration/index.d.ts +5 -0
- package/dist/cjs/integration/index.d.ts.map +1 -0
- package/dist/cjs/integration/index.js +21 -0
- package/dist/cjs/integration/index.js.map +1 -0
- package/dist/cjs/integration/p12.d.ts +58 -0
- package/dist/cjs/integration/p12.d.ts.map +1 -0
- package/dist/cjs/integration/p12.js +127 -0
- package/dist/cjs/integration/p12.js.map +1 -0
- package/dist/{integration → cjs/integration}/repository.d.ts +2 -0
- package/dist/cjs/integration/repository.d.ts.map +1 -0
- package/dist/cjs/integration/repository.js +3 -0
- package/dist/cjs/integration/repository.js.map +1 -0
- package/dist/{integration → cjs/integration}/service.d.ts +11 -2
- package/dist/cjs/integration/service.d.ts.map +1 -0
- package/dist/cjs/integration/service.js +230 -0
- package/dist/cjs/integration/service.js.map +1 -0
- package/dist/cjs/integration/tenant-credentials.d.ts +44 -0
- package/dist/cjs/integration/tenant-credentials.d.ts.map +1 -0
- package/dist/cjs/integration/tenant-credentials.js +62 -0
- package/dist/cjs/integration/tenant-credentials.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/aeat/client.d.ts +162 -0
- package/dist/esm/aeat/client.d.ts.map +1 -0
- package/dist/esm/aeat/client.js +259 -0
- package/dist/esm/aeat/client.js.map +1 -0
- package/dist/esm/aeat/index.d.ts +3 -0
- package/dist/esm/aeat/index.d.ts.map +1 -0
- package/dist/esm/aeat/index.js +3 -0
- package/dist/esm/aeat/index.js.map +1 -0
- package/dist/esm/aeat/response.d.ts +78 -0
- package/dist/esm/aeat/response.d.ts.map +1 -0
- package/dist/esm/aeat/response.js +114 -0
- package/dist/esm/aeat/response.js.map +1 -0
- package/dist/esm/core/hash/chain.d.ts +139 -0
- package/dist/esm/core/hash/chain.d.ts.map +1 -0
- package/dist/{core → esm/core}/hash/chain.js +37 -0
- package/dist/esm/core/hash/chain.js.map +1 -0
- package/dist/esm/core/hash/index.d.ts +2 -0
- package/dist/esm/core/hash/index.d.ts.map +1 -0
- package/dist/esm/core/hash/index.js.map +1 -0
- package/dist/esm/core/index.d.ts +7 -0
- package/dist/esm/core/index.d.ts.map +1 -0
- package/dist/{core → esm/core}/index.js +2 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/core/models/event.d.ts +25 -0
- package/dist/esm/core/models/event.d.ts.map +1 -0
- package/dist/esm/core/models/event.js.map +1 -0
- package/dist/esm/core/models/index.d.ts +3 -0
- package/dist/esm/core/models/index.d.ts.map +1 -0
- package/dist/esm/core/models/index.js.map +1 -0
- package/dist/esm/core/models/invoice.d.ts +115 -0
- package/dist/esm/core/models/invoice.d.ts.map +1 -0
- package/dist/esm/core/models/invoice.js.map +1 -0
- package/dist/esm/core/qr/index.d.ts +2 -0
- package/dist/esm/core/qr/index.d.ts.map +1 -0
- package/dist/esm/core/qr/index.js.map +1 -0
- package/dist/esm/core/qr/qr.d.ts +13 -0
- package/dist/esm/core/qr/qr.d.ts.map +1 -0
- package/dist/esm/core/qr/qr.js.map +1 -0
- package/dist/esm/core/utils/date.d.ts +16 -0
- package/dist/esm/core/utils/date.d.ts.map +1 -0
- package/dist/esm/core/utils/date.js +24 -0
- package/dist/esm/core/utils/date.js.map +1 -0
- package/dist/esm/core/utils/index.d.ts +2 -0
- package/dist/esm/core/utils/index.d.ts.map +1 -0
- package/dist/esm/core/utils/index.js +2 -0
- package/dist/esm/core/utils/index.js.map +1 -0
- package/dist/esm/core/validation/index.d.ts +2 -0
- package/dist/esm/core/validation/index.d.ts.map +1 -0
- package/dist/esm/core/validation/index.js +2 -0
- package/dist/esm/core/validation/index.js.map +1 -0
- package/dist/esm/core/validation/validate.d.ts +18 -0
- package/dist/esm/core/validation/validate.d.ts.map +1 -0
- package/dist/esm/core/validation/validate.js +62 -0
- package/dist/esm/core/validation/validate.js.map +1 -0
- package/dist/esm/core/xml/generator.d.ts +140 -0
- package/dist/esm/core/xml/generator.d.ts.map +1 -0
- package/dist/esm/core/xml/generator.js +259 -0
- package/dist/esm/core/xml/generator.js.map +1 -0
- package/dist/esm/core/xml/index.d.ts +2 -0
- package/dist/esm/core/xml/index.d.ts.map +1 -0
- package/dist/esm/core/xml/index.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/integration/accountability.d.ts +41 -0
- package/dist/esm/integration/accountability.d.ts.map +1 -0
- package/dist/esm/integration/accountability.js +88 -0
- package/dist/esm/integration/accountability.js.map +1 -0
- package/dist/esm/integration/index.d.ts +5 -0
- package/dist/esm/integration/index.d.ts.map +1 -0
- package/dist/esm/integration/index.js +5 -0
- package/dist/esm/integration/index.js.map +1 -0
- package/dist/esm/integration/p12.d.ts +58 -0
- package/dist/esm/integration/p12.d.ts.map +1 -0
- package/dist/esm/integration/p12.js +119 -0
- package/dist/esm/integration/p12.js.map +1 -0
- package/dist/esm/integration/repository.d.ts +79 -0
- package/dist/esm/integration/repository.d.ts.map +1 -0
- package/dist/esm/integration/repository.js.map +1 -0
- package/dist/esm/integration/service.d.ts +45 -0
- package/dist/esm/integration/service.d.ts.map +1 -0
- package/dist/{integration → esm/integration}/service.js +101 -15
- package/dist/esm/integration/service.js.map +1 -0
- package/dist/esm/integration/tenant-credentials.d.ts +44 -0
- package/dist/esm/integration/tenant-credentials.d.ts.map +1 -0
- package/dist/esm/integration/tenant-credentials.js +58 -0
- package/dist/esm/integration/tenant-credentials.js.map +1 -0
- package/package.json +45 -13
- package/readme.md +566 -42
- package/src/aeat/client.ts +296 -55
- package/src/aeat/index.ts +1 -0
- package/src/aeat/response.ts +195 -0
- package/src/core/hash/chain.ts +68 -0
- package/src/core/index.ts +2 -0
- package/src/core/models/invoice.ts +40 -18
- package/src/core/utils/date.ts +27 -0
- package/src/core/utils/index.ts +1 -0
- package/src/core/validation/index.ts +1 -0
- package/src/core/validation/validate.ts +71 -0
- package/src/core/xml/generator.ts +306 -73
- package/src/integration/accountability.ts +125 -0
- package/src/integration/index.ts +2 -0
- package/src/integration/p12.ts +155 -0
- package/src/integration/repository.ts +2 -0
- package/src/integration/service.ts +113 -14
- package/dist/aeat/client.d.ts +0 -57
- package/dist/aeat/client.d.ts.map +0 -1
- package/dist/aeat/client.js +0 -87
- package/dist/aeat/client.js.map +0 -1
- package/dist/aeat/index.d.ts +0 -2
- package/dist/aeat/index.d.ts.map +0 -1
- package/dist/aeat/index.js +0 -2
- package/dist/aeat/index.js.map +0 -1
- package/dist/core/hash/chain.d.ts.map +0 -1
- package/dist/core/hash/chain.js.map +0 -1
- package/dist/core/hash/index.d.ts.map +0 -1
- package/dist/core/hash/index.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/models/event.d.ts.map +0 -1
- package/dist/core/models/index.d.ts.map +0 -1
- package/dist/core/models/index.js.map +0 -1
- package/dist/core/models/invoice.d.ts.map +0 -1
- package/dist/core/qr/index.d.ts.map +0 -1
- package/dist/core/qr/index.js.map +0 -1
- package/dist/core/qr/qr.d.ts.map +0 -1
- package/dist/core/qr/qr.js.map +0 -1
- package/dist/core/xml/generator.d.ts +0 -32
- package/dist/core/xml/generator.d.ts.map +0 -1
- package/dist/core/xml/generator.js +0 -103
- package/dist/core/xml/generator.js.map +0 -1
- package/dist/core/xml/index.d.ts.map +0 -1
- package/dist/core/xml/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integration/index.d.ts +0 -3
- package/dist/integration/index.d.ts.map +0 -1
- package/dist/integration/index.js +0 -3
- package/dist/integration/index.js.map +0 -1
- package/dist/integration/repository.d.ts.map +0 -1
- package/dist/integration/repository.js.map +0 -1
- package/dist/integration/service.d.ts.map +0 -1
- package/dist/integration/service.js.map +0 -1
- /package/dist/{core → cjs/core}/hash/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/models/event.d.ts +0 -0
- /package/dist/{core → cjs/core}/models/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/qr/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/qr/qr.d.ts +0 -0
- /package/dist/{core → cjs/core}/xml/index.d.ts +0 -0
- /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
- /package/dist/{core → esm/core}/hash/index.js +0 -0
- /package/dist/{core → esm/core}/models/event.js +0 -0
- /package/dist/{core → esm/core}/models/index.js +0 -0
- /package/dist/{core → esm/core}/models/invoice.js +0 -0
- /package/dist/{core → esm/core}/qr/index.js +0 -0
- /package/dist/{core → esm/core}/qr/qr.js +0 -0
- /package/dist/{core → esm/core}/xml/index.js +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{integration → esm/integration}/repository.js +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VerifactuService = void 0;
|
|
4
|
+
const generator_js_1 = require("../core/xml/generator.js");
|
|
5
|
+
const chain_js_1 = require("../core/hash/chain.js");
|
|
6
|
+
const qr_js_1 = require("../core/qr/qr.js");
|
|
7
|
+
const date_js_1 = require("../core/utils/date.js");
|
|
8
|
+
const validate_js_1 = require("../core/validation/validate.js");
|
|
9
|
+
const p12_js_1 = require("./p12.js");
|
|
10
|
+
/** Convierte `YYYY-MM-DD` a `DD-MM-YYYY` (formato XSD `fecha` de AEAT). */
|
|
11
|
+
function toDdMmYyyy(iso) {
|
|
12
|
+
const [y, m, d] = iso.split('-');
|
|
13
|
+
return `${d}-${m}-${y}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Servicio de orquestación de Verifactu.
|
|
17
|
+
*
|
|
18
|
+
* Une el núcleo puro (generación XML + hash) con el cliente AEAT y la
|
|
19
|
+
* persistencia. El sistema consumidor DEBE proporcionar un repositorio que
|
|
20
|
+
* implemente `VerifactuRepository` (ver `repository.ts` y `schema.sql`).
|
|
21
|
+
*/
|
|
22
|
+
class VerifactuService {
|
|
23
|
+
repository;
|
|
24
|
+
client;
|
|
25
|
+
certificatePem;
|
|
26
|
+
constructor(repository, client, certificatePem) {
|
|
27
|
+
this.repository = repository;
|
|
28
|
+
this.client = client;
|
|
29
|
+
this.certificatePem = certificatePem;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Registra una factura: genera el XML, calcula el hash encadenado, guarda
|
|
33
|
+
* el registro y lo envía a AEAT, persistiendo la respuesta.
|
|
34
|
+
*/
|
|
35
|
+
async registerInvoice(invoice) {
|
|
36
|
+
// 0. Validar la factura antes de generar el XML.
|
|
37
|
+
const validation = (0, validate_js_1.validateInvoice)(invoice);
|
|
38
|
+
if (!validation.valid) {
|
|
39
|
+
throw new Error(`Factura inválida: ${validation.errors.join('; ')}`);
|
|
40
|
+
}
|
|
41
|
+
// 1. Obtener el hash del último registro para encadenar.
|
|
42
|
+
const last = await this.repository.getLastRecord();
|
|
43
|
+
const previousHash = last?.hash ?? null;
|
|
44
|
+
// 2. Generar XML y calcular la huella (hash) del registro.
|
|
45
|
+
const registroAltaXml = (0, generator_js_1.generateInvoiceXml)(invoice, previousHash);
|
|
46
|
+
const cabecera = (0, generator_js_1.buildCabecera)({ nombreRazon: invoice.emisor.nombreRazon, nif: invoice.emisor.nif }, this.certificatePem, p12_js_1.extractCertificateInfo);
|
|
47
|
+
const xml = (0, generator_js_1.generateSuministroXml)(registroAltaXml, cabecera);
|
|
48
|
+
const fechaHoraHusoGenRegistro = (0, date_js_1.nowIsoWithOffset)();
|
|
49
|
+
const hash = (0, chain_js_1.computeRegistroAlta)({
|
|
50
|
+
idEmisorFactura: invoice.emisor.nif,
|
|
51
|
+
numSerieFactura: `${invoice.id.serie ?? ''}${invoice.id.numero}`,
|
|
52
|
+
fechaExpedicionFactura: invoice.id.fechaExpedicion,
|
|
53
|
+
tipoFactura: invoice.tipoFactura,
|
|
54
|
+
cuotaTotal: invoice.desglose.cuotaTotal.toFixed(2),
|
|
55
|
+
importeTotal: invoice.desglose.importeTotal.toFixed(2),
|
|
56
|
+
huellaAnterior: previousHash,
|
|
57
|
+
fechaHoraHusoGenRegistro,
|
|
58
|
+
});
|
|
59
|
+
// 3. Persistir el registro como PENDING.
|
|
60
|
+
const now = new Date().toISOString();
|
|
61
|
+
const record = {
|
|
62
|
+
id: `${invoice.id.serie ?? ''}${invoice.id.numero}`,
|
|
63
|
+
invoiceId: `${invoice.id.serie ?? ''}${invoice.id.numero}`,
|
|
64
|
+
emisorNif: invoice.emisor.nif,
|
|
65
|
+
xml,
|
|
66
|
+
hash,
|
|
67
|
+
previousHash,
|
|
68
|
+
qrPayload: (0, qr_js_1.buildQrPayload)(hash),
|
|
69
|
+
status: 'PENDING',
|
|
70
|
+
createdAt: now,
|
|
71
|
+
updatedAt: now,
|
|
72
|
+
};
|
|
73
|
+
await this.repository.saveRecord(record);
|
|
74
|
+
// 4. Enviar a AEAT y persistir la respuesta.
|
|
75
|
+
const result = await this.client.sendRecord(xml);
|
|
76
|
+
await this.repository.saveResponse({
|
|
77
|
+
id: `${record.id}-${Date.now()}`,
|
|
78
|
+
recordId: record.id,
|
|
79
|
+
operationId: result.operationId,
|
|
80
|
+
status: result.status,
|
|
81
|
+
message: result.message,
|
|
82
|
+
receivedAt: new Date().toISOString(),
|
|
83
|
+
});
|
|
84
|
+
// 5. Actualizar el estado del registro según la respuesta.
|
|
85
|
+
const status = result.status === 'OK' ? 'CONFIRMED' : 'REJECTED';
|
|
86
|
+
await this.repository.updateRecordStatus(record.id, status);
|
|
87
|
+
return { ...record, status };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Anula una factura previamente registrada.
|
|
91
|
+
*
|
|
92
|
+
* Genera un registro de anulación que referencia la factura anulada,
|
|
93
|
+
* lo envía a AEAT y marca la factura original como ANNULLED.
|
|
94
|
+
*/
|
|
95
|
+
async annulInvoice(invoiceId, invoice) {
|
|
96
|
+
// 1. Obtener la factura original (para su huella) y el último registro.
|
|
97
|
+
const original = await this.repository.getRecord(invoiceId);
|
|
98
|
+
if (!original) {
|
|
99
|
+
throw new Error(`No existe el registro a anular: ${invoiceId}`);
|
|
100
|
+
}
|
|
101
|
+
const last = await this.repository.getLastRecord();
|
|
102
|
+
const previousHash = last?.hash ?? null;
|
|
103
|
+
const fechaHoraHusoGenRegistro = (0, date_js_1.nowIsoWithOffset)();
|
|
104
|
+
const fechaExpedicionFacturaAnulada = toDdMmYyyy(invoice?.id.fechaExpedicion ?? new Date().toISOString().slice(0, 10));
|
|
105
|
+
const sistemaInformatico = invoice?.sistemaInformatico ?? {
|
|
106
|
+
nombreRazon: 'Sistema',
|
|
107
|
+
nif: original.emisorNif,
|
|
108
|
+
nombreSistemaInformatico: 'SIF',
|
|
109
|
+
idSistemaInformatico: '01',
|
|
110
|
+
version: '1.0',
|
|
111
|
+
numeroInstalacion: '1',
|
|
112
|
+
};
|
|
113
|
+
// 2. Generar XML de anulación y calcular la huella.
|
|
114
|
+
const hash = (0, chain_js_1.computeRegistroAnulacion)({
|
|
115
|
+
idEmisorFacturaAnulada: original.emisorNif,
|
|
116
|
+
numSerieFacturaAnulada: original.invoiceId,
|
|
117
|
+
fechaExpedicionFacturaAnulada,
|
|
118
|
+
huellaAnterior: previousHash,
|
|
119
|
+
fechaHoraHusoGenRegistro,
|
|
120
|
+
});
|
|
121
|
+
const xml = (0, generator_js_1.generateAnulacionXml)({
|
|
122
|
+
idEmisorFacturaAnulada: original.emisorNif,
|
|
123
|
+
numSerieFacturaAnulada: original.invoiceId,
|
|
124
|
+
fechaExpedicionFacturaAnulada,
|
|
125
|
+
huellaFacturaAnulada: original.hash,
|
|
126
|
+
sistemaInformatico,
|
|
127
|
+
fechaHoraHusoGenRegistro,
|
|
128
|
+
huella: hash,
|
|
129
|
+
}, previousHash);
|
|
130
|
+
// 3. Persistir el registro de anulación como PENDING.
|
|
131
|
+
const now = new Date().toISOString();
|
|
132
|
+
const record = {
|
|
133
|
+
id: `${invoiceId}-ANUL`,
|
|
134
|
+
invoiceId,
|
|
135
|
+
emisorNif: original.emisorNif,
|
|
136
|
+
xml,
|
|
137
|
+
hash,
|
|
138
|
+
previousHash,
|
|
139
|
+
status: 'PENDING',
|
|
140
|
+
createdAt: now,
|
|
141
|
+
updatedAt: now,
|
|
142
|
+
};
|
|
143
|
+
await this.repository.saveRecord(record);
|
|
144
|
+
// 4. Enviar a AEAT y persistir la respuesta.
|
|
145
|
+
const result = await this.client.sendRecord(xml);
|
|
146
|
+
await this.repository.saveResponse({
|
|
147
|
+
id: `${record.id}-${Date.now()}`,
|
|
148
|
+
recordId: record.id,
|
|
149
|
+
operationId: result.operationId,
|
|
150
|
+
status: result.status,
|
|
151
|
+
message: result.message,
|
|
152
|
+
receivedAt: new Date().toISOString(),
|
|
153
|
+
});
|
|
154
|
+
// 5. Actualizar estados.
|
|
155
|
+
const status = result.status === 'OK' ? 'CONFIRMED' : 'REJECTED';
|
|
156
|
+
await this.repository.updateRecordStatus(record.id, status);
|
|
157
|
+
if (result.status === 'OK') {
|
|
158
|
+
await this.repository.updateRecordStatus(invoiceId, 'ANNULLED');
|
|
159
|
+
}
|
|
160
|
+
return { ...record, status };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Modifica una factura previamente registrada.
|
|
164
|
+
*
|
|
165
|
+
* Verifactu no tiene un tipo "modificación": se implementa como anulación
|
|
166
|
+
* del registro original + alta con los datos corregidos. El usuario solo
|
|
167
|
+
* envía la referencia a la factura original y la factura corregida.
|
|
168
|
+
*/
|
|
169
|
+
async modifyInvoice(invoiceId, correctedInvoice) {
|
|
170
|
+
// 1. Anular la factura original.
|
|
171
|
+
await this.annulInvoice(invoiceId);
|
|
172
|
+
// 2. Emitir la factura corregida como un nuevo alta.
|
|
173
|
+
return this.registerInvoice(correctedInvoice);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Registra un evento (RegistroEvento) ante AEAT.
|
|
177
|
+
*
|
|
178
|
+
* Genera el XML del evento, calcula su huella encadenada con el último
|
|
179
|
+
* registro, persiste el evento y lo envía a AEAT.
|
|
180
|
+
*/
|
|
181
|
+
async registerEvent(evento) {
|
|
182
|
+
// 1. Obtener el hash del último registro para encadenar.
|
|
183
|
+
const last = await this.repository.getLastRecord();
|
|
184
|
+
const previousHash = last?.hash ?? null;
|
|
185
|
+
// 2. Generar XML y calcular la huella del evento.
|
|
186
|
+
const si = evento.sistemaInformatico;
|
|
187
|
+
const hash = (0, chain_js_1.computeRegistroEvento)({
|
|
188
|
+
nifSistemaInformatico: si.nif,
|
|
189
|
+
id: '',
|
|
190
|
+
idSistemaInformatico: si.idSistemaInformatico,
|
|
191
|
+
version: si.version,
|
|
192
|
+
numeroInstalacion: si.numeroInstalacion,
|
|
193
|
+
nifObligado: evento.obligadoEmision.nif,
|
|
194
|
+
tipoEvento: evento.tipoEvento,
|
|
195
|
+
huellaAnterior: previousHash,
|
|
196
|
+
fechaHoraHusoGenEvento: evento.fechaHoraHusoGenEvento,
|
|
197
|
+
});
|
|
198
|
+
const xml = (0, generator_js_1.generateEventoXml)({ ...evento, huellaEvento: hash });
|
|
199
|
+
// 3. Persistir el evento.
|
|
200
|
+
const now = new Date().toISOString();
|
|
201
|
+
const record = {
|
|
202
|
+
id: `EV-${evento.tipoEvento}-${Date.now()}`,
|
|
203
|
+
invoiceId: evento.obligadoEmision.nif,
|
|
204
|
+
emisorNif: evento.obligadoEmision.nif,
|
|
205
|
+
xml,
|
|
206
|
+
hash,
|
|
207
|
+
previousHash,
|
|
208
|
+
status: 'PENDING',
|
|
209
|
+
createdAt: now,
|
|
210
|
+
updatedAt: now,
|
|
211
|
+
};
|
|
212
|
+
await this.repository.saveRecord(record);
|
|
213
|
+
// 4. Enviar a AEAT y persistir la respuesta.
|
|
214
|
+
const result = await this.client.sendRecord(xml);
|
|
215
|
+
await this.repository.saveResponse({
|
|
216
|
+
id: `${record.id}-${Date.now()}`,
|
|
217
|
+
recordId: record.id,
|
|
218
|
+
operationId: result.operationId,
|
|
219
|
+
status: result.status,
|
|
220
|
+
message: result.message,
|
|
221
|
+
receivedAt: new Date().toISOString(),
|
|
222
|
+
});
|
|
223
|
+
// 5. Actualizar el estado.
|
|
224
|
+
const status = result.status === 'OK' ? 'CONFIRMED' : 'REJECTED';
|
|
225
|
+
await this.repository.updateRecordStatus(record.id, status);
|
|
226
|
+
return { ...record, status };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.VerifactuService = VerifactuService;
|
|
230
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/integration/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AccountabilityService } from './accountability.js';
|
|
2
|
+
import type { VerifactuRepository } from './repository.js';
|
|
3
|
+
/**
|
|
4
|
+
* Carga el `.p12` y el NIF de un tenant desde la base de datos.
|
|
5
|
+
* Lo implementa la aplicación (cada app decide cómo guarda el `.p12`).
|
|
6
|
+
*/
|
|
7
|
+
export interface TenantP12Loader {
|
|
8
|
+
/** Devuelve el contenido del `.p12` de un tenant. */
|
|
9
|
+
getP12(tenantId: string): Promise<Buffer>;
|
|
10
|
+
/** Devuelve el NIF del tenant. */
|
|
11
|
+
getNif(tenantId: string): Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Almacén de credenciales por tenant.
|
|
15
|
+
*
|
|
16
|
+
* La contraseña del `.p12` se pide UNA vez al iniciar el proceso de
|
|
17
|
+
* facturación y se cachea en memoria (nunca se guarda en la BD). El `.p12`
|
|
18
|
+
* se carga de la BD por tenant, y el `AccountabilityService` se crea con la
|
|
19
|
+
* contraseña cacheada.
|
|
20
|
+
*/
|
|
21
|
+
export declare class TenantCredentialStore {
|
|
22
|
+
private readonly loader;
|
|
23
|
+
private readonly baseUrl;
|
|
24
|
+
private readonly repository;
|
|
25
|
+
/** Contraseñas cacheadas en memoria por tenant. */
|
|
26
|
+
private readonly passwords;
|
|
27
|
+
constructor(loader: TenantP12Loader, baseUrl: string, repository: VerifactuRepository);
|
|
28
|
+
/**
|
|
29
|
+
* Establece la contraseña de un tenant (se pide una vez al iniciar).
|
|
30
|
+
*/
|
|
31
|
+
setPassword(tenantId: string, password: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Devuelve el `AccountabilityService` de un tenant, usando la contraseña
|
|
34
|
+
* cacheada y el `.p12` cargado de la BD.
|
|
35
|
+
*/
|
|
36
|
+
getService(tenantId: string): Promise<AccountabilityService>;
|
|
37
|
+
/**
|
|
38
|
+
* Limpia la contraseña cacheada de un tenant (p. ej. al cerrar sesión).
|
|
39
|
+
*/
|
|
40
|
+
clear(tenantId: string): void;
|
|
41
|
+
/** Limpia todas las contraseñas cacheadas. */
|
|
42
|
+
clearAll(): void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=tenant-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-credentials.d.ts","sourceRoot":"","sources":["../../../src/integration/tenant-credentials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,kCAAkC;IAClC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IAK9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAN7B,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;gBAGpC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,mBAAmB;IAGlD;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIrD;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAmBlE;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI7B,8CAA8C;IAC9C,QAAQ,IAAI,IAAI;CAGjB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TenantCredentialStore = void 0;
|
|
4
|
+
const accountability_js_1 = require("./accountability.js");
|
|
5
|
+
/**
|
|
6
|
+
* Almacén de credenciales por tenant.
|
|
7
|
+
*
|
|
8
|
+
* La contraseña del `.p12` se pide UNA vez al iniciar el proceso de
|
|
9
|
+
* facturación y se cachea en memoria (nunca se guarda en la BD). El `.p12`
|
|
10
|
+
* se carga de la BD por tenant, y el `AccountabilityService` se crea con la
|
|
11
|
+
* contraseña cacheada.
|
|
12
|
+
*/
|
|
13
|
+
class TenantCredentialStore {
|
|
14
|
+
loader;
|
|
15
|
+
baseUrl;
|
|
16
|
+
repository;
|
|
17
|
+
/** Contraseñas cacheadas en memoria por tenant. */
|
|
18
|
+
passwords = new Map();
|
|
19
|
+
constructor(loader, baseUrl, repository) {
|
|
20
|
+
this.loader = loader;
|
|
21
|
+
this.baseUrl = baseUrl;
|
|
22
|
+
this.repository = repository;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Establece la contraseña de un tenant (se pide una vez al iniciar).
|
|
26
|
+
*/
|
|
27
|
+
setPassword(tenantId, password) {
|
|
28
|
+
this.passwords.set(tenantId, password);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Devuelve el `AccountabilityService` de un tenant, usando la contraseña
|
|
32
|
+
* cacheada y el `.p12` cargado de la BD.
|
|
33
|
+
*/
|
|
34
|
+
async getService(tenantId) {
|
|
35
|
+
const password = this.passwords.get(tenantId);
|
|
36
|
+
if (!password) {
|
|
37
|
+
throw new Error(`No hay contraseña cacheada para el tenant ${tenantId}. Llama a setPassword primero.`);
|
|
38
|
+
}
|
|
39
|
+
const [p12Buffer, nif] = await Promise.all([
|
|
40
|
+
this.loader.getP12(tenantId),
|
|
41
|
+
this.loader.getNif(tenantId),
|
|
42
|
+
]);
|
|
43
|
+
return new accountability_js_1.AccountabilityService(this.repository, {
|
|
44
|
+
baseUrl: this.baseUrl,
|
|
45
|
+
nif,
|
|
46
|
+
p12Buffer,
|
|
47
|
+
p12Password: password,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Limpia la contraseña cacheada de un tenant (p. ej. al cerrar sesión).
|
|
52
|
+
*/
|
|
53
|
+
clear(tenantId) {
|
|
54
|
+
this.passwords.delete(tenantId);
|
|
55
|
+
}
|
|
56
|
+
/** Limpia todas las contraseñas cacheadas. */
|
|
57
|
+
clearAll() {
|
|
58
|
+
this.passwords.clear();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.TenantCredentialStore = TenantCredentialStore;
|
|
62
|
+
//# sourceMappingURL=tenant-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-credentials.js","sourceRoot":"","sources":["../../../src/integration/tenant-credentials.ts"],"names":[],"mappings":";;;AACA,2DAA4D;AAc5D;;;;;;;GAOG;AACH,MAAa,qBAAqB;IAKb;IACA;IACA;IANnB,mDAAmD;IAClC,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvD,YACmB,MAAuB,EACvB,OAAe,EACf,UAA+B;QAF/B,WAAM,GAAN,MAAM,CAAiB;QACvB,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAqB;IAC/C,CAAC;IAEJ;;OAEG;IACH,WAAW,CAAC,QAAgB,EAAE,QAAgB;QAC5C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,6CAA6C,QAAQ,gCAAgC,CAAC,CAAC;QACzG,CAAC;QAED,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;SAC7B,CAAC,CAAC;QAEH,OAAO,IAAI,yCAAqB,CAAC,IAAI,CAAC,UAAU,EAAE;YAChD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG;YACH,SAAS;YACT,WAAW,EAAE,QAAQ;SACtB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAgB;QACpB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,8CAA8C;IAC9C,QAAQ;QACN,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF;AAnDD,sDAmDC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { type SoapResponse, type RegistroResponse, type ConsultaResponse, type EventoResponse } from './response.js';
|
|
2
|
+
/**
|
|
3
|
+
* URL base del servicio AEAT en PRODUCCIÓN (Verifactu v1.0).
|
|
4
|
+
* Fuente: Veri-Factu_Descripcion_SWeb.pdf (Anexo I, entorno de producción).
|
|
5
|
+
*/
|
|
6
|
+
export declare const AEAT_PRODUCTION_BASE_URL = "https://www2.agenciatributaria.gob.es";
|
|
7
|
+
/**
|
|
8
|
+
* URL base del servicio AEAT en PRUEBAS/desarrollo (Verifactu v1.0).
|
|
9
|
+
* Fuente: Veri-Factu_Descripcion_SWeb.pdf (Anexo I, entorno de pruebas).
|
|
10
|
+
*/
|
|
11
|
+
export declare const AEAT_TEST_BASE_URL = "https://prewww2.aeat.es";
|
|
12
|
+
/**
|
|
13
|
+
* Ruta del WSDL del servicio SistemaFacturacion (Verifactu v1.0).
|
|
14
|
+
*/
|
|
15
|
+
export declare const AEAT_WSDL_PATH = "/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl";
|
|
16
|
+
/**
|
|
17
|
+
* Endpoint SOAP de remisión voluntaria (VerifactuSOAP).
|
|
18
|
+
* - Producción: `https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP`
|
|
19
|
+
* - Pruebas: `https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP`
|
|
20
|
+
*/
|
|
21
|
+
export declare const AEAT_PRODUCTION_SOAP_URL = "https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP";
|
|
22
|
+
export declare const AEAT_TEST_SOAP_URL = "https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP";
|
|
23
|
+
/**
|
|
24
|
+
* Endpoint SOAP de remisión bajo requerimiento (RequerimientoSOAP).
|
|
25
|
+
*/
|
|
26
|
+
export declare const AEAT_PRODUCTION_REQUERIMIENTO_URL = "https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP";
|
|
27
|
+
export declare const AEAT_TEST_REQUERIMIENTO_URL = "https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP";
|
|
28
|
+
/**
|
|
29
|
+
* Operación SOAP de alta/anulación de registros.
|
|
30
|
+
*/
|
|
31
|
+
export declare const AEAT_OPERATION_REGISTRO = "RegFactuSistemaFacturacion";
|
|
32
|
+
/**
|
|
33
|
+
* Operación SOAP de consulta.
|
|
34
|
+
*/
|
|
35
|
+
export declare const AEAT_OPERATION_CONSULTA = "ConsultaFactuSistemaFacturacion";
|
|
36
|
+
/**
|
|
37
|
+
* Namespace del servicio (targetNamespace del WSDL).
|
|
38
|
+
*/
|
|
39
|
+
export declare const AEAT_SOAP_NAMESPACE = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SistemaFacturacion.wsdl";
|
|
40
|
+
/**
|
|
41
|
+
* Namespace del elemento de consulta (`ConsultaLR.xsd`).
|
|
42
|
+
*/
|
|
43
|
+
export declare const AEAT_CONSULTA_NAMESPACE = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd";
|
|
44
|
+
/**
|
|
45
|
+
* Entorno de ejecución del cliente AEAT.
|
|
46
|
+
* - `production`: usa la URL de producción.
|
|
47
|
+
* - `test` / `development`: usa la URL de pruebas (si se aporta).
|
|
48
|
+
*/
|
|
49
|
+
export type VerifactuEnv = 'production' | 'test' | 'development';
|
|
50
|
+
/**
|
|
51
|
+
* Cliente para los servicios web de AEAT (Verifactu).
|
|
52
|
+
*
|
|
53
|
+
* Capa de red, separada del núcleo puro. Depende de credenciales y entornos
|
|
54
|
+
* (producción vs. pruebas), por lo que no debe acoplarse a la lógica de
|
|
55
|
+
* negocio.
|
|
56
|
+
*
|
|
57
|
+
* Soporta autenticación por certificado digital (mTLS): se pasan `cert` y
|
|
58
|
+
* `key` (PEM) extraídos de un `.p12` (ver `readP12` en `integration/p12.ts`).
|
|
59
|
+
*/
|
|
60
|
+
export interface VerifactuClientConfig {
|
|
61
|
+
/** URL del endpoint SOAP del servicio AEAT en PRODUCCIÓN. */
|
|
62
|
+
baseUrl?: string;
|
|
63
|
+
/** URL del endpoint SOAP del servicio AEAT en PRUEBAS/desarrollo. */
|
|
64
|
+
testBaseUrl?: string;
|
|
65
|
+
/** Entorno de ejecución. Si no es `production`, se usa `testBaseUrl`. */
|
|
66
|
+
env?: VerifactuEnv;
|
|
67
|
+
/** Token de autenticación (OAuth) alternativo al certificado. */
|
|
68
|
+
authToken?: string;
|
|
69
|
+
/** Certificado de cliente en PEM (mTLS). */
|
|
70
|
+
cert?: string;
|
|
71
|
+
/** Clave privada del cliente en PEM (mTLS). */
|
|
72
|
+
key?: string;
|
|
73
|
+
/** Operación SOAP (por defecto `RegFactuSistemaFacturacion`). */
|
|
74
|
+
operation?: string;
|
|
75
|
+
/** Namespace del servicio SOAP. */
|
|
76
|
+
namespace?: string;
|
|
77
|
+
/** Timeout de petición en ms. */
|
|
78
|
+
timeoutMs?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resuelve la URL base efectiva según el entorno.
|
|
82
|
+
*
|
|
83
|
+
* Regla: **por defecto se usa producción** (la app real factura en prod).
|
|
84
|
+
* - Si `env` es `production` (o no se indica), usa `baseUrl`.
|
|
85
|
+
* - Si `env` es `test`/`development`, usa `testBaseUrl`.
|
|
86
|
+
*
|
|
87
|
+
* Los tests se ejecutan aparte y fijan `env: 'test'` + `testBaseUrl` para
|
|
88
|
+
* usar desarrollo (sin necesidad de indicar la URL de producción).
|
|
89
|
+
*/
|
|
90
|
+
export declare function resolveBaseUrl(config: Pick<VerifactuClientConfig, 'baseUrl' | 'testBaseUrl' | 'env'>): string;
|
|
91
|
+
export interface SendResult {
|
|
92
|
+
/** Identificador de la operación devuelto por AEAT. */
|
|
93
|
+
operationId: string;
|
|
94
|
+
/** Estado de la respuesta. */
|
|
95
|
+
status: 'OK' | 'ERROR';
|
|
96
|
+
/** Mensaje de la AEAT. */
|
|
97
|
+
message?: string;
|
|
98
|
+
/** Código de error de AEAT (si lo hay). */
|
|
99
|
+
errorCode?: string;
|
|
100
|
+
/** Cuerpo completo de la respuesta (para auditoría). */
|
|
101
|
+
rawResponse?: string;
|
|
102
|
+
/** Respuesta completa parseada de AEAT (todo lo recibido, OK o error). */
|
|
103
|
+
data?: Record<string, unknown>;
|
|
104
|
+
/** Respuesta SOAP normalizada a JSON (XML → JSON). */
|
|
105
|
+
response?: SoapResponse;
|
|
106
|
+
/** Respuesta tipada de registro (si la operación es de registro). */
|
|
107
|
+
registro?: RegistroResponse;
|
|
108
|
+
/** Respuesta tipada de consulta (si la operación es de consulta). */
|
|
109
|
+
consulta?: ConsultaResponse;
|
|
110
|
+
/** Respuesta tipada de evento (si la operación es de evento). */
|
|
111
|
+
evento?: EventoResponse;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Cliente de servicios AEAT para Verifactu.
|
|
115
|
+
*/
|
|
116
|
+
export declare class VerifactuClient {
|
|
117
|
+
private readonly config;
|
|
118
|
+
private readonly agent;
|
|
119
|
+
constructor(config: VerifactuClientConfig);
|
|
120
|
+
/**
|
|
121
|
+
* Envía un registro (factura o evento) a AEAT mediante SOAP.
|
|
122
|
+
*/
|
|
123
|
+
sendRecord(xml: string): Promise<SendResult>;
|
|
124
|
+
/**
|
|
125
|
+
* @deprecated La consulta por ID de operación no está soportada por el
|
|
126
|
+
* esquema `ConsultaLR.xsd` de AEAT (devuelve error 4118). Usa
|
|
127
|
+
* {@link queryInvoice} para consultar registros por número de factura.
|
|
128
|
+
*/
|
|
129
|
+
getOperationStatus(operationId: string): Promise<SendResult>;
|
|
130
|
+
/**
|
|
131
|
+
* Consulta un registro de facturación por número de factura y devuelve la
|
|
132
|
+
* respuesta completa (incluida la huella confirmada por AEAT).
|
|
133
|
+
*
|
|
134
|
+
* Usa la operación `ConsultaFactuSistemaFacturacion` (mismo endpoint SOAP).
|
|
135
|
+
*/
|
|
136
|
+
queryInvoice(params: {
|
|
137
|
+
/** Obligado a la emisión (quien consulta). */
|
|
138
|
+
obligado: {
|
|
139
|
+
nombreRazon: string;
|
|
140
|
+
nif: string;
|
|
141
|
+
};
|
|
142
|
+
/** Nº serie + nº factura a consultar (opcional: consulta por periodo). */
|
|
143
|
+
numSerieFactura?: string;
|
|
144
|
+
/** Ejercicio (año) del periodo de imputación, p. ej. `2026`. */
|
|
145
|
+
ejercicio: string;
|
|
146
|
+
/** Periodo de imputación, p. ej. `01` (enero) o `12` (diciembre). */
|
|
147
|
+
periodo: string;
|
|
148
|
+
}): Promise<SendResult>;
|
|
149
|
+
/**
|
|
150
|
+
* Realiza la petición HTTPS usando el agente (con mTLS si hay certificado).
|
|
151
|
+
*/
|
|
152
|
+
private request;
|
|
153
|
+
/**
|
|
154
|
+
* Parsea la respuesta de AEAT. La estructura exacta depende del servicio;
|
|
155
|
+
* este es un mapeo base que el consumidor puede sobrescribir.
|
|
156
|
+
*
|
|
157
|
+
* Devuelve SIEMPRE todo lo recibido: la respuesta completa parseada en
|
|
158
|
+
* `data` y el cuerpo crudo en `rawResponse`, tanto si es OK como error.
|
|
159
|
+
*/
|
|
160
|
+
protected parseResponse(raw: string, httpStatus: number): SendResult;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/aeat/client.ts"],"names":[],"mappings":"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"}
|