npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

digifact-sdk

v2.2.3

Published

JavaScript SDK for Digifact FEL Guatemala e-invoicing API

Readme

Digifact FEL Guatemala — SDK para JavaScript

SDK en JavaScript (Node 18+) para la API de facturación electrónica en línea (FEL) de Guatemala de Digifact.

Sin dependencias en tiempo de ejecución — usa fetch nativo (Node 18+).

Configuración del cliente (new DigifactClient({...}))

| Propiedad | Tipo | Por defecto | Descripción | |-----------|------|-------------|-------------| | taxid | string | requerido | NIT del emisor. Acepta dígitos o con separadores ("12345678", "1234567-8"). | | username | string | requerido | Usuario corto de Digifact (p. ej. "FELUSER"). | | password | string | "" | Contraseña de la cuenta. Requerido si no se provee token. | | environment | string | "test" | "test" o "production". | | token | string | "" | Bearer token preobtenido. Si se provee, se omite el login. | | seller_name | string | "" | Nombre comercial del emisor. Si está vacío, se consulta en SAT vía lookupNit(). | | seller_address | string | "" | Dirección del emisor. Si está vacía, se consulta en SAT. | | afiliacion_iva | string | "GEN" | Afiliación IVA del RTU: "GEN", "PEQ" o "EXE". | | tipo_personeria | string | "1" | Código de TipoPersoneria del RTU (usado en RDON). | | branch_code | string | "1" | Código del establecimiento del RTU. Se escribe en Seller.BranchInfo.Code. | | branch_name | string | "ESTABLECIMIENTO PRINCIPAL" | Nombre comercial del establecimiento. Se escribe en Seller.BranchInfo.Name. | | tipo_frase | string \| null | null | Sobreescritura global de TipoFrase. Ver frases. | | escenario | string \| null | null | Sobreescritura global de CodigoEscenario. | | timeout | number | 120000 | Timeout HTTP en ms. | | petroleo_rates | Object<string,number> | {} | Mapa código PETROLEO → tarifa por unidad. Usado por fuelInvoice(). |

Inicio rápido

import { DigifactClient } from './src/index.js';

const client = new DigifactClient({
  taxid: '12345678',
  username: 'FELUSER',
  password: 'secret',
  environment: 'test',  // o 'production'
});

// FACT CF
const result = await client.invoice('CF', [
  { description: 'Consultoría', qty: 1, price: 100.00 }
]);
console.log(result.authNumber);

// FACT a NIT (el nombre del receptor se consulta automáticamente en SAT)
const result2 = await client.invoice('12345678', [
  { description: 'Laptop', qty: 1, price: 5000.00, type: 'Bien' },
  { description: 'Soporte', qty: 1, price: 500.00 },
]);

// FACT a receptor con CUI
const result3 = await client.invoice(
  { taxid: '3730617490101', type: 'CUI', name: 'Juan Pérez' },
  [{ description: 'Producto', qty: 2, price: 50.00 }]
);

// Receptor NIT con datos explícitos (sin consulta automática)
const result3b = await client.invoice(
  {
    taxid:    '12345678',
    name:     'EMPRESA EJEMPLO S.A.',
    address:  '6 AV 6-48 ZONA 9',
    city:     '01009',
    district: 'GUATEMALA',
    state:    'GUATEMALA',
    country:  'GT',
    email:    '[email protected]',  // opcional
  },
  [{ description: 'Producto', qty: 1, price: 100.00 }]
);

// FCAM (Factura Cambiaria)
const result4 = await client.invoice('12345678', [
  { description: 'Servicio', qty: 1, price: 500.00 }
], {
  doc_type: 'FCAM',
  payment_terms: [{ date: '2026-04-18', amount: 500.00 }],
});

// Nota de crédito (NCRE)
const ncre = await client.creditNote('12345678', [
  { description: 'Devolución', qty: 1, price: 100.00 }
], {
  auth_number: 'XXXXXXXX-...',
  date: '2026-03-18',
  series: 'XXXXXXXX',
  number: '123456',
}, 'Producto defectuoso');

// Nota de débito (NDEB)
const ndeb = await client.debitNote('12345678', [...], origin, 'Cargo extra');

// Anulación
const cancel = await client.cancel('XXXXXXXX-...', 'CF', '2026-03-18 21:40:14', 'Error en monto');

