Skip to content

Inicio rápido ​

Registra tu primera factura contra el entorno de pre-producción de la AEAT en menos de treinta líneas. El flujo es el mismo contra producción — sólo cambian el flag environment y el certificado.

Prerrequisitos ​

  1. Un certificado mTLS emitido por la AEAT en formato .pfx (PKCS#12) o PEM. Consulta Certificados para saber cómo obtenerlo.
  2. Bun 1.4.2 (o Node ≥ 20).
  3. El SDK instalado en tu proyecto (bun add verifactu-sdk).
  4. Un HashStore para persistir la cadena. Es una opción obligatoria del constructor — no hay valor por defecto en memoria, porque una cadena que puede desaparecer al reiniciar es un fallo de cumplimiento normativo, no sólo un bug. Elige uno de los adaptadores incluidos (verifactu-sdk/store/*) o implementa tú mismo la interfaz de dos métodos; consulta Cadena de huellas → Persistir la cadena para ver la lista completa (Postgres, MySQL, SQLite, Redis, Drizzle…) y cómo configurar cada uno.

Configuración mínima del cliente ​

El ejemplo siguiente usa BunSqlHashStore (Postgres a través de Bun.sql, integrado en Bun, sin dependencias extra) — puedes cambiarlo por cualquier otro adaptador de la tabla enlazada arriba sin tocar nada más.

ts
import { VerifactuClient, Environment } from 'verifactu-sdk';
import { BunSqlHashStore } from 'verifactu-sdk/store/bun-sql';
import { SQL } from 'bun';
import { readFileSync } from 'node:fs';

const hashStore = new BunSqlHashStore(new SQL(process.env.DATABASE_URL!));
await hashStore.migrate(); // idempotente — crea verifactu_hash_chain si no existe

const client = new VerifactuClient({
  environment: Environment.Preproduction,
  mode: 'verifactu',
  certificate: {
    pfx: readFileSync('./cert.pfx'),
    passphrase: process.env.CERT_PASS ?? '',
  },
  taxpayer: { nif: 'B12345678', legalName: 'My Company SL' },
  billingSystem: {
    producerName: 'My Company SL',
    nif: 'B12345678',
    systemId: 'JC',
    systemName: 'My App',
    version: '1.0.0',
    installationNumber: '0001',
    onlyVerifactu: 'S',
    multipleTaxpayer: 'N',
    hasMultipleTaxpayers: 'N',
  },
  hashStore,
});

El cliente gestiona la cadena de huellas, el controlador de flujo (que respeta el TiempoEsperaEnvio devuelto por la AEAT) y el validador por cada llamada. Sólo necesitas una instancia por obligado tributario.

Registrar una factura ​

ts
const response = await client.registerInvoice({
  invoiceId: {
    issuerNif: 'B12345678',
    seriesNumber: 'A/2026/0001',
    issueDate: '2026-05-20',
  },
  invoiceType: 'F1',
  description: 'Servicios de consultoría — mayo 2026',
  recipients: [{ nif: '12345678Z', legalName: 'Customer SL' }],
  breakdown: [
    {
      tax: '01',
      regimeKey: '01',
      operationQualification: 'S1',
      taxRate: '21',
      taxBase: '100.00',
      taxAmount: '21.00',
    },
  ],
  totalTaxAmount: '21.00',
  totalAmount: '121.00',
});

console.log(response.csv);            // → CSV de la AEAT (Código Seguro de Verificación)
console.log(response.envelopeState);  // → 'Correcto' | 'ParcialmenteCorrecto' | 'Incorrecto'
console.log(response.records[0]?.state);

El array response.records lleva una entrada por factura enviada con su resultado por registro (Correcto, AceptadoConErrores o Incorrecto) y el código de error de la AEAT que corresponda. Consulta Códigos de error para el catálogo completo.

Renderizar el QR obligatorio ​

Toda factura emitida bajo VERI*FACTU debe llevar un código QR que apunte al portal de validación de la AEAT. El SDK lo renderiza por ti en PNG, SVG o DataURL.

ts
import { buildQrUrl, renderQrPng } from 'verifactu-sdk/qr';

const url = buildQrUrl({
  mode: 'verifactu',
  environment: 'production',
  invoice: {
    issuerNif: 'B12345678',
    seriesNumber: 'A/2026/0001',
    issueDate: '2026-05-20',
    totalAmount: '121.00',
  },
});

const png = await renderQrPng(url, { sizeMm: 35 });
// → Buffer listo para insertar en tu PDF/HTML de factura.

El tamaño del QR debe ser de 30-40 mm según la especificación de la AEAT. Consulta la guía de QR para los requisitos de maquetación.

Consultar facturas ya enviadas ​

En modo verifactu (sólo) la AEAT expone un endpoint de consulta paginado. El SDK devuelve un async iterable, así que nunca tienes que gestionar cursores manualmente.

ts
for await (const page of client.queryInvoices({ year: '2026', period: '05' })) {
  for (const record of page.records) {
    console.log(record.invoiceId.seriesNumber, record.state);
  }
}

Anular ​

ts
await client.cancelInvoice({
  cancelledInvoiceId: {
    issuerNif: 'B12345678',
    seriesNumber: 'A/2026/0001',
    issueDate: '2026-05-20',
  },
  generatedBy: 'E',
});

Próximos pasos ​

  • Cadena de huellas — cómo el SDK encadena los registros y qué verifica la AEAT.
  • Validaciones — las 23 reglas de negocio aplicadas localmente.
  • Control de flujo — cómo el SDK respeta TiempoEsperaEnvio y el límite de 1 000 registros por envío.
  • Pruebas — cómo ejecutar la suite e2e contra pre-producción de la AEAT.

Released under the MIT license.