Usuario o contraseña incorrectos
POSCAM SPA · PROD · palena.sii.cl
POSCAM ERP — Sistema Tributario Chile
Dashboard
77.088.475-6 — POSCAM SPA
Emitidas este mes
47
↑ 12% vs anterior
Monto neto mes
$8.42M
IVA: $1.60M
Por cobrar
$4.2M
6 facturas activas
Automáticas activas
5
Próxima: día 1
Facturación 2025Neto mensual
Ene
$5.2M
Feb
$6.0M
Mar
$7.1M
Abr
$7.9M
May
$8.4M
Estado SII
Token authActivo
Certificado .p12Válido
Folios FAC 33312 disp.
Folios NC 6198 disp.
Último DTEhoy 11:47
Documentos recientes
FolioReceptorNetoTotalEstadoFecha
Facturas Electrónicas (33)
FolioReceptorRUTNetoIVATotalEstadoFechaCond. pagoAcciones
ℹ️ Las NC referencian una factura aceptada. Pueden anular totalmente (motivo 1) o corregir montos (motivo 3).
Notas de Crédito (61)
Folio NCFac. ref.ReceptorMotivoMontoEstadoFecha
Directorio de clientes
RUTRazón SocialGiroEmailCiudadFacturasPor cobrarAcciones
Catálogo
CódigoDescripciónPrecio netoIVATotalStock
Proveedores y compras
RUTRazón SocialGiroEmailFacturas recibidasSaldo pendienteAcciones
Total por cobrar
$6.8M
12 facturas
Vencidas +30d
$2.1M
4 facturas
Cobrado mes
$5.3M
↑ 8%
Cartera de cobranza
Cobrado este mes
$5.3M
Pagado proveedores
$1.8M
Pendiente cobro
$6.8M
Pendiente pago
$1.6M
💰 Pagos recibidos
💳 Pagos a proveedores
↻ Se emiten el día configurado de cada mes. El XML se firma con tu certificado .p12 y se envía al SII + correo del receptor.
Enviados mes
52
Entregados
50
Fallidos
2
Historial de envíos XML
Fecha/HoraDestinatarioAsuntoFolioTipoEstadoAdjunto
Documentos
47
Monto neto
$8.42M
IVA débito fiscal
$1.60M
Libro de Ventas — Mayo 2025
FolioTipoRUTRazón SocialNetoIVATotalFecha
⚠️ Referencial. Verifica con tu contador antes de declarar.
📊 Resumen IVA Mayo 2025
Débito fiscal ventas$1.600.200
Ajuste NC (61)−$79.800
IVA a declarar$1.520.400
📅 Calendario tributario
F29 Mayo 202512 Jun
Libro ventas electrónico15 Jun
F29 Abril 2025Presentado
Libro ventas AbrilPresentado
Usuarios y permisos
Roles disponibles
AdministradorAcceso total al sistema, configuración SII, usuarios
ContadorFacturas, NC, reportes, libro de ventas, F29
FacturadorCrear facturas, NC, clientes — sin configuración
Solo lecturaVer reportes y documentos únicamente
⚠️ Estos datos aparecen en todos los documentos tributarios emitidos.
🏛 Datos tributarios
🎨 Personalización facturas
📧 Correo empresa
XML al receptor automático
Alerta rechazo SII
Resumen mensual por email
🔑 Cambiar Contraseña
👥 Usuarios activos
⚠️ Producción activa — palena.sii.cl. Los DTE emitidos tienen validez legal.
🔐 Certificado digital
🔗 Test de conexión SII
Endpointpalena.sii.cl
Semilla
Firma RSA-SHA1
Token
Obtén CAF en misiir.cl → DTE → Solicitar folios. Sube el XML aquí.
Factura (33)
312
Folios 501–900 · próximo: 589
NC (61)
98
Folios 1–200 · próximo: 103
Subir archivo CAF
📂
Arrastra el archivo CAF aquí
XML descargado desde misiir.cl · tipo detectado automáticamente
Esta guía está diseñada específicamente para tu Contabo VPS con Ubuntu. Sigue los pasos en orden.
① Instalar
② .env
③ Auth SII
④ DTE Builder
⑤ Scheduler
⑥ Nginx + SSL
📦 1. Preparar el VPS Contabo
# Conecta a tu VPS por SSH ssh root@TU_IP_CONTABO # Actualizar sistema apt update && apt upgrade -y # Instalar Node.js 20 LTS curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt install -y nodejs # Verificar versiones node --version # debe ser v20.x npm --version # debe ser 10.x # Instalar PM2 (gestor de procesos) npm install -g pm2 # Instalar Git apt install -y git # Crear directorio del proyecto mkdir -p /opt/poscam-backend cd /opt/poscam-backend
📦 2. Instalar dependencias Node
cd /opt/poscam-backend npm init -y # Dependencias principales npm install express cors dotenv npm install node-forge # firma RSA-SHA1 npm install xmlbuilder2 xml2js # XML DTE npm install axios # HTTP al SII npm install nodemailer # envío email con XML npm install node-cron # facturas automáticas npm install mongoose # base de datos npm install multer # subida .p12 y CAF npm install bcryptjs jsonwebtoken # auth usuarios npm install helmet express-rate-limit # seguridad # Instalar MongoDB apt install -y mongodb systemctl enable mongodb systemctl start mongodb # Estructura del proyecto mkdir -p certs routes services models touch index.js .env
🔑 Archivo .env — completo para Contabo
# /opt/poscam-backend/.env # NUNCA subir a Git — agregar .env a .gitignore # ── SII PRODUCCIÓN ────────────────────────── SII_AMBIENTE=produccion SII_HOST=palena.sii.cl SII_URL_SEED=https://palena.sii.cl/DTEWS/CrSeed.jws SII_URL_TOKEN=https://palena.sii.cl/DTEWS/GetTokenFromSeed.jws SII_URL_UPLOAD=https://palena.sii.cl/cgi_dte/UPL_DTE SII_URL_ESTADO=https://palena.sii.cl/cgi_dte/UPL_DTE # ── CERTIFICADO DIGITAL ───────────────────── CERT_PATH=./certs/certificado.p12 CERT_PASSWORD=tu_contraseña_aqui RUT_FIRMA=12345678-9 # representante legal # ── DATOS EMPRESA ─────────────────────────── EMPRESA_RUT=76543210-K EMPRESA_RAZON=POSCAM SPA EMPRESA_GIRO=INSTALACION Y SERVICIO DE ACCESORIOS PARA VEHICULOS EMPRESA_DIR=PASAJE NUMPAY 1641 STA. TERESA DE COLIN EMPRESA_CIUDAD=Talca RES_SII= # ── CORREO (SMTP) ─────────────────────────── SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=[email protected] SMTP_PASS=abcd_efgh_ijkl_mnop # App Password Gmail EMAIL_FROM="POSCAM ERP <[email protected]>" EMAIL_NOTIFY=[email protected] EMAIL_CC=[email protected] # ── BASE DE DATOS ─────────────────────────── MONGODB_URI=mongodb://localhost:27017/poscam # ── SERVIDOR ──────────────────────────────── PORT=3000 JWT_SECRET=poscam_secreto_muy_largo_y_seguro_2025 FRONTEND_URL=https://poscam.miempresa.cl
🔐 services/sii-auth.js
const forge = require('node-forge'); const axios = require('axios'); const fs = require('fs'); const xml2js = require('xml2js'); let _token = null; let _tokenExp = null; async function getToken() { // Reusar token si no ha expirado (1 hora) if (_token && Date.now() < _tokenExp) return _token; // 1. Cargar certificado .p12 const p12Der = fs.readFileSync(process.env.CERT_PATH); const p12b64 = forge.util.encode64(p12Der.toString('binary')); const p12Asn1 = forge.asn1.fromDer(forge.util.decode64(p12b64)); const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, process.env.CERT_PASSWORD); // 2. Extraer clave privada const keyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag }); const privKey = keyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0].key; // 3. Obtener semilla del SII const seedResp = await axios.get(process.env.SII_URL_SEED); const seedParsed = await xml2js.parseStringPromise(seedResp.data); const seed = seedParsed.SII_BODY.SEMILLA[0]; // 4. Firmar semilla RSA-SHA1 const md = forge.md.sha1.create(); md.update(seed, 'utf8'); const sig64 = forge.util.encode64(privKey.sign(md)); // 5. Solicitar token const xml = `<getToken> <item> <Semilla>${seed}</Semilla> <Signature>${sig64}</Signature> </item> </getToken>`; const tokResp = await axios.post(process.env.SII_URL_TOKEN, xml, { headers: { 'Content-Type': 'text/xml' }}); const tokParsed = await xml2js.parseStringPromise(tokResp.data); _token = tokParsed.SII_BODY.TOKEN[0]; _tokenExp = Date.now() + 3500000; // ~58 min return _token; } module.exports = { getToken };
📤 services/mailer.js
const nodemailer = require('nodemailer'); const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: +process.env.SMTP_PORT, secure: false, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } }); async function sendXML({ to, folio, tipo, xmlContent, razonReceptor }) { const label = tipo === '61' ? 'Nota de Crédito' : 'Factura Electrónica'; await transporter.sendMail({ from: process.env.EMAIL_FROM, to, cc: process.env.EMAIL_CC || undefined, subject: `${label}${folio}${process.env.EMPRESA_RAZON}`, html: `<p>Estimado(a) ${razonReceptor},</p> <p>Adjunto encontrará su ${label.toLowerCase()}${folio} emitida por <strong>${process.env.EMPRESA_RAZON}</strong>.</p> <p>Puede verificar la validez del documento en misiir.cl</p>`, attachments: [{ filename: `DTE_${tipo}_${folio}.xml`, content: xmlContent, contentType: 'application/xml' }] }); } module.exports = { sendXML };
📄 services/dte-builder.js — Constructor DTE completo
const { create } = require('xmlbuilder2'); const forge = require('node-forge'); const fs = require('fs'); // Carga el certificado una sola vez function loadCert() { const p12Der = fs.readFileSync(process.env.CERT_PATH); const p12 = forge.pkcs12.pkcs12FromAsn1( forge.asn1.fromDer(p12Der.toString('binary')), process.env.CERT_PASSWORD ); const keyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag }); const certBags = p12.getBags({ bagType: forge.pki.oids.certBag }); return { key: keyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0].key, cert: certBags[forge.pki.oids.certBag][0].cert }; } function buildDTE(factura) { const cfg = { rut: process.env.EMPRESA_RUT, razon: process.env.EMPRESA_RAZON, giro: process.env.EMPRESA_GIRO, dir: process.env.EMPRESA_DIR, ciudad:process.env.EMPRESA_CIUDAD, res: process.env.RES_SII }; const doc = create({ version: '1.0', encoding: 'ISO-8859-1' }) .ele('DTE', { version: '1.0', xmlns: 'http://www.sii.cl/SiiDte' }) .ele('Documento', { ID: `DTE-${factura.tipo}-${factura.folio}` }) .ele('Encabezado') .ele('IdDoc') .ele('TipoDTE').txt(factura.tipo).up() .ele('Folio').txt(factura.folio).up() .ele('FchEmis').txt(factura.fecha).up() .ele('IndMntNeto').txt('2').up() .ele('TpoTranVenta').txt('1').up() .ele('FmaPago').txt('1').up() .up() .ele('Emisor') .ele('RUTEmisor').txt(cfg.rut).up() .ele('RznSoc').txt(cfg.razon).up() .ele('GiroEmis').txt(cfg.giro).up() .ele('DirOrigen').txt(cfg.dir).up() .ele('CmnaOrigen').txt(cfg.ciudad).up() .ele('CiudadOrigen').txt(cfg.ciudad).up() .up() .ele('Receptor') .ele('RUTRecep').txt(factura.rutReceptor).up() .ele('RznSocRecep').txt(factura.razonReceptor).up() .ele('GiroRecep').txt(factura.giroReceptor || '').up() .ele('DirRecep').txt(factura.dirReceptor || '').up() .up() .ele('Totales') .ele('MntNeto').txt(factura.neto).up() .ele('TasaIVA').txt('19.00').up() .ele('IVA').txt(factura.iva).up() .ele('MntTotal').txt(factura.total).up() .up() .up(); // Agregar líneas de detalle factura.items.forEach((item, i) => { doc.ele('Detalle') .ele('NroLinDet').txt(i + 1).up() .ele('NmbItem').txt(item.desc).up() .ele('QtyItem').txt(item.qty).up() .ele('PrcItem').txt(item.precio).up() .ele('MontoItem').txt(Math.round(item.qty * item.precio)).up() .up(); }); // Si es NC (61), agregar referencia if (factura.tipo === '61' && factura.folioRef) { doc.ele('Referencia') .ele('NroLinRef').txt('1').up() .ele('TpoDocRef').txt('33').up() .ele('FolioRef').txt(factura.folioRef).up() .ele('CodRef').txt(factura.motivoRef || '1').up() .up(); } return doc.end({ prettyPrint: true }); } module.exports = { buildDTE, loadCert };
⏰ services/scheduler.js — Automáticas
const cron = require('node-cron'); const Programada = require('../models/Programada'); const { emitirFactura } = require('./emisor'); // Corre todos los días a las 08:00 AM cron.schedule('0 8 * * *', async () => { const hoy = new Date(); const diaHoy = hoy.getDate(); const ultimoDia = new Date( hoy.getFullYear(), hoy.getMonth()+1, 0 ).getDate(); // Buscar todas las automáticas activas const progs = await Programada.find({ activa: true }); for (const p of progs) { const diaEmision = p.dia === 'ultimo' ? ultimoDia : parseInt(p.dia); if (diaHoy === diaEmision) { try { await emitirFactura({ rutReceptor: p.rut, razonReceptor: p.razon, emailReceptor: p.email, neto: p.monto, iva: Math.round(p.monto * 0.19), total: p.monto + Math.round(p.monto * 0.19), items: [{ desc: p.descripcion, qty: 1, precio: p.monto }], enviarXML: p.enviarXML, notificar: p.notificar }); console.log(`✅ Emitida: ${p.nombre} folio OK`); } catch(e) { console.error(`❌ Error: ${p.nombre}:`, e.message); } } } }, { timezone: 'America/Santiago' });
📡 routes/programadas.js — CRUD completo
// GET todas las automatizaciones router.get('/', async (req, res) => { const list = await Programada.find(); res.json(list); }); // POST nueva automatización router.post('/', async (req, res) => { const prog = new Programada(req.body); await prog.save(); res.json(prog); }); // PUT editar — monto, desc, dia, receptor router.put('/:id', async (req, res) => { const { nombre, dia, monto, descripcion, rut, razon, email, activa, enviarXML, notificar } = req.body; const updated = await Programada.findByIdAndUpdate( req.params.id, { nombre, dia, monto, descripcion, rut, razon, email, activa, enviarXML, notificar, updatedAt: new Date() }, { new: true } ); res.json(updated); }); // PATCH activar/pausar individual router.patch('/:id/toggle', async (req, res) => { const prog = await Programada.findById(req.params.id); prog.activa = !prog.activa; await prog.save(); res.json({ activa: prog.activa }); }); // DELETE eliminar router.delete('/:id', async (req, res) => { await Programada.findByIdAndDelete(req.params.id); res.json({ ok: true }); });
🌐 Nginx como proxy inverso
# Instalar Nginx apt install -y nginx # Crear configuración para POSCAM nano /etc/nginx/sites-available/poscam # ── Contenido del archivo ────────────────── server { listen 80; server_name poscam.miempresa.cl; # Redirigir HTTP a HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl; server_name poscam.miempresa.cl; ssl_certificate /etc/letsencrypt/live/poscam.miempresa.cl/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/poscam.miempresa.cl/privkey.pem; # API backend Node.js location /api/ { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } # Frontend ERP (archivo HTML) location / { root /opt/poscam-frontend; index index.html; try_files $uri $uri/ /index.html; } } # ─────────────────────────────────────────── # Activar el sitio ln -s /etc/nginx/sites-available/poscam /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx
🔒 SSL gratuito + PM2
# Instalar Certbot para SSL gratuito apt install -y certbot python3-certbot-nginx # Obtener certificado SSL (Let's Encrypt) certbot --nginx -d poscam.miempresa.cl # Renovación automática (ya incluida) systemctl status certbot.timer # ── PM2 — Gestión del proceso Node.js ────── cd /opt/poscam-backend # Iniciar la aplicación pm2 start index.js --name poscam-api # Ver logs en tiempo real pm2 logs poscam-api # Reiniciar si hay cambios pm2 restart poscam-api # Configurar para arrancar con el servidor pm2 startup systemd pm2 save # ── Firewall (UFW) ────────────────────────── ufw allow 22 # SSH ufw allow 80 # HTTP ufw allow 443 # HTTPS ufw enable # ── Subir el ERP frontend ─────────────────── mkdir -p /opt/poscam-frontend # Copia POSCAM-ERP.html como index.html: cp /ruta/POSCAM-ERP.html /opt/poscam-frontend/index.html # Actualizar la URL de la API en el JS del ERP: # Busca: const API_URL = '...' # Cambia a: const API_URL = 'https://poscam.miempresa.cl/api' # ── Verificar todo funciona ───────────────── curl https://poscam.miempresa.cl/api/health # Debe responder: { "status": "ok", "sii": "connected" }