// Consulta de NIT
const info = await client.lookupNit('12345678');
console.log(info.name);

// Obtener DTE
const doc = await client.getDte('XXXXXXXX-...');

// FACT Combustible — tarifas fijadas al inicializar (recomendado para gasolineras)
const stationClient = new DigifactClient({
  taxid: '12345678', username: 'FELUSER', password: 'secret',
  petroleo_rates: { '1': 4.70, '2': 4.60, '4': 1.30 }, // SUPER / REGULAR / DIESEL
});
// Sólo hace falta petroleo_code — petroleo_amount se completa automáticamente
const fuel = await stationClient.fuelInvoice('CF', [
  { description: 'GASOLINA SUPER',    qty: 30, price: 35.00, petroleo_code: '1', type: 'Bien' },
  { description: 'GASOLINA REGULAR',  qty: 20, price: 34.00, petroleo_code: '2', type: 'Bien' },
  { description: 'GASOLINA DIESEL',   qty: 50, price: 32.00, petroleo_code: '4', type: 'Bien' },
  // Ítems regulares (sin petroleo_code): sólo IVA, pueden coexistir
  { description: 'FILTRO DE ACEITE',    qty: 1, price: 45.00, type: 'Bien' },
  { description: 'SET DE CANDELAS NGK', qty: 1, price: 400.00, type: 'Bien' },
]);
console.log(fuel.authNumber);

// Alternativa: petroleo_amount explícito por ítem (no se necesita petroleo_rates)
const fuel2 = await client.fuelInvoice('CF', [
  { description: 'GASOLINA SUPER', qty: 1, price: 35.00, petroleo_amount: 4.70, petroleo_code: '1', type: 'Bien' },
]);

Campos del ítem de combustible

| Campo | Tipo | Por defecto | Descripción | |-----|------|---------|-------------| | description | string | requerido | Descripción de la línea | | price | number | requerido | Precio unitario completo al consumidor (incluye PETROLEO + IVA). Es lo que paga el cliente en la bomba. Si la factura del proveedor muestra un precio unitario sin PETROLEO/IDP (p. ej. 37.99), suma la tarifa IDP por unidad: price = 37.99 + 4.70 = 42.69. | | qty | number | 1 | Cantidad | | type | string | 'Servicio' | 'Bien' o 'Servicio' | | unitOfMeasure | string | 'UNI' | Código de unidad de SAT | | petroleo_amount | number | — | Impuesto PETROLEO por unidad (omitir para ítems sólo-IVA) | | petroleo_code | string | '1' | '1'=SUPER, '2'=REGULAR, '4'=DIESEL. Si se usa sin petroleo_amount, el código debe estar en petroleo_rates o se lanza DigifactValidationError. |

Configuración de frases (TipoFrase / CodigoEscenario)

Todo DTE (excepto FESP) debe llevar un par TipoFrase + CodigoEscenario. El SDK elige valores por defecto adecuados, por lo que no hace falta configurar nada en el caso común.

Orden de precedencia: opts por llamada → globales del constructor (tipo_frase / escenario) → tabla de valores por defecto.

Tabla de valores por defecto:

| DTE | Afiliación | TipoFrase | CodigoEscenario | Notas | |-------------|-----------:|:---------:|:---------------:|-------| | FESP | — | — | — | Sin bloque AdditionlInfo | | FPEQ | PEQ | 2 | 1 | Pequeño contribuyente | | RDON | cualquiera | 4 | 4 | Donaciones | | RECI | cualquiera | 4 | 5 | Recibos (universidades) | | NABN | cualquiera | 1 | 1 | Abonos | | FACT / FCAM / NCRE / NDEB | GEN | 1 | 1 | Por defecto: ISR régimen sobre utilidades trimestrales | | FACT / FCAM / NCRE / NDEB | PEQ | 2 | 1 | | | FACT / FCAM / NCRE / NDEB | EXE | 4 | 1 | Exento |

Tanto tipo_frase como escenario se pueden sobreescribir de forma independiente — por llamada (dentro del objeto opts) o globalmente al construir el cliente. Cuando se omiten, cada uno cae al global del constructor y luego a la tabla de valores por defecto.

// Sobreescritura por llamada (uno o ambos)
await client.invoice('CF', items, { escenario: '1' });
await client.invoice('CF', items, { tipo_frase: '2', escenario: '1' });

