DEV Community

Luis Dev
Luis Dev

Posted on

🚀 Automatiza tu Facturación Electrónica en Perú: De Manual a API en 10 minutos con Node.js

¿Sigues emitiendo facturas manualmente? Si eres desarrollador en Perú, probablemente has sufrido con los procesos manuales de facturación de SUNAT. ¡Hay una mejor manera! Te muestro cómo automatizar todo el proceso con menos de 50 líneas de código.

Facturación Electrónica Automatizada

📋 Lo que vas a aprender

  • ✅ Configurar facturación electrónica desde código
  • ✅ Integrar SUNAT con Node.js usando APIs modernas
  • ✅ Manejar errores y validaciones como un pro
  • ✅ Optimizar el flujo para aplicaciones en producción
  • ✅ Casos de uso reales y mejores prácticas

Tiempo estimado: 10-15 minutos | Nivel: Intermedio


🎯 El problema que todos enfrentamos

Como desarrolladores, constantemente necesitamos integrar sistemas de facturación en nuestras aplicaciones. El proceso tradicional implica:

  • 😤 Llenar formularios web manualmente
  • ⏰ Perder tiempo en tareas repetitivas
  • 🐛 Errores humanos en la captura de datos
  • 🔄 Falta de automatización en workflows

La solución: Una API que maneje todo esto por nosotros.

🛠️ Tecnologías que usaremos

// Stack técnico
const stack = {
  runtime: "Node.js 18+",
  API: "Billme",
  method: "REST API",
  format: "JSON",
  auth: "Token-based"
};
Enter fullscreen mode Exit fullscreen mode

🚀 Setup inicial (2 minutos)

Prerrequisitos

  • Node.js 18 o superior
  • Cuenta gratuita en Billme
  • RUC de prueba

1. Crear cuenta de testing

# Paso 1: Registrarse en Billme
# Paso 2: Crear empresa de prueba
# Paso 3: Obtener token de sandbox
Enter fullscreen mode Exit fullscreen mode

💡 Pro tip: Billme tiene ambiente de pruebas gratuito, perfecto para desarrollo y testing.

Sigue su documentación oficial para el setup inicial.

💻 Implementación paso a paso

Estructura del proyecto

facturacion-sunat/
├── src/
│   ├── config/
│   │   └── billme.js
│   ├── services/
│   │   └── invoiceService.js
│   ├── utils/
│   │   └── validator.js
│   └── script.js
└── package.json
Enter fullscreen mode Exit fullscreen mode

1. Configuración base

// config/billme.js
const config = {
  baseURL: 'https://www.api.billmeperu.com/api/v1',
  endpoints: {
    invoice: '/Emission/EnviarBoletaFactura'
  },
  token: process.env.BILLME_TOKEN || 'your-token-here'
};

export default config;
Enter fullscreen mode Exit fullscreen mode

2. Servicio de facturación (¡La magia sucede aquí!)

// services/invoiceService.js
import config from '../config/billme.js';

class InvoiceService {
  constructor() {
    this.baseURL = config.baseURL;
    this.token = config.token;
  }

  /**
   * Emite una factura electrónica a SUNAT
   * @param {Object} invoiceData - Datos de la factura
   * @returns {Promise<Object>} Respuesta de SUNAT
   */
  async emitInvoice(invoiceData) {
    const url = `${this.baseURL}${config.endpoints.invoice}`;

    try {
      console.log('🚀 Emitiendo factura a SUNAT...');

      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'token': this.token,
          'Accept': 'application/json'
        },
        body: JSON.stringify(invoiceData)
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(`Error ${response.status}: ${errorData.message || 'Error desconocido'}`);
      }

      const result = await response.json();

      console.log('✅ Factura emitida exitosamente');
      console.log(`📄 Serie: ${invoiceData.serie}-${invoiceData.correlativo}`);
      console.log(`💰 Monto: S/ ${invoiceData.totales.totalPagar}`);

