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.
Estructura sugerida
Sección titulada «Estructura sugerida»Directoriosrc
Directoriofacturacion
- emitir-factura.ts el flujo completo
- config.ts ambiente y credenciales
- index.ts
Directoriocertificados
- certificado.p12
Configuración
Sección titulada «Configuración»Separa lo que cambia entre ambientes para no tocar el código al desplegar.
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!,};El flujo completo
Sección titulada «El flujo completo»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 }}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);}Proyecto de ejemplo
Sección titulada «Proyecto de ejemplo»Ver el repositorio completo en GitHubEstructura modular, certificados de prueba y rutas listas para guardar tus XML generados, firmados y autorizados.