// Funciona igual en los demás métodos de DTE
await client.creditNote('12345678', items, origin, '...', { tipo_frase: '2', escenario: '1' });
await client.fuelInvoice('CF', items, { tipo_frase: '2', escenario: '1' });

// O globalmente al construir el cliente (p. ej. GEN + ISR régimen opcional simplificado)
const client = new DigifactClient({
  taxid: '12345678', username: 'FELUSER', password: '...',
  afiliacion_iva: 'GEN',
  tipo_frase: '1',  // opcional — la tabla ya devuelve '1' para GEN
  escenario: '2',   // ISR régimen opcional simplificado (sobreescribe el '1' por defecto)
});

Referencia de métodos

Todos los métodos son asíncronos. Los de emisión devuelven DteResult con result.authNumber, series, number, issueDateTime, raw.

| Método | Firma | Descripción | |--------|-------|-------------| | invoice() | invoice(buyer, items, opts = {}) | Emite FACT, FCAM, FESP, FPEQ, NABN, RDON o RECI según opts.doc_type. | | ccaInvoice() | ccaInvoice(buyer, items, cobros, opts = {}) | FACT con complemento CCA. | | fuelInvoice() | fuelInvoice(buyer, items, opts = {}) | FACT con complemento combustible (IVA + PETROLEO). | | creditNote() | creditNote(buyer, items, origin, reason, opts = {}) | Nota de crédito (NCRE). | | debitNote() | debitNote(buyer, items, origin, reason, opts = {}) | Nota de débito (NDEB). | | creditNoteTotal() | creditNoteTotal(authNumber, issueDateTime, reason = '...', reference = '') | Nota de crédito total. Devuelve object. | | cancel() | cancel(authNumber, receiverId, issueDateTime, reason = 'Anulación') | Anula un DTE. Devuelve object. | | lookupNit() | lookupNit(nit) | Consulta SAT. Devuelve { nit, name, address, city, district, state }. | | getDte() | getDte(authNumber, format = 'JSON') | Recupera el DTE ('JSON', 'XML', 'HTML', 'PDF'). | | getDteInfo() | getDteInfo(authNumber) | Metadatos del DTE. |

Parámetros comunes

  • buyer: 'CF' (consumidor final), un NIT string ('12345678' — se consulta el nombre), un objeto CUI ({ type: 'CUI', taxid, name }) o un objeto NIT explícito ({ taxid, name, address, city, district, state, country, email }).
  • items: array de objetos con description (req), price (req), qty (1), type ('Servicio'/'Bien'), unit_of_measure ('UNI'), discount (opcional).
  • opts: doc_type, payment_terms (req. para FCAM), amount_str, observaciones, tipo_personeria, tipo_frase, escenario.
  • origin (NCRE/NDEB): { auth_number, date: 'YYYY-MM-DD', series, number }.

Establecimiento (sucursal)

Cada NIT puede tener varios establecimientos registrados en el RTU. Configúralos al crear el cliente:

const client = new DigifactClient({
  taxid: '12345678',
  username: 'FELUSER',
  password: 'secret',
  branch_code: '2',
  branch_name: 'SUCURSAL ZONA 10',
});

Aplican a todos los DTE emitidos por ese cliente. Si se omiten, se usan los defaults '1' / 'ESTABLECIMIENTO PRINCIPAL'.

Manejo de errores

import {
  DigifactError,            // base
  DigifactAuthError,        // fallo de autenticación
  DigifactApiError,         // error HTTP / de API
  DigifactValidationError,  // rechazo de SAT
  DigifactNitNotFoundError, // NIT no encontrado
} from 'digifact-sdk';

try {
  const r = await client.invoice('CF', items);
} catch (e) {
  if (e instanceof DigifactValidationError) {
    console.error('SAT rechazó:', e.message, e.raw);
  } else if (e instanceof DigifactError) {
    console.error('Error del SDK:', e.message);
  }
}

Ejecutar las pruebas

# Pruebas unitarias (sin credenciales)
node --test --test-name-pattern='Unit' tests/integration.test.js

# Todas las pruebas incluyendo integración
export DIGIFACT_TAXID=12345678
export DIGIFACT_USERNAME=FELUSER
export DIGIFACT_PASSWORD=tu_contraseña
npm test

Requisitos

  • Node.js 18+ (usa fetch y BigInt nativos)
  • No requiere dependencias npm