      return result;

    } catch (error) {
      console.error('❌ Error al emitir factura:', error.message);
      throw error;
    }
  }

  /**
   * Genera datos de factura de ejemplo
   * @returns {Object} Estructura de factura válida
   */
  generateSampleInvoice() {
    return {
      tipoOperacion: "010",
      serie: "F001", 
      correlativo: this.generateCorrelativo(),
      fechaEmision: new Date().toISOString().split('T')[0],
      horaEmision: "00:00:00",
      fechaVencimiento: new Date().toISOString().split('T')[0],
      codigoTipoOperacion: "0101",
      codigoTipoDocumento: "01",
      moneda: "PEN",
      montoCredito: 0,
      formaPago: "Contado",
      igv: 18.00,
      icbper: 0,
      cuotas: [],

      // Datos del emisor (tu empresa)
      emisor: {
        codigoTipoDocumento: "6",
        numDocumento: "20123456721",
        razonSocial: "TU EMPRESA SAC",
        nombreComercial: "TU EMPRESA",
        ubigeo: "150101", // Lima
        ciudad: "LIMA",
        distrito: "LIMA", 
        provincia: "LIMA",
        direccion: "AV. EJEMPLO 123, LIMA"
      },

      // Datos del cliente
      cliente: {
        codigoTipoDocumento: "1", // DNI
        numDocumento: "12345678",
        razonSocial: "CLIENTE EJEMPLO",
        nombreComercial: "CLIENTE EJEMPLO",
        ubigeo: "",
        ciudad: "",
        distrito: "",
        provincia: "",
        direccion: ""
      },

      // Cálculos automáticos
      totales: {
        totalOpGravadas: 100.00,
        totalOpInafectas: 0,
        totalOpExoneradas: 0,
        totalImpuestos: 18.00,
        totalSinImpuestos: 100.00,
        totalConImpuestos: 118.00,
        totalPagar: 118.00,
        totalDescuentoGlobal: 0,
        totalDescuentoProductos: 0
      },

      // Productos/servicios
      productos: [{
        unidades: 1,
        codigoUnidad: "NIU", // Unidad
        nombre: "Desarrollo de Software",
        moneda: "PEN",
        precioUnitario: 100.00,
        precioLista: "118.00",
        montoSinImpuesto: 100.00,
        montoImpuestos: 18.00,
        montoTotal: 100.00,
        montoIcbper: 0,
        factorIcbper: 0,
        montoDescuento: 0,
        codigoTipoPrecio: "01",
        impuestos: [{
          monto: 18.00,
          idCategoria: "S",
          porcentaje: 18,
          codigoAfectacionIgv: "10",
          codigoInterTributo: "VAT",
          nombreTributo: "IGV",
          codigoTributo: "1000"
        }],
        id: "DEV001",
        codigoClasificacion: "531117"
      }]
    };
  }

  /**
   * Genera correlativo único
   * @returns {string} Correlativo con formato
   */
  generateCorrelativo() {
    const timestamp = Date.now().toString().slice(-5);
    return timestamp.padStart(8, '0');
  }
}

export default InvoiceService;
Enter fullscreen mode Exit fullscreen mode

3. Script principal con manejo robusto de errores

// script.js
import InvoiceService from './services/invoiceService.js';

class InvoiceApp {
  constructor() {
    this.invoiceService = new InvoiceService();
  }

  async run() {
    console.log('🏁 Iniciando proceso de facturación electrónica...\n');

    try {
      // Generar factura de ejemplo
      const invoiceData = this.invoiceService.generateSampleInvoice();

      console.log('📋 Datos de la factura:');
      console.log(`   Serie: ${invoiceData.serie}-${invoiceData.correlativo}`);
      console.log(`   Cliente: ${invoiceData.cliente.razonSocial}`);
      console.log(`   Monto: S/ ${invoiceData.totales.totalPagar}\n`);

      // Emitir factura
      const result = await this.invoiceService.emitInvoice(invoiceData);

      // Mostrar resultado
      this.displayResult(result);

    } catch (error) {
      this.handleError(error);
    }
  }

  /**
   * Muestra el resultado de la emisión
   * @param {Object} result - Respuesta de la API
   */
  displayResult(result) {
    console.log('\n🎉 ¡FACTURA EMITIDA EXITOSAMENTE!');
    console.log(''.repeat(50));
    console.log(`📋 Número: ${result.numeroFactura || 'N/A'}`);
    console.log(`📅 Fecha: ${result.fechaEmision || 'N/A'}`);
    console.log(`🔗 Link PDF: ${result.linkPdf || 'Generando...'}`);
    console.log(`✅ Estado: ${result.estado || 'Procesado'}`);
    console.log(''.repeat(50));
  }

  /**
   * Maneja errores de manera elegante
   * @param {Error} error - Error ocurrido
   */
  handleError(error) {
    console.log('\n❌ ERROR EN LA EMISIÓN');
    console.log(''.repeat(50));
    console.log(`💥 Mensaje: ${error.message}`);

    // Errores comunes y soluciones
    if (error.message.includes('token')) {
      console.log('💡 Solución: Verifica tu token de Billme');
    } else if (error.message.includes('400')) {
      console.log('💡 Solución: Revisa los datos de la factura');
    } else if (error.message.includes('network') || error.message.includes('fetch')) {
      console.log('💡 Solución: Revisa tu conexión a internet');
    }

    console.log(''.repeat(50));
  }
}

