false, 'error' => 'Printer IP is not configured.']; } // Determine if IP is local/private. // Cloud servers cannot reach local network IPs (e.g., 192.168.x.x). // If it's a local IP, we return immediately to avoid the 1-second fsockopen timeout. $isLocalIp = preg_match('/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/', $ip); if ($isLocalIp) { return ['success' => false, 'error' => "Cannot connect to local network IP $ip from cloud. Please use a public IP or a local print proxy."]; } $timeout = 5; // 5 seconds for public IPs try { $fp = @fsockopen($ip, $port, $errno, $errstr, $timeout); if (!$fp) { return ['success' => false, 'error' => "Could not connect to printer at $ip:$port. Error: $errstr ($errno)"]; } fwrite($fp, $data); fclose($fp); return ['success' => true]; } catch (Exception $e) { return ['success' => false, 'error' => 'Exception: ' . $e->getMessage()]; } } /** * Generate basic ESC/POS receipt content. */ public static function formatReceipt($order, $items, $company) { $esc = "\x1b"; $gs = "\x1d"; $line = str_repeat("-", 32) . "\n"; $out = ""; $out .= $esc . "@"; // Initialize printer $out .= $esc . "!" . "\x38"; // Double height and width $out .= $esc . "a" . "\x01"; // Center align $out .= ($company['company_name'] ?? 'Restaurant') . "\n"; $out .= $esc . "!" . "\x00"; // Reset $out .= ($company['address'] ?? '') . "\n"; $out .= ($company['phone'] ?? '') . "\n\n"; $out .= $esc . "a" . "\x00"; // Left align $out .= "Order ID: #" . $order['id'] . "\n"; $out .= "Date: " . ($order['created_at'] ?? date('Y-m-d H:i:s')) . "\n"; if (!empty($order['customer_name'])) { $out .= "Customer: " . $order['customer_name'] . "\n"; } if (!empty($order['customer_phone'])) { $out .= "Phone: " . $order['customer_phone'] . "\n"; } if (!empty($order['car_plate'])) { $out .= "Car Plate: " . $order['car_plate'] . "\n"; } $out .= $line; foreach ($items as $item) { $name = substr(($item['product_name'] ?? $item['name'] ?? 'Item'), 0, 20); $qty = ($item['quantity'] ?? 1) . "x"; $price = number_format((float)($item['unit_price'] ?? $item['price'] ?? 0), 2); $out .= sprintf("% -20s %3s %7s\n", $name, $qty, $price); if (!empty($item['variant_name'])) { $out .= " (" . $item['variant_name'] . ")\n"; } } $out .= $line; $out .= sprintf("% -20s %11s\n", "TOTAL", number_format((float)($order['total_amount'] ?? 0), 2)); $out .= "\n\n\n\n"; $out .= $esc . "m"; // Cut paper return $out; } }