Ir al contenido

Ejemplo Node + TypeScript

El Inicio rápido muestra los cuatro pasos por separado. Aquí está el mismo flujo empaquetado como un módulo reutilizable, con el manejo de errores que necesitas en producción.

  • Directoriosrc
    • Directoriofacturacion
      • emitir-factura.ts el flujo completo
      • config.ts ambiente y credenciales
    • index.ts
  • Directoriocertificados
    • certificado.p12

Separa lo que cambia entre ambientes para no tocar el código al desplegar.

src/facturacion/config.ts
import { ENV_ENUM } from "osodreamer-sri-xml-signer";
import * as fs from "fs";
const esProduccion = process.env.NODE_ENV === "production";
export const config = {
ambiente: esProduccion ? ENV_ENUM.PROD : ENV_ENUM.TEST,
env: (esProduccion ? "prod" : "test") as "test" | "prod",
p12Buffer: fs.readFileSync(process.env.CERT_PATH!),
password: process.env.CERT_PASSWORD!,
};
src/facturacion/emitir-factura.ts
import {
generateXmlInvoice,
signXml,
validateXml,
authorizeXml,
ValidationException,
InvalidP12PasswordError,
SRIRejectedError,
type ComprobanteType,
} from "osodreamer-sri-xml-signer";
import { config } from "./config";
type Resultado =
| { ok: true; claveAcceso: string; xmlAutorizado: string }
| { ok: false; motivo: string; detalles?: string[] };
export async function emitirFactura(
comprobante: ComprobanteType
): Promise<Resultado> {
try {
// 1. Generar
const { generatedXml, invoiceJson } = await generateXmlInvoice(comprobante);
const claveAcceso = invoiceJson.factura.infoTributaria.claveAcceso;
// 2. Firmar
const firmado = await signXml({
p12Buffer: config.p12Buffer,
password: config.password,
xmlBuffer: new TextEncoder().encode(generatedXml),
});
// 3. Validar (recepción)
await validateXml({
xml: new TextEncoder().encode(firmado),
env: config.env,
});
// 4. Autorizar
const autorizacion = await authorizeXml({ claveAcceso, env: config.env });
if (autorizacion.estadoAutorizacion !== "AUTORIZADO") {
return { ok: false, motivo: autorizacion.estadoAutorizacion };
}
return {
ok: true,
claveAcceso,
xmlAutorizado: autorizacion.comprobanteCrudo,
};
} catch (error) {
// Datos del comprobante mal formados: se pueden corregir y reintentar
if (error instanceof ValidationException) {
return { ok: false, motivo: "datos_invalidos", detalles: error.errors };
}
// Problema con el certificado
if (error instanceof InvalidP12PasswordError) {
return { ok: false, motivo: "certificado_password" };
}
// El SRI rechazó el comprobante
if (error instanceof SRIRejectedError) {
return {
ok: false,
motivo: `sri_${error.identificador}`,
detalles: [error.mensajeSRI, error.informacionAdicional],
};
}
throw error; // lo desconocido no se silencia
}
}
src/index.ts
import { emitirFactura } from "./facturacion/emitir-factura";
import { config } from "./facturacion/config";
const resultado = await emitirFactura({
infoTributaria: {
ambiente: config.ambiente,
razonSocial: "Mi Empresa S.A.",
ruc: "1790012345001",
estab: "001",
ptoEmi: "001",
secuencial: await siguienteSecuencial("001", "001"),
dirMatriz: "Av. Amazonas N33-100",
},
// resto del comprobante...
});
if (resultado.ok) {
await guardarXml(resultado.claveAcceso, resultado.xmlAutorizado);
} else {
console.error(resultado.motivo, resultado.detalles);
}