// 🚀 Ejecutar la aplicación
const app = new InvoiceApp();
app.run();
Enter fullscreen mode Exit fullscreen mode

🎯 Ejecutar el código

# Instalar dependencias (si usas package.json)
npm install

# Ejecutar el script
node script.js
Enter fullscreen mode Exit fullscreen mode

Resultado esperado:

🏁 Iniciando proceso de facturación electrónica...

📋 Datos de la factura:
   Serie: F001-00012345
   Cliente: CLIENTE EJEMPLO  
   Monto: S/ 118.00

🚀 Emitiendo factura a SUNAT...
✅ Factura emitida exitosamente
📄 Serie: F001-00012345
💰 Monto: S/ 118.00

🎉 ¡FACTURA EMITIDA EXITOSAMENTE!
══════════════════════════════════════════════════
📋 Número: F001-00012345
📅 Fecha: 2025-06-23
✅ Estado: Aceptado por SUNAT
══════════════════════════════════════════════════
Enter fullscreen mode Exit fullscreen mode

🔥 Casos de uso avanzados

1. Facturación masiva

// Procesar múltiples facturas
const invoices = [
  { cliente: 'Cliente A', monto: 100 },
  { cliente: 'Cliente B', monto: 250 },
  { cliente: 'Cliente C', monto: 500 }
];

for (const invoice of invoices) {
  await processInvoice(invoice);
  await new Promise(resolve => setTimeout(resolve, 1000)); // Rate limiting
}
Enter fullscreen mode Exit fullscreen mode

2. Integración con base de datos

// Ejemplo con MongoDB
const pendingInvoices = await Invoice.find({ status: 'pending' });

for (const invoice of pendingInvoices) {
  try {
    const result = await invoiceService.emitInvoice(invoice.data);
    await Invoice.updateOne(
      { _id: invoice._id }, 
      { status: 'emitted', sunatResponse: result }
    );
  } catch (error) {
    await Invoice.updateOne(
      { _id: invoice._id }, 
      { status: 'error', error: error.message }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Webhook para notificaciones

// Notificar cuando se emita una factura
app.post('/webhook/invoice-emitted', (req, res) => {
  const { invoiceId, status, pdfUrl } = req.body;

  // Enviar email al cliente
  sendEmailToClient(invoiceId, pdfUrl);

  // Actualizar sistema interno
  updateInvoiceStatus(invoiceId, status);

  res.json({ received: true });
});
Enter fullscreen mode Exit fullscreen mode

⚡ Optimizaciones para producción

1. Variables de entorno

# .env
BILLME_TOKEN=your-production-token
BILLME_ENV=production
MAX_RETRIES=3
RATE_LIMIT_MS=1000
Enter fullscreen mode Exit fullscreen mode

2. Rate limiting y reintentos

class RobustInvoiceService extends InvoiceService {
  async emitInvoiceWithRetry(invoiceData, maxRetries = 3) {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await this.emitInvoice(invoiceData);
      } catch (error) {
        if (attempt === maxRetries) throw error;

        console.log(`⚠️ Intento ${attempt} falló, reintentando...`);
        await this.delay(1000 * attempt); // Backoff exponencial
      }
    }
  }

  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}
Enter fullscreen mode Exit fullscreen mode

🎯 Próximos pasos

Una vez que domines lo básico, puedes expandir tu sistema:

  • 📧 Envío automático de facturas por email
  • 📊 Dashboard de reportes y analytics
  • 🔄 Sincronización con sistemas contables
  • 📱 API REST para tu frontend
  • 🤖 Chatbot para consulta de facturas

🤝 Conclusión

¡Felicidades! Acabas de automatizar uno de los procesos más tediosos para cualquier developer peruano. Con este setup puedes:

✅ Emitir facturas desde cualquier aplicación

✅ Integrar con tus sistemas existentes

✅ Escalar sin límites (casi)

✅ Olvidarte de procesos manuales

¿Qué sigue? Te reto a integrar esto en tu próximo project. Las posibilidades son infinitas.


💬 ¿Te funcionó?

Si implementaste este tutorial:

  • 👍 Dale like si te sirvió
  • 💬 Comparte tu experiencia en los comentarios
  • 🔄 Compártelo con otros devs que necesiten esto
  • 🐙 Sube tu versión a GitHub y comparte el repo

¿Tienes dudas? Comenta abajo y te ayudo a resolverlas.


📚 Referencias útiles

Tags: #nodejs #javascript #sunat #facturacion #api #peru #automation #fintech

Top comments (0)