El problema de la impresión en el navegador
Si alguna vez has intentado imprimir un recibo desde una aplicación web, conoces el dolor: window.print() abre un cuadro de diálogo de impresión, renderiza la página como un PDF y no tiene soporte para comandos ESC/POS como corte automático o ancho de papel personalizado.
Para las impresoras térmicas de recibos, esto es un gran inconveniente. Necesitas:
- Impresión silenciosa — sin diálogo, sin interacción del usuario
- Comandos ESC/POS — para corte automático, texto en negrita, alineación, códigos de barras
- Codificación fiable — UTF-8 a la página de códigos correcta para tu impresora
- Soporte multiplataforma — Mac, Windows, Linux
La solución es un puente WebSocket local — una pequeña aplicación en segundo plano que se ejecuta en el ordenador del usuario, acepta trabajos de impresión de tu aplicación web y envía comandos ESC/POS puros a la impresora.
Cómo funciona el puente WebSocket
Tu aplicación web → WebSocket (ws://localhost:8765) → Agente de impresión → Impresora térmica
El flujo es simple:
- Tu aplicación web abre una conexión WebSocket a ws://localhost:8765
- Envías una carga útil JSON que describe el recibo
- El Agente de impresión lo convierte a ESC/POS y lo envía a la impresora
- La impresora imprime silenciosamente — sin diálogo, sin interacción con el controlador
MenuForma Print Agent implementa este puente. Es gratuito, de código abierto y se ejecuta en Mac, Windows y Linux.
Referencia de la API
Conexión
const ws = new WebSocket('ws://localhost:8765');
ws.onopen = () => {
console.log('Print Agent conectado');
};
ws.onerror = () => {
console.log('Print Agent no está en ejecución');
};
Comprobar el estado del agente
Envía un ping para verificar que el agente está en ejecución:
ws.send(JSON.stringify({ type: 'ping' }));
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'pong') {
console.log('Versión del agente:', data.version);
}
};
Imprimir un recibo
const receipt = {
type: 'print',
printer: 'auto', // o IP específica como '192.168.1.100'
data: {
storeName: 'Mi Restaurante',
storeAddress: '123 Calle Principal',
orderNumber: '#1042',
tableNumber: 'Mesa 5',
items: [
{ name: 'Pizza Margherita', qty: 1, price: 14.90 },
{ name: 'Agua con gas', qty: 2, price: 3.50 },
],
subtotal: 21.90,
tax: 1.97,
total: 23.87,
paymentMethod: 'Tarjeta',
footer: '¡Gracias por cenar con nosotros!',
}
};
ws.send(JSON.stringify(receipt));
Ejemplo de integración con React
import { useEffect, useRef, useCallback, useState } from 'react';
function usePrintAgent() {
const wsRef = useRef(null);
const [connected, setConnected] = useState(false);
useEffect(() => {
const connect = () => {
const ws = new WebSocket('ws://localhost:8765');
ws.onopen = () => setConnected(true);
ws.onclose = () => {
setConnected(false);
setTimeout(connect, 3000); // reintentar
};
wsRef.current = ws;
};
connect();
return () => wsRef.current?.close();
}, []);
const print = useCallback((receiptData) => {
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
throw new Error('Print Agent not connected');
}
wsRef.current.send(JSON.stringify({ type: 'print', data: receiptData }));
}, []);
return { connected, print };
}
Integración con Shopify
Para las tiendas Shopify, puedes activar la impresión desde un webhook de pedido o desde una extensión del navegador:
// En tu tienda Shopify o extensión de administración
async function printShopifyOrder(order) {
const ws = new WebSocket('ws://localhost:8765');
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'print',
data: {
storeName: order.shop_name,
orderNumber: order.name,
items: order.line_items.map(item => ({
name: item.title,
qty: item.quantity,
price: parseFloat(item.price),
})),
total: parseFloat(order.total_price),
paymentMethod: order.payment_gateway,
}
}));
};
}
Configuración de la Impresora
El campo "printer" en la carga útil de impresión acepta:
| Valor | Descripción |
|---|---|
| "auto" | Usar la impresora configurada en los ajustes del Agente de Impresión |
| "192.168.1.100" | Conectar directamente a una impresora de red por IP |
| "USB" | Usar la primera impresora USB detectada |
Manejo de Errores
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
if (response.type === 'print_success') {
console.log('Printed successfully');
} else if (response.type === 'print_error') {
console.error('Print failed:', response.error);
// Mostrar UI de respaldo o reintentar
}
};
Detectando si el Agente de Impresión está Instalado
Antes de mostrar la funcionalidad de impresión en tu interfaz de usuario, verifica si el agente está ejecutándose:
async function isPrintAgentAvailable() {
return new Promise((resolve) => {
const ws = new WebSocket('ws://localhost:8765');
const timeout = setTimeout(() => {
ws.close();
resolve(false);
}, 1000);
ws.onopen = () => {
clearTimeout(timeout);
ws.close();
resolve(true);
};
ws.onerror = () => {
clearTimeout(timeout);
resolve(false);
};
});
}
// Uso
const agentAvailable = await isPrintAgentAvailable();
if (!agentAvailable) {
// Mostrar mensaje de descarga
window.open('/print-agent', '_blank');
}
Descargar Agente de Impresión
MenuForma Print Agent es gratuito y está disponible para Mac, Windows y Linux. Soporta impresoras térmicas USB, de red y Bluetooth, e implementa la API completa de WebSocket descrita en esta guía.
Para obtener la documentación completa de la API y el código de código abierto, visita la página del Agente de Impresión.
Related Articles
- La crisis de personal en la hostelería española acelera la adopción de menús QR en 2025
- Gentrificación y nuevos retos: Por qué los restaurantes en México apuestan por la tecnología en 2025
- Cierres de restaurantes en España en 2025: La digitalización como salvavidas del sector