Skip to content
บล็อก

การพิมพ์ ESC/POS จากเบราว์เซอร์: คู่มือสำหรับนักพัฒนา

ปัญหาการพิมพ์บนเบราว์เซอร์

หากคุณเคยลองพิมพ์ใบเสร็จจากเว็บแอป คุณจะรู้ถึงความยุ่งยาก: window.print() จะเปิดกล่องโต้ตอบการพิมพ์, แสดงผลหน้าเว็บเป็น PDF, และไม่มีการรองรับคำสั่ง ESC/POS เช่น การตัดอัตโนมัติ หรือการกำหนดความกว้างกระดาษเองเลย

สำหรับเครื่องพิมพ์ใบเสร็จความร้อน นี่คือปัญหาใหญ่ คุณจำเป็นต้องมี:

  • การพิมพ์แบบเงียบ — ไม่มีกล่องโต้ตอบ ไม่มีการโต้ตอบจากผู้ใช้
  • คำสั่ง ESC/POS — สำหรับการตัดอัตโนมัติ, ตัวหนา, การจัดตำแหน่ง, บาร์โค้ด
  • การเข้ารหัสที่เชื่อถือได้ — UTF-8 ไปยังหน้าโค้ดที่ถูกต้องสำหรับเครื่องพิมพ์ของคุณ
  • การรองรับข้ามแพลตฟอร์ม — Mac, Windows, Linux

ทางออกคือ WebSocket bridge ในเครื่อง — แอปพลิเคชันพื้นหลังขนาดเล็กที่ทำงานบนคอมพิวเตอร์ของผู้ใช้ รับงานพิมพ์จากเว็บแอปของคุณ และส่งคำสั่ง ESC/POS ดิบไปยังเครื่องพิมพ์


WebSocket Bridge ทำงานอย่างไร

Your Web App  →  WebSocket (ws://localhost:8765)  →  Print Agent  →  Thermal Printer

ขั้นตอนง่ายๆ:

  1. เว็บแอปของคุณเปิดการเชื่อมต่อ WebSocket ไปยัง ws://localhost:8765
  2. คุณส่ง JSON payload ที่อธิบายใบเสร็จ
  3. Print Agent แปลงเป็น ESC/POS และส่งไปยังเครื่องพิมพ์
  4. เครื่องพิมพ์พิมพ์แบบเงียบ — ไม่มีกล่องโต้ตอบ ไม่มีการโต้ตอบกับไดรเวอร์

MenuForma Print Agent นำสะพานนี้ไปใช้งาน มันฟรี เป็นโอเพนซอร์ส และทำงานบน Mac, Windows และ Linux


การอ้างอิง API

การเชื่อมต่อ

const ws = new WebSocket('ws://localhost:8765');

ws.onopen = () => {
  console.log('Print Agent connected');
};

ws.onerror = () => {
  console.log('Print Agent not running');
};

ตรวจสอบสถานะ Agent

ส่ง ping เพื่อตรวจสอบว่า Agent กำลังทำงานอยู่:

ws.send(JSON.stringify({ type: 'ping' }));

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === 'pong') {
    console.log('Agent version:', data.version);
  }
};

พิมพ์ใบเสร็จ

const receipt = {
  type: 'print',
  printer: 'auto',   // or specific IP like '192.168.1.100'
  data: {
    storeName: 'My Restaurant',
    storeAddress: '123 Main St',
    orderNumber: '#1042',
    tableNumber: 'Table 5',
    items: [
      { name: 'Margherita Pizza', qty: 1, price: 14.90 },
      { name: 'Sparkling Water', qty: 2, price: 3.50 },
    ],
    subtotal: 21.90,
    tax: 1.97,
    total: 23.87,
    paymentMethod: 'Card',
    footer: 'Thank you for dining with us!',
  }
};

ws.send(JSON.stringify(receipt));

ตัวอย่างการผสานรวม 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); // retry
      };
      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 };
}

การผสานรวมกับ Shopify

สำหรับร้านค้า Shopify คุณสามารถเรียกใช้การพิมพ์จาก webhook ของคำสั่งซื้อ หรือจากส่วนขยายของเบราว์เซอร์:

// ในหน้าร้านค้า Shopify หรือส่วนขยายผู้ดูแลระบบของคุณ
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,
      }
    }));
  };
}

การกำหนดค่าเครื่องพิมพ์

ฟิลด์ "printer" ในเพย์โหลดการพิมพ์ยอมรับค่าดังนี้:

ค่า คำอธิบาย
"auto" ใช้เครื่องพิมพ์ที่กำหนดค่าไว้ในการตั้งค่า Print Agent
"192.168.1.100" เชื่อมต่อโดยตรงกับเครื่องพิมพ์เครือข่ายด้วย IP
"USB" ใช้เครื่องพิมพ์ USB ที่ตรวจพบเครื่องแรก

การจัดการข้อผิดพลาด

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);
    // แสดง UI สำรองหรือลองใหม่
  }
};

การตรวจจับว่ามีการติดตั้ง Print Agent หรือไม่

ก่อนที่จะแสดงฟังก์ชันการพิมพ์ใน UI ของคุณ ให้ตรวจสอบว่าเอเจนต์กำลังทำงานอยู่หรือไม่:

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);
    };
  });
}

// การใช้งาน
const agentAvailable = await isPrintAgentAvailable();
if (!agentAvailable) {
  // แสดงข้อความแจ้งให้ดาวน์โหลด
  window.open('/print-agent', '_blank');
}

ดาวน์โหลด Print Agent

MenuForma Print Agent นั้นฟรีและพร้อมใช้งานสำหรับ Mac, Windows และ Linux รองรับเครื่องพิมพ์เทอร์มอลแบบ USB, เครือข่าย และ Bluetooth และใช้งาน WebSocket API เต็มรูปแบบตามที่อธิบายไว้ในคู่มือนี้

สำหรับเอกสารประกอบ API ฉบับเต็มและซอร์สโค้ดโอเพนซอร์ส โปรดเยี่ยมชม หน้า Print Agent

บทความที่เกี่ยวข้อง

ผลิตภัณฑ์ MenuForma