paymentModel = new Payment(); } /** * POST /api/v1/billing/checkout - Cashier Checkout & Payment Processing */ public function checkout(): void { $user = JwtAuthMiddleware::handle(); $input = json_decode(file_get_contents('php://input'), true) ?? $_POST; $validator = Validator::make($input, [ 'queue_id' => 'required|numeric', 'patient_id' => 'required|numeric', 'subtotal' => 'required|numeric', ]); if ($validator->fails()) { Response::error('ข้อมูลการคิดเงินไม่ถูกต้อง', 422, $validator->getErrors()); } $input['branch_id'] = $input['branch_id'] ?? $user['branch']; $input['cashier_id'] = $user['id']; $res = $this->paymentModel->processCheckout($input); AuditLogger::logPayment($res['payment_id'], $res['receipt_no'], (float)$res['net_amount']); Response::success('ชำระเงินและออกใบเสร็จรับเงินสำเร็จ', $res, 201); } /** * GET /api/v1/billing/print-ticket/{queueId} - Get Base64 ESC/POS Queue Ticket Buffer */ public function printTicket(int $queueId): void { JwtAuthMiddleware::handle(); $queue = (new Queue())->find($queueId); if (!$queue) { Response::error('ไม่พบข้อมูลคิวสำหรับพิมพ์ใบคิว', 404); } // ดึงชื่อสาขา ผู้ป่วย และหมอนวด $db = \App\Models\Model::getDB(); $stmt = $db->prepare("SELECT b.name_th as branch_name, CONCAT(p.first_name_th, ' ', p.last_name_th) as patient_name, s.name_th as service_name, CONCAT(u.first_name, ' ', u.last_name) as therapist_name, r.room_no FROM queues q JOIN branches b ON q.branch_id = b.id JOIN patients p ON q.patient_id = p.id JOIN services s ON q.service_id = s.id LEFT JOIN therapists t ON q.therapist_id = t.id LEFT JOIN users u ON t.user_id = u.id LEFT JOIN rooms r ON q.room_id = r.id WHERE q.id = :id"); $stmt->execute(['id' => $queueId]); $details = $stmt->fetch(); $ticketData = array_merge($queue, $details ?: []); $base64Buffer = EscPosPrinter::buildQueueTicket($ticketData); Response::success('คำสั่งเครื่องพิมพ์ความร้อน ESC/POS (Base64)', [ 'queue_id' => $queueId, 'queue_no' => $queue['queue_no'], 'printer_type' => 'ESC/POS Thermal 80mm/58mm', 'raw_base64_buffer' => $base64Buffer, 'webusb_ready' => true, ]); } }