@dwast/verifactu-lib 0.1.0-beta.2 → 0.1.0-beta.4
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 +2 -6
- package/dist/cjs/aeat/client.d.ts.map +1 -1
- package/dist/cjs/aeat/client.js +19 -19
- package/dist/cjs/aeat/client.js.map +1 -1
- package/dist/cjs/aeat/response.d.ts +2 -0
- package/dist/cjs/aeat/response.d.ts.map +1 -1
- package/dist/cjs/aeat/response.js +1 -0
- package/dist/cjs/aeat/response.js.map +1 -1
- package/dist/cjs/core/models/index.d.ts +0 -1
- package/dist/cjs/core/models/index.d.ts.map +1 -1
- package/dist/cjs/core/models/index.js +0 -1
- package/dist/cjs/core/models/index.js.map +1 -1
- package/dist/cjs/core/qr/qr.d.ts +31 -8
- package/dist/cjs/core/qr/qr.d.ts.map +1 -1
- package/dist/cjs/core/qr/qr.js +24 -14
- package/dist/cjs/core/qr/qr.js.map +1 -1
- package/dist/cjs/core/xml/generator.d.ts +13 -15
- package/dist/cjs/core/xml/generator.d.ts.map +1 -1
- package/dist/cjs/core/xml/generator.js +16 -3
- package/dist/cjs/core/xml/generator.js.map +1 -1
- package/dist/cjs/integration/accountability.d.ts.map +1 -1
- package/dist/cjs/integration/accountability.js +6 -1
- package/dist/cjs/integration/accountability.js.map +1 -1
- package/dist/cjs/integration/repository.d.ts +2 -3
- package/dist/cjs/integration/repository.d.ts.map +1 -1
- package/dist/cjs/integration/service.d.ts.map +1 -1
- package/dist/cjs/integration/service.js +22 -3
- package/dist/cjs/integration/service.js.map +1 -1
- package/dist/esm/aeat/client.d.ts +2 -6
- package/dist/esm/aeat/client.d.ts.map +1 -1
- package/dist/esm/aeat/client.js +19 -19
- package/dist/esm/aeat/client.js.map +1 -1
- package/dist/esm/aeat/response.d.ts +2 -0
- package/dist/esm/aeat/response.d.ts.map +1 -1
- package/dist/esm/aeat/response.js +1 -0
- package/dist/esm/aeat/response.js.map +1 -1
- package/dist/esm/core/models/index.d.ts +0 -1
- package/dist/esm/core/models/index.d.ts.map +1 -1
- package/dist/esm/core/models/index.js +0 -1
- package/dist/esm/core/models/index.js.map +1 -1
- package/dist/esm/core/qr/qr.d.ts +31 -8
- package/dist/esm/core/qr/qr.d.ts.map +1 -1
- package/dist/esm/core/qr/qr.js +24 -14
- package/dist/esm/core/qr/qr.js.map +1 -1
- package/dist/esm/core/xml/generator.d.ts +13 -15
- package/dist/esm/core/xml/generator.d.ts.map +1 -1
- package/dist/esm/core/xml/generator.js +16 -3
- package/dist/esm/core/xml/generator.js.map +1 -1
- package/dist/esm/integration/accountability.d.ts.map +1 -1
- package/dist/esm/integration/accountability.js +6 -1
- package/dist/esm/integration/accountability.js.map +1 -1
- package/dist/esm/integration/repository.d.ts +2 -3
- package/dist/esm/integration/repository.d.ts.map +1 -1
- package/dist/esm/integration/service.d.ts.map +1 -1
- package/dist/esm/integration/service.js +23 -4
- package/dist/esm/integration/service.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +191 -7
- package/src/aeat/client.ts +21 -21
- package/src/aeat/response.ts +3 -0
- package/src/core/models/index.ts +0 -1
- package/src/core/qr/qr.ts +61 -14
- package/src/core/xml/generator.ts +25 -19
- package/src/integration/accountability.ts +6 -1
- package/src/integration/repository.ts +2 -3
- package/src/integration/schema.sql +0 -14
- package/src/integration/service.ts +28 -4
- package/src/core/models/event.ts +0 -25
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/integration/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAoB,MAAM,0BAA0B,CAAC;AAC/J,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/integration/service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwast/verifactu-lib",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.4",
|
|
4
4
|
"description": "Librería para la gestión de Verifactu (RD 1007/2023): modelo de datos, encadenado hash, generación XML, QR y cliente AEAT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
package/readme.md
CHANGED
|
@@ -25,10 +25,11 @@ npm install @dwast/verifactu-lib
|
|
|
25
25
|
```
|
|
26
26
|
src/
|
|
27
27
|
├── core/
|
|
28
|
-
│ ├── models/ # Tipos: Invoice, Emisor, Desglose, SistemaInformatico
|
|
28
|
+
│ ├── models/ # Tipos: Invoice, Emisor, Desglose, SistemaInformatico
|
|
29
29
|
│ ├── hash/ # Encadenado hash (SHA-256)
|
|
30
30
|
│ ├── xml/ # Generación XML Verifactu (RegistroAlta, Anulacion, Evento, Suministro)
|
|
31
31
|
│ ├── qr/ # Generación QR Verifactu
|
|
32
|
+
│ ├── validation/ # Validación de facturas
|
|
32
33
|
│ └── utils/ # Utilidades (fecha/hora con huso)
|
|
33
34
|
├── aeat/ # Cliente de servicios AEAT (SOAP + mTLS)
|
|
34
35
|
├── integration/ # Repositorio, servicio, p12, accountability
|
|
@@ -46,6 +47,175 @@ src/
|
|
|
46
47
|
|
|
47
48
|
---
|
|
48
49
|
|
|
50
|
+
## Cómo usar
|
|
51
|
+
|
|
52
|
+
Guía práctica paso a paso. La receta recomendada es **`VerifactuService`** (se encarga de validar, generar el hash, el XML, encadenar con la huella anterior, enviar y persistir). Para casos "de bajo nivel" usa el `VerifactuClient` directamente.
|
|
53
|
+
|
|
54
|
+
### 0. Configuración inicial (certificado `.p12` → cliente)
|
|
55
|
+
|
|
56
|
+
Extrae `cert` + `key` del `.p12` y construye el cliente (aquí en entorno de **pruebas**; en producción usa `baseUrl` y no pases `env`).
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { readFileSync } from 'node:fs';
|
|
60
|
+
import { readP12 } from '@dwast/verifactu-lib/integration';
|
|
61
|
+
import { VerifactuClient, AEAT_TEST_SOAP_URL } from '@dwast/verifactu-lib/aeat';
|
|
62
|
+
import { VerifactuService } from '@dwast/verifactu-lib/integration';
|
|
63
|
+
import type { VerifactuRepository } from '@dwast/verifactu-lib/integration';
|
|
64
|
+
|
|
65
|
+
// 1. Leer el .p12 y extraer certificado + clave (PEM).
|
|
66
|
+
const { certificates, keys } = readP12(
|
|
67
|
+
readFileSync('mi-certificado.p12'),
|
|
68
|
+
'contraseña-del-p12',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// 2. Cliente AEAT con mTLS. env:'test' → usa testBaseUrl (pruebas).
|
|
72
|
+
const client = new VerifactuClient({
|
|
73
|
+
testBaseUrl: AEAT_TEST_SOAP_URL, // https://prewww1.aeat.es/.../VerifactuSOAP
|
|
74
|
+
env: 'test',
|
|
75
|
+
cert: certificates[0],
|
|
76
|
+
key: keys[0],
|
|
77
|
+
timeoutMs: 15_000,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 3. Servicio de orquestación (repositorio implementado por tu app + cliente).
|
|
81
|
+
// El 3er arg (cert PEM) sirve para detectar representante en la cabecera.
|
|
82
|
+
const repo: VerifactuRepository = /* tu implementación (Mongoose, Prisma...) */;
|
|
83
|
+
const service = new VerifactuService(repo, client, certificates[0]);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> **Producción:** omite `env` y `testBaseUrl`, usa `baseUrl`:
|
|
87
|
+
> `new VerifactuClient({ baseUrl: AEAT_PRODUCTION_SOAP_URL, cert, key })`.
|
|
88
|
+
|
|
89
|
+
### 1. Enviar 1 factura (alta)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import type { Invoice } from '@dwast/verifactu-lib/core';
|
|
93
|
+
|
|
94
|
+
const invoice: Invoice = {
|
|
95
|
+
id: { serie: 'A', numero: '2026-0001', fechaExpedicion: '07-09-2026' }, // DD-MM-YYYY
|
|
96
|
+
emisor: { nif: 'B18579458', nombreRazon: 'MOVVIENDO TOURISM GROUP SL' },
|
|
97
|
+
tipoFactura: 'F1',
|
|
98
|
+
tipoDesglose: 'S1',
|
|
99
|
+
descripcion: 'Servicios de consultoria',
|
|
100
|
+
destinatarios: [{ nombreRazon: 'CLIENTE SL', nif: 'B12345678' }],
|
|
101
|
+
desglose: {
|
|
102
|
+
detalles: [
|
|
103
|
+
{ calificacionOperacion: 'S1', claveRegimen: '01', tipoImpositivo: 21, baseImponible: 100, cuotaRepercutida: 21 },
|
|
104
|
+
],
|
|
105
|
+
cuotaTotal: 21,
|
|
106
|
+
importeTotal: 121,
|
|
107
|
+
},
|
|
108
|
+
sistemaInformatico: {
|
|
109
|
+
nombreRazon: 'MOVVIENDO TOURISM GROUP SL',
|
|
110
|
+
nif: 'B18579458',
|
|
111
|
+
nombreSistemaInformatico: 'SIF-PROD',
|
|
112
|
+
idSistemaInformatico: '01', // máx 2 caracteres
|
|
113
|
+
version: '1.0',
|
|
114
|
+
numeroInstalacion: '1',
|
|
115
|
+
},
|
|
116
|
+
fechaHoraHusoGenRegistro: new Date().toISOString().replace(/\.\d+Z/, 'Z'), // o nowIsoWithOffset()
|
|
117
|
+
huella: '', // la calcula el servicio
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const record = await service.registerInvoice(invoice);
|
|
121
|
+
|
|
122
|
+
console.log(record.status); // 'CONFIRMED' | 'REJECTED'
|
|
123
|
+
console.log(record.hash); // huella SHA-256 (64 hex)
|
|
124
|
+
console.log(record.hashVerified); // true/false (si AEAT devolvió huella que coincide)
|
|
125
|
+
console.log(record.qrPayload); // URL del QR
|
|
126
|
+
console.log(record.xml); // XML Verifactu que se envió (persistir)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> La **fecha/hora** debe ser la actual (margen AEAT de 240s). Usa `nowIsoWithOffset()` de `@dwast/verifactu-lib/core` en vez de `toISOString()`.
|
|
130
|
+
|
|
131
|
+
### 2. Enviar varias facturas encadenadas
|
|
132
|
+
|
|
133
|
+
`VerifactuService` **encadena automáticamente** la huella: cada alta usa como `huellaAnterior` el hash del último registro guardado en `getLastRecord()`. Solo encadena **dentro del mismo obligado + sistema informático**.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const f1 = await service.registerInvoice(invoice1); // PrimerRegistro
|
|
137
|
+
const f2 = await service.registerInvoice(invoice2); // usa f1.hash como huellaAnterior
|
|
138
|
+
const f3 = await service.registerInvoice(invoice3); // usa f2.hash
|
|
139
|
+
console.log(f2.previousHash === f1.hash); // true
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> **Importante:** el sistema informático (`numeroInstalacion`/`idSistemaInformatico`) debe ser **el mismo** en toda la cadena. Para empezar una cadena nueva por cada sistema, usa un `numeroInstalacion` único.
|
|
143
|
+
|
|
144
|
+
### 3. Anular una factura
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
await service.annulInvoice(invoiceId, invoice);
|
|
148
|
+
// - Genera el registro de anulación (encadena con el último hash).
|
|
149
|
+
// - Envía a AEAT.
|
|
150
|
+
// - Marca la factura original como 'ANNULLED'.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`invoiceId` es la **referencia de la original** (serie + número, p. ej. `A2026-0001`), que debe existir en el repositorio. Pasa la `invoice` completa si quieres que use su `sistemaInformatico` y sus fechas; si no, usa los valores por defecto de la librería.
|
|
154
|
+
|
|
155
|
+
### 4. Modificar una factura
|
|
156
|
+
|
|
157
|
+
Verifactu **no tiene "modificación"**: se hace como anulación + nuevo alta. `modifyInvoice` lo hace por ti.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const original: Invoice = /* la factura ya enviada */;
|
|
161
|
+
await service.registerInvoice(original);
|
|
162
|
+
|
|
163
|
+
const corregida: Invoice = { ...original, id: { ...original.id, numero: '2026-0002' }, /* cambios */ };
|
|
164
|
+
await service.modifyInvoice(`${original.id.serie ?? ''}${original.id.numero}`, corregida);
|
|
165
|
+
// Anula la original y emite la corregida como nuevo alta (CONFIRMED si AEAT la acepta).
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 5. Consultar la huella de un registro enviado
|
|
169
|
+
|
|
170
|
+
En lugar de guardar la huella, puedes pedirla a AEAT. Necesitas el **número de factura** y el **mes** real.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const res = await client.queryInvoice({
|
|
174
|
+
obligado: { nombreRazon: 'MOVVIENDO TOURISM GROUP SL', nif: 'B18579458' },
|
|
175
|
+
numSerieFactura: 'A2026-0001',
|
|
176
|
+
ejercicio: '2026',
|
|
177
|
+
periodo: '09', // 01–12, el mes real de la factura
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const huella = res.consulta?.huella; // hash confirmado por AEAT
|
|
181
|
+
const pagSiguiente = res.consulta?.clavePaginacion; // pasar como clavePaginacion en la siguiente consulta
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
> **Recomendación:** para encadenar la siguiente factura es más fiable **guardar la huella localmente** (`getLastRecord()`). `queryInvoice` es útil para auditoría o recuperación puntual. Consultar "por periodo" sin número devuelve registros de todos los sistemas y ejecuciones (poco fiable).
|
|
185
|
+
|
|
186
|
+
### 6. Enviar un evento (`RegistroEvento`)
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { computeRegistroEvento } from '@dwast/verifactu-lib/core';
|
|
190
|
+
|
|
191
|
+
const last = await repo.getLastRecord(); // último registro (para encadenar)
|
|
192
|
+
const fechaHora = nowIsoWithOffset();
|
|
193
|
+
const hash = computeRegistroEvento({
|
|
194
|
+
nifSistemaInformatico: 'B18579458',
|
|
195
|
+
id: '', // IDOtro del software (vacío si usa NIF)
|
|
196
|
+
idSistemaInformatico: '01',
|
|
197
|
+
version: '1.0',
|
|
198
|
+
numeroInstalacion: '1',
|
|
199
|
+
nifObligado: 'B18579458',
|
|
200
|
+
tipoEvento: '01', // 01–10, 90 (ver EventosSIF.xsd)
|
|
201
|
+
huellaAnterior: last?.hash ?? null,
|
|
202
|
+
fechaHoraHusoGenEvento: fechaHora,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
await service.registerEvent({
|
|
206
|
+
sistemaInformatico: { /* igual que en la factura */ },
|
|
207
|
+
obligadoEmision: { nombreRazon: 'MOVVIENDO TOURISM GROUP SL', nif: 'B18579458' },
|
|
208
|
+
fechaHoraHusoGenEvento: fechaHora,
|
|
209
|
+
tipoEvento: '01',
|
|
210
|
+
huellaEvento: hash,
|
|
211
|
+
// eventoAnterior: { tipoEvento, fechaHoraHusoGenEvento, huellaEvento } // para el 2º evento en adelante
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
> **⚠️ Envío de eventos.** La generación del XML (`generateEventoXml`) es correcta y está cubierta por unit tests, pero **el envío real necesita un endpoint/operación SOAP específica de eventos** que el cliente actual (`VerifactuSOAP`) todavía no cubre (AEAT devuelve error 4118). Úsalo cuando se añada ese endpoint.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
49
219
|
## Núcleo (`core`)
|
|
50
220
|
|
|
51
221
|
### Modelos (`core/models`)
|
|
@@ -204,12 +374,27 @@ const envelope = buildRemisionEnvelope(xml);
|
|
|
204
374
|
|
|
205
375
|
### QR (`core/qr`)
|
|
206
376
|
|
|
377
|
+
El QR codifica la URL de verificación de la AEAT con los **datos de la factura** (`nif`, `numserie`, `fecha`, `importe`), no el hash (RD 1007/2023, art. 20-21).
|
|
378
|
+
|
|
207
379
|
```ts
|
|
208
380
|
import { buildQrPayload } from '@dwast/verifactu-lib/core';
|
|
209
381
|
|
|
210
|
-
const qr = buildQrPayload(
|
|
382
|
+
const qr = buildQrPayload({
|
|
383
|
+
nif: 'B18579458',
|
|
384
|
+
numSerieFactura: 'A2026-0001',
|
|
385
|
+
fechaExpedicion: '07-09-2026',
|
|
386
|
+
importeTotal: 121,
|
|
387
|
+
});
|
|
388
|
+
// → https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR?nif=B18579458&numserie=A2026-0001&fecha=07-09-2026&importe=121
|
|
389
|
+
|
|
390
|
+
// Entorno de pruebas o factura no verificable:
|
|
391
|
+
buildQrPayload(input, { environment: 'test' }); // prewww2.aeat.es
|
|
392
|
+
buildQrPayload(input, { verified: false }); // ValidarQRNoVerifactu
|
|
393
|
+
buildQrPayload(input, { baseUrl: 'https://...' }); // URL base custom
|
|
211
394
|
```
|
|
212
395
|
|
|
396
|
+
> El servicio (`registerInvoice`) ya rellena `record.qrPayload` con estos datos.
|
|
397
|
+
|
|
213
398
|
### Fecha/hora (`core/utils`)
|
|
214
399
|
|
|
215
400
|
```ts
|
|
@@ -272,8 +457,7 @@ const client = new VerifactuClient({
|
|
|
272
457
|
| Método | Descripción |
|
|
273
458
|
|--------|-------------|
|
|
274
459
|
| `sendRecord(xml)` | Envía un registro (alta/anulación/evento) a AEAT |
|
|
275
|
-
| `
|
|
276
|
-
| `queryInvoice({ obligado, numSerieFactura, ejercicio, periodo })` | Consulta un registro por número de factura y devuelve su huella |
|
|
460
|
+
| `queryInvoice({ obligado, numSerieFactura, ejercicio, periodo, clavePaginacion? })` | Consulta un registro por número de factura y devuelve su huella; acepta `clavePaginacion` para cursar las páginas siguientes |
|
|
277
461
|
|
|
278
462
|
### `queryInvoice` — obtener la huella de un registro enviado
|
|
279
463
|
|
|
@@ -296,7 +480,7 @@ const huella = res.consulta?.huella; // hash confirmado por AEAT
|
|
|
296
480
|
|
|
297
481
|
### Formato de respuesta (`SendResult`)
|
|
298
482
|
|
|
299
|
-
`sendRecord
|
|
483
|
+
`sendRecord` y `queryInvoice` devuelven **todo lo recibido de AEAT**, sea OK o error:
|
|
300
484
|
|
|
301
485
|
```ts
|
|
302
486
|
interface SendResult {
|
|
@@ -332,6 +516,7 @@ interface ConsultaResponse {
|
|
|
332
516
|
descripcionError?: string;
|
|
333
517
|
csv?: string;
|
|
334
518
|
huella?: string; // hash del primer registro devuelto
|
|
519
|
+
clavePaginacion?: string; // cursor para la página siguiente
|
|
335
520
|
registros?: RegistroResponse[];
|
|
336
521
|
}
|
|
337
522
|
|
|
@@ -421,7 +606,7 @@ WSDL: `.../tikeV1.0/cont/ws/SistemaFacturacion.wsdl`. La operación de consulta
|
|
|
421
606
|
|
|
422
607
|
### Requisitos del sistema consumidor
|
|
423
608
|
|
|
424
|
-
1. **Persistencia.** El sistema DEBE implementar `VerifactuRepository` y disponer de las tablas de `src/integration/schema.sql` (`verifactu_records`, `verifactu_responses
|
|
609
|
+
1. **Persistencia.** El sistema DEBE implementar `VerifactuRepository` y disponer de las tablas de `src/integration/schema.sql` (`verifactu_records`, `verifactu_responses`). La AEAT exige conservar la respuesta de cada envío.
|
|
425
610
|
2. **Credenciales.** Para el envío real, el sistema aporta el certificado (`.p12`) y el NIF.
|
|
426
611
|
|
|
427
612
|
### Repositorio (`VerifactuRepository`)
|
|
@@ -437,7 +622,6 @@ const repo: VerifactuRepository = {
|
|
|
437
622
|
getLastRecord() { /* ... */ },
|
|
438
623
|
saveResponse(response) { /* ... */ },
|
|
439
624
|
getResponses(recordId) { /* ... */ },
|
|
440
|
-
saveEvent(event) { /* ... */ },
|
|
441
625
|
};
|
|
442
626
|
```
|
|
443
627
|
|
package/src/aeat/client.ts
CHANGED
|
@@ -197,27 +197,6 @@ export class VerifactuClient {
|
|
|
197
197
|
return this.request(url, 'POST', headers, envelope);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/**
|
|
201
|
-
* @deprecated La consulta por ID de operación no está soportada por el
|
|
202
|
-
* esquema `ConsultaLR.xsd` de AEAT (devuelve error 4118). Usa
|
|
203
|
-
* {@link queryInvoice} para consultar registros por número de factura.
|
|
204
|
-
*/
|
|
205
|
-
async getOperationStatus(operationId: string): Promise<SendResult> {
|
|
206
|
-
const consultaXml = `<ConsultaFactuSistemaFacturacion xmlns="${this.config.namespace}"><ID>${operationId}</ID></ConsultaFactuSistemaFacturacion>`;
|
|
207
|
-
const envelope = buildRemisionEnvelope(consultaXml);
|
|
208
|
-
const url = new URL(this.config.baseUrl);
|
|
209
|
-
const headers: Record<string, string> = {
|
|
210
|
-
'Content-Type': 'text/xml; charset=utf-8',
|
|
211
|
-
SOAPAction: '',
|
|
212
|
-
Accept: 'application/xml',
|
|
213
|
-
};
|
|
214
|
-
if (this.config.authToken) {
|
|
215
|
-
headers.Authorization = `Bearer ${this.config.authToken}`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return this.request(url, 'POST', headers, envelope);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
200
|
/**
|
|
222
201
|
* Consulta un registro de facturación por número de factura y devuelve la
|
|
223
202
|
* respuesta completa (incluida la huella confirmada por AEAT).
|
|
@@ -233,6 +212,8 @@ export class VerifactuClient {
|
|
|
233
212
|
ejercicio: string;
|
|
234
213
|
/** Periodo de imputación, p. ej. `01` (enero) o `12` (diciembre). */
|
|
235
214
|
periodo: string;
|
|
215
|
+
/** Clave de paginación devuelta por AEAT en la página anterior (cursor). */
|
|
216
|
+
clavePaginacion?: string;
|
|
236
217
|
}): Promise<SendResult> {
|
|
237
218
|
const consultaXml = [
|
|
238
219
|
`<sfLRC:ConsultaFactuSistemaFacturacion xmlns:sfLRC="${AEAT_CONSULTA_NAMESPACE}" xmlns:sf="${AEAT_INFORMACION_NAMESPACE}">`,
|
|
@@ -251,6 +232,9 @@ export class VerifactuClient {
|
|
|
251
232
|
...(params.numSerieFactura
|
|
252
233
|
? [` <sfLRC:NumSerieFactura>${params.numSerieFactura}</sfLRC:NumSerieFactura>`]
|
|
253
234
|
: []),
|
|
235
|
+
...(params.clavePaginacion
|
|
236
|
+
? [` <sfLRC:ClavePaginacion>${params.clavePaginacion}</sfLRC:ClavePaginacion>`]
|
|
237
|
+
: []),
|
|
254
238
|
' </sfLRC:FiltroConsulta>',
|
|
255
239
|
'</sfLRC:ConsultaFactuSistemaFacturacion>',
|
|
256
240
|
].join('\n');
|
|
@@ -332,6 +316,22 @@ export class VerifactuClient {
|
|
|
332
316
|
response = soap;
|
|
333
317
|
data = soap.body;
|
|
334
318
|
operationId = String(soap.body['ID'] ?? soap.body['IdPeticion'] ?? '');
|
|
319
|
+
// SOAP Fault: AEAT devolvió un error técnico (HTTP 200 con <Fault>).
|
|
320
|
+
const faultString = String(soap.body['faultstring'] ?? '');
|
|
321
|
+
if (soap.operation === 'Fault' || faultString) {
|
|
322
|
+
status = 'ERROR';
|
|
323
|
+
message = faultString || message;
|
|
324
|
+
errorCode = String(soap.body['faultcode'] ?? '') || errorCode;
|
|
325
|
+
return {
|
|
326
|
+
operationId,
|
|
327
|
+
status,
|
|
328
|
+
message,
|
|
329
|
+
errorCode,
|
|
330
|
+
rawResponse: raw,
|
|
331
|
+
data,
|
|
332
|
+
response,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
335
|
// Estado según el registro/consulta/evento mapeado.
|
|
336
336
|
const reg = soap.registro;
|
|
337
337
|
const con = soap.consulta;
|
package/src/aeat/response.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface ConsultaResponse {
|
|
|
37
37
|
csv?: string;
|
|
38
38
|
/** Huella (hash) de la operación consultada. */
|
|
39
39
|
huella?: string;
|
|
40
|
+
/** Clave de paginación para obtener la siguiente página (cursor). */
|
|
41
|
+
clavePaginacion?: string;
|
|
40
42
|
/** Registros devueltos por la consulta. */
|
|
41
43
|
registros?: RegistroResponse[];
|
|
42
44
|
}
|
|
@@ -160,6 +162,7 @@ function mapConsulta(opBody: Record<string, unknown> | undefined): ConsultaRespo
|
|
|
160
162
|
descripcionError: str(opBody['DescripcionError']),
|
|
161
163
|
csv: str(opBody['CSV']),
|
|
162
164
|
huella: registros?.[0]?.huella,
|
|
165
|
+
clavePaginacion: str(opBody['ClavePaginacion']),
|
|
163
166
|
registros: registros as RegistroResponse[] | undefined,
|
|
164
167
|
};
|
|
165
168
|
}
|
package/src/core/models/index.ts
CHANGED
package/src/core/qr/qr.ts
CHANGED
|
@@ -1,19 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Construye
|
|
2
|
+
* Construye la URL que codifica el QR Verifactu.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Según la especificación técnica de la AEAT (RD 1007/2023, art. 20-21), el QR
|
|
5
|
+
* codifica una URL de cotejo/verificación con los datos de la factura
|
|
6
|
+
* (`nif`, `numserie`, `fecha`, `importe`), NO el hash. Cualquier persona que
|
|
7
|
+
* escanee el QR abre esa URL y la AEAT verifica la factura.
|
|
7
8
|
*
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
10
|
-
* @param extraParams Parámetros adicionales a incluir en la URL.
|
|
9
|
+
* @param input Datos de la factura necesarios para la URL.
|
|
10
|
+
* @param options Opciones de entorno y tipo de factura.
|
|
11
11
|
*/
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
export interface QrPayloadInput {
|
|
13
|
+
/** NIF del emisor de la factura. */
|
|
14
|
+
nif: string;
|
|
15
|
+
/** Número de serie + número de factura (p. ej. `A2026-0001`). */
|
|
16
|
+
numSerieFactura: string;
|
|
17
|
+
/** Fecha de emisión en formato `DD-MM-YYYY`. */
|
|
18
|
+
fechaExpedicion: string;
|
|
19
|
+
/** Importe total de la factura. */
|
|
20
|
+
importeTotal: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Entorno al que apunta la URL de verificación. */
|
|
24
|
+
export type QrEnvironment = 'production' | 'test';
|
|
25
|
+
|
|
26
|
+
export interface QrUrlOptions {
|
|
27
|
+
/** `production` (por defecto) o `test` (prewww2.aeat.es). */
|
|
28
|
+
environment?: QrEnvironment;
|
|
29
|
+
/**
|
|
30
|
+
* `true` (por defecto) → `ValidarQR` (factura verificable VERI*FACTU).
|
|
31
|
+
* `false` → `ValidarQRNoVerifactu` (factura no verificable).
|
|
32
|
+
*/
|
|
33
|
+
verified?: boolean;
|
|
34
|
+
/** URL base completa (sobrescribe el entorno). */
|
|
35
|
+
baseUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const QR_BASE_URLS: Record<QrEnvironment, { verified: string; notVerified: string }> = {
|
|
39
|
+
production: {
|
|
40
|
+
verified: 'https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR',
|
|
41
|
+
notVerified: 'https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQRNoVerifactu',
|
|
42
|
+
},
|
|
43
|
+
test: {
|
|
44
|
+
verified: 'https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR',
|
|
45
|
+
notVerified: 'https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQRNoVerifactu',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Formatea el importe sin ceros sobrantes (241.50 → `241.5`, 121 → `121`). */
|
|
50
|
+
function formatImporte(importe: number): string {
|
|
51
|
+
return String(Math.round(importe * 100) / 100);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function buildQrPayload(input: QrPayloadInput, options: QrUrlOptions = {}): string {
|
|
55
|
+
const { environment = 'production', verified = true, baseUrl } = options;
|
|
56
|
+
const base = baseUrl ?? QR_BASE_URLS[environment][verified ? 'verified' : 'notVerified'];
|
|
57
|
+
|
|
58
|
+
const params = new URLSearchParams({
|
|
59
|
+
nif: input.nif,
|
|
60
|
+
numserie: input.numSerieFactura,
|
|
61
|
+
fecha: input.fechaExpedicion,
|
|
62
|
+
importe: formatImporte(input.importeTotal),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return `${base}?${params.toString()}`;
|
|
19
66
|
}
|
|
@@ -128,16 +128,6 @@ export function generateRegistroAltaXml(invoice: Invoice, previousHash: string |
|
|
|
128
128
|
return xml;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
/**
|
|
132
|
-
* Cabecera del suministro (CabeceraType del XSD).
|
|
133
|
-
*/
|
|
134
|
-
export interface Cabecera {
|
|
135
|
-
/** Nombre o razón social del obligado a emitir. */
|
|
136
|
-
nombreRazon: string;
|
|
137
|
-
/** NIF del obligado a emitir. */
|
|
138
|
-
nif: string;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
131
|
/**
|
|
142
132
|
* Namespace del esquema `SuministroLR.xsd` de la AEAT.
|
|
143
133
|
*/
|
|
@@ -282,12 +272,6 @@ export function generateAnulacionXml(
|
|
|
282
272
|
].join('\n');
|
|
283
273
|
}
|
|
284
274
|
|
|
285
|
-
/**
|
|
286
|
-
* Datos de un registro de evento (RegistroEvento).
|
|
287
|
-
*
|
|
288
|
-
* Nota: el `NIF` aparece dos veces: `nifSistemaInformatico` (del sistema
|
|
289
|
-
* informático) y `nifObligado` (del obligado a emitir).
|
|
290
|
-
*/
|
|
291
275
|
/**
|
|
292
276
|
* Datos de un registro de evento (RegistroEvento, EventosSIF.xsd).
|
|
293
277
|
*/
|
|
@@ -302,6 +286,15 @@ export interface EventoInput {
|
|
|
302
286
|
tipoEvento: string;
|
|
303
287
|
/** Huella (hash) del evento. */
|
|
304
288
|
huellaEvento: string;
|
|
289
|
+
/** Evento anterior de la cadena (obligatorio para el 2º evento en adelante). */
|
|
290
|
+
eventoAnterior?: {
|
|
291
|
+
/** Tipo de evento del evento anterior. */
|
|
292
|
+
tipoEvento: string;
|
|
293
|
+
/** Fecha/hora de generación del evento anterior. */
|
|
294
|
+
fechaHoraHusoGenEvento: string;
|
|
295
|
+
/** Huella (hash) del evento anterior. */
|
|
296
|
+
huellaEvento: string;
|
|
297
|
+
};
|
|
305
298
|
/** Versión del esquema (por defecto '1.0'). */
|
|
306
299
|
idVersion?: string;
|
|
307
300
|
}
|
|
@@ -311,9 +304,24 @@ export interface EventoInput {
|
|
|
311
304
|
*
|
|
312
305
|
* Estructura basada en `EventosSIF.xsd` (namespace `sf`). El evento se envía
|
|
313
306
|
* como un suministro independiente (no dentro de `RegFactuSistemaFacturacion`).
|
|
307
|
+
*
|
|
308
|
+
* El encadenado emite `<sf:PrimerEvento>S</sf:PrimerEvento>` si es el primer
|
|
309
|
+
* evento, o `<sf:EventoAnterior>` con los datos del evento previo si se
|
|
310
|
+
* proporciona `eventoAnterior`.
|
|
314
311
|
*/
|
|
315
312
|
export function generateEventoXml(evento: EventoInput): string {
|
|
316
313
|
const si = evento.sistemaInformatico;
|
|
314
|
+
const encadenamiento = evento.eventoAnterior
|
|
315
|
+
? [
|
|
316
|
+
' <sf:Encadenamiento>',
|
|
317
|
+
' <sf:EventoAnterior>',
|
|
318
|
+
` <sf:TipoEvento>${escapeXml(evento.eventoAnterior.tipoEvento)}</sf:TipoEvento>`,
|
|
319
|
+
` <sf:FechaHoraHusoGenEvento>${evento.eventoAnterior.fechaHoraHusoGenEvento}</sf:FechaHoraHusoGenEvento>`,
|
|
320
|
+
` <sf:HuellaEvento>${evento.eventoAnterior.huellaEvento}</sf:HuellaEvento>`,
|
|
321
|
+
' </sf:EventoAnterior>',
|
|
322
|
+
' </sf:Encadenamiento>',
|
|
323
|
+
]
|
|
324
|
+
: [' <sf:Encadenamiento>', ' <sf:PrimerEvento>S</sf:PrimerEvento>', ' </sf:Encadenamiento>'];
|
|
317
325
|
const xml = [
|
|
318
326
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
319
327
|
`<sf:RegistroEvento xmlns:sf="${AEAT_EVENTO_NAMESPACE}">`,
|
|
@@ -336,9 +344,7 @@ export function generateEventoXml(evento: EventoInput): string {
|
|
|
336
344
|
' </sf:ObligadoEmision>',
|
|
337
345
|
` <sf:FechaHoraHusoGenEvento>${evento.fechaHoraHusoGenEvento}</sf:FechaHoraHusoGenEvento>`,
|
|
338
346
|
` <sf:TipoEvento>${escapeXml(evento.tipoEvento)}</sf:TipoEvento>`,
|
|
339
|
-
|
|
340
|
-
' <sf:PrimerEvento>S</sf:PrimerEvento>',
|
|
341
|
-
' </sf:Encadenamiento>',
|
|
347
|
+
...encadenamiento,
|
|
342
348
|
' <sf:TipoHuella>01</sf:TipoHuella>',
|
|
343
349
|
` <sf:HuellaEvento>${evento.huellaEvento}</sf:HuellaEvento>`,
|
|
344
350
|
' </sf:Evento>',
|
|
@@ -100,7 +100,12 @@ export class AccountabilityService {
|
|
|
100
100
|
xml,
|
|
101
101
|
hash,
|
|
102
102
|
previousHash,
|
|
103
|
-
qrPayload: buildQrPayload(
|
|
103
|
+
qrPayload: buildQrPayload({
|
|
104
|
+
nif: invoiceConNif.emisor.nif,
|
|
105
|
+
numSerieFactura: `${invoiceConNif.id.serie ?? ''}${invoiceConNif.id.numero}`,
|
|
106
|
+
fechaExpedicion: invoiceConNif.id.fechaExpedicion,
|
|
107
|
+
importeTotal: invoiceConNif.desglose.importeTotal,
|
|
108
|
+
}),
|
|
104
109
|
status: 'PENDING',
|
|
105
110
|
createdAt: now,
|
|
106
111
|
updatedAt: now,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Invoice } from '../core/models/invoice.js';
|
|
2
|
-
import type { Event } from '../core/models/event.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Estado de un registro de facturación en el sistema consumidor.
|
|
@@ -29,6 +28,8 @@ export interface StoredRecord {
|
|
|
29
28
|
previousHash: string | null;
|
|
30
29
|
/** Payload del QR Verifactu (URL de verificación con el hash). */
|
|
31
30
|
qrPayload?: string;
|
|
31
|
+
/** `true` si la huella devuelta por AEAT coincide con la calculada. */
|
|
32
|
+
hashVerified?: boolean;
|
|
32
33
|
/** Estado del registro. */
|
|
33
34
|
status: RecordStatus;
|
|
34
35
|
/** Fecha/hora de creación. */
|
|
@@ -83,6 +84,4 @@ export interface VerifactuRepository {
|
|
|
83
84
|
saveResponse(response: StoredResponse): Promise<void>;
|
|
84
85
|
/** Obtiene las respuestas de un registro. */
|
|
85
86
|
getResponses(recordId: string): Promise<StoredResponse[]>;
|
|
86
|
-
/** Guarda un evento. */
|
|
87
|
-
saveEvent(event: Event): Promise<void>;
|
|
88
87
|
}
|
|
@@ -32,20 +32,6 @@ CREATE TABLE IF NOT EXISTS verifactu_responses (
|
|
|
32
32
|
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
-- Eventos (alta, anulación, modificación, eventos del sistema)
|
|
36
|
-
CREATE TABLE IF NOT EXISTS verifactu_events (
|
|
37
|
-
id VARCHAR(64) PRIMARY KEY,
|
|
38
|
-
type VARCHAR(16) NOT NULL, -- ALTA | ANULACION | MODIFICACION
|
|
39
|
-
fecha_hora TIMESTAMP NOT NULL,
|
|
40
|
-
invoice_id VARCHAR(128),
|
|
41
|
-
motivo TEXT,
|
|
42
|
-
factura_anulada VARCHAR(128),
|
|
43
|
-
hash VARCHAR(64),
|
|
44
|
-
previous_hash VARCHAR(64),
|
|
45
|
-
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
46
|
-
);
|
|
47
|
-
|
|
48
35
|
-- Índices recomendados
|
|
49
36
|
CREATE INDEX IF NOT EXISTS idx_records_invoice ON verifactu_records (invoice_id);
|
|
50
37
|
CREATE INDEX IF NOT EXISTS idx_responses_record ON verifactu_responses (record_id);
|
|
51
|
-
CREATE INDEX IF NOT EXISTS idx_events_invoice ON verifactu_events (invoice_id);
|