Skip to content

Inici ràpid ​

Registra la teva primera factura contra l'entorn de preproducció de l'AEAT en menys de trenta línies. El flux és el mateix contra producció — només canvien el flag environment i el certificat.

Prerequisits ​

  1. Un certificat mTLS emès per l'AEAT en format .pfx (PKCS#12) o PEM. Consulta Certificats per saber com obtenir-lo.
  2. Bun 1.4.2 (o Node ≥ 20).
  3. El SDK instal·lat al teu projecte (bun add verifactu-sdk).
  4. Un HashStore per persistir la cadena. És una opció obligatòria del constructor — no hi ha cap valor per defecte en memòria, perquè una cadena que pot desaparèixer en reiniciar és una fallada de compliment normatiu, no només un bug. Tria un dels adaptadors inclosos (verifactu-sdk/store/*) o implementa tu mateix la interfície de dos mètodes; consulta Cadena d'empremtes → Persistir la cadena per veure la llista completa (Postgres, MySQL, SQLite, Redis, Drizzle…) i com configurar cadascun.

Configuració mínima del client ​

L'exemple següent usa BunSqlHashStore (Postgres via Bun.sql, integrat a Bun, sense dependències extra) — pots canviar-lo per qualsevol altre adaptador de la taula enllaçada a dalt sense tocar res 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(); // idempotent — crea verifactu_hash_chain si no existeix

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 client gestiona la cadena d'empremtes, el controlador de flux (que respecta el TiempoEsperaEnvio retornat per l'AEAT) i el validador per cada crida. Només necessites una instància per obligat tributari.

Registrar una factura ​

ts
const response = await client.registerInvoice({
  invoiceId: {
    issuerNif: 'B12345678',
    seriesNumber: 'A/2026/0001',
    issueDate: '2026-05-20',
  },
  invoiceType: 'F1',
  description: 'Serveis de consultoria — maig 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 l'AEAT (Código Seguro de Verificación)
console.log(response.envelopeState);  // → 'Correcto' | 'ParcialmenteCorrecto' | 'Incorrecto'
console.log(response.records[0]?.state);

L'array response.records porta una entrada per factura enviada amb el seu resultat per registre (Correcto, AceptadoConErrores o Incorrecto) i el codi d'error de l'AEAT que correspongui. Consulta Codis d'error per al catàleg complet.

Renderitzar el QR obligatori ​

Tota factura emesa sota VERI*FACTU ha de portar un codi QR que apunti al portal de validació de l'AEAT. El SDK el renderitza per tu 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 llest per inserir al teu PDF/HTML de factura.

La mida del QR ha de ser de 30-40 mm segons l'especificació de l'AEAT. Consulta la guia del QR per als requisits de maquetació.

Consultar factures ja enviades ​

En mode verifactu (només) l'AEAT exposa un endpoint de consulta paginat. El SDK retorna un async iterable, així que mai has de gestionar cursors manualment.

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);
  }
}

Anul·lar ​

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

Pròxims passos ​

  • Cadena d'empremtes — com el SDK encadena els registres i què verifica l'AEAT.
  • Validacions — les 23 regles de negoci aplicades localment.
  • Control de flux — com el SDK respecta TiempoEsperaEnvio i el límit de 1 000 registres per enviament.
  • Proves — com executar la suite e2e contra preproducció de l'AEAT.

Released under the MIT license.