File manager - Edit - /home/webapp69.cm.in.th/u69319090001/Shop 001/process_booking.php
Back
<?php /** * ไฟล์ประมวลผลการจองสินค้า (Backend Pre-order Processor) * รองรับการอัปโหลดรูปสลิป, ตรวจสอบช่วงเวลาเปิดจอง และประมวลผลสต็อก (Made-to-Order & Limit Stock) */ session_start(); require_once 'db.php'; // 1. ตรวจสอบสิทธิ์และ CSRF Token if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header("Location: index.php"); exit; } if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { $_SESSION['error_msg'] = "เซสชันหมดอายุหรือคำขอไม่ถูกต้อง (CSRF Token invalid)"; header("Location: index.php"); exit; } // 2. รับและทำความสะอาดข้อมูลฟอร์ม $customer_name = isset($_POST['customer_name']) ? trim(htmlspecialchars($_POST['customer_name'])) : ''; $customer_phone = isset($_POST['customer_phone']) ? trim(htmlspecialchars($_POST['customer_phone'])) : ''; $payment_method = isset($_POST['payment_method']) ? trim(htmlspecialchars($_POST['payment_method'])) : 'Bank Transfer'; $cart_items_json = isset($_POST['cart_items']) ? $_POST['cart_items'] : ''; $cart_items = []; if (!empty($cart_items_json)) { $cart_items = json_decode($cart_items_json, true); if (!is_array($cart_items)) { $cart_items = []; } } // ถ้าไม่มี cart_items ส่งมา ให้ลองดึงข้อมูลจาก single product (เพื่อรองรับการทำงานแบบเดิม หรือ Buy Now) if (empty($cart_items)) { $product_id = isset($_POST['product_id']) ? (int)$_POST['product_id'] : 0; $variant_id = isset($_POST['variant_id']) ? (int)$_POST['variant_id'] : 0; $quantity = isset($_POST['quantity']) ? (int)$_POST['quantity'] : 0; if ($product_id > 0 && $variant_id > 0 && $quantity > 0) { $cart_items[] = [ 'product_id' => $product_id, 'variant_id' => $variant_id, 'quantity' => $quantity ]; } } // 3. ตรวจสอบข้อมูลเบื้องต้น if (empty($customer_name) || empty($customer_phone) || empty($cart_items)) { $_SESSION['error_msg'] = "กรุณากรอกข้อมูลที่จำเป็นและเลือกสินค้าลงในรถเข็นก่อนทำรายการ"; header("Location: index.php"); exit; } if (!preg_match("/^[0-9]{9,10}$/", $customer_phone)) { $_SESSION['error_msg'] = "รูปแบบเบอร์โทรศัพท์ไม่ถูกต้อง กรุณากรอกเฉพาะตัวเลข 9-10 หลัก"; header("Location: index.php"); exit; } // 4. ตรวจสอบการอัปโหลดรูปภาพสลิปโอนเงิน if (!isset($_FILES['slip_image']) || $_FILES['slip_image']['error'] !== UPLOAD_ERR_OK) { $_SESSION['error_msg'] = "กรุณาแนบรูปภาพสลิปหลักฐานการโอนเงิน"; header("Location: index.php"); exit; } $file = $_FILES['slip_image']; $allowed_exts = ['jpg', 'jpeg', 'png', 'webp', 'gif']; $file_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); // ตรวจสอบนามสกุลไฟล์ if (!in_array($file_ext, $allowed_exts)) { $_SESSION['error_msg'] = "ประเภทไฟล์ไม่ถูกต้อง อนุญาตเฉพาะสลิปที่เป็นไฟล์รูปภาพ JPG, PNG, WEBP เท่านั้น"; header("Location: index.php"); exit; } // ตรวจสอบ mime type ถ้าฟังก์ชัน mime_content_type มีให้ใช้งาน if (function_exists('mime_content_type')) { $file_type = @mime_content_type($file['tmp_name']); $allowed_mimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif', 'application/octet-stream']; if ($file_type && !in_array($file_type, $allowed_mimes) && strpos($file_type, 'image/') !== 0) { $_SESSION['error_msg'] = "ประเภทไฟล์รูปภาพไม่ถูกต้อง"; header("Location: index.php"); exit; } } // จำกัดขนาดไฟล์ที่ 10MB $max_size = 10 * 1024 * 1024; if ($file['size'] > $max_size) { $_SESSION['error_msg'] = "ขนาดไฟล์ใหญ่เกินไป จำกัดขนาดไม่เกิน 10MB"; header("Location: index.php"); exit; } // สร้างโฟลเดอร์สำหรับเก็บรูปสลิป $upload_dir = __DIR__ . '/uploads/slips/'; if (!is_dir($upload_dir)) { @mkdir($upload_dir, 0777, true); @chmod($upload_dir, 0777); } // สุ่มชื่อไฟล์ใหม่เพื่อป้องกันชื่อซ้ำและเรื่องความปลอดภัย $file_name = 'slip_' . uniqid() . '_' . time() . '.' . $file_ext; $target_file = $upload_dir . $file_name; if (!move_uploaded_file($file['tmp_name'], $target_file)) { $_SESSION['error_msg'] = "ไม่สามารถบันทึกไฟล์สลิปได้ (โปรดตรวจสอบ Permission โฟลเดอร์ uploads/slips/)"; header("Location: index.php"); exit; } $slip_image_url = 'uploads/slips/' . $file_name; // 5. เข้าสู่ขั้นตอนการประมวลผลการจอง (Transaction + FOR UPDATE) try { $pdo->beginTransaction(); $grand_total = 0; $booking_details = []; foreach ($cart_items as $item) { $p_id = isset($item['product_id']) ? (int)$item['product_id'] : 0; $v_id = isset($item['variant_id']) ? (int)$item['variant_id'] : 0; $qty = isset($item['quantity']) ? (int)$item['quantity'] : 0; if ($p_id <= 0 || $v_id <= 0 || $qty <= 0) { throw new Exception("ข้อมูลสินค้าในรถเข็นไม่ถูกต้อง"); } // ดึงข้อมูลตัวเลือกและเวลาจองของสินค้า เพื่อเช็กช่วงเวลาเปิดจอง $sql = "SELECT pv.id, pv.product_id, pv.variant_name, pv.stock, pv.is_unlimited, p.name AS product_name, p.price, p.start_time, p.end_time FROM product_variants pv INNER JOIN products p ON pv.product_id = p.id WHERE pv.id = :variant_id AND pv.product_id = :product_id FOR UPDATE"; $stmt = $pdo->prepare($sql); $stmt->execute([ 'variant_id' => $v_id, 'product_id' => $p_id ]); $variant = $stmt->fetch(); if (!$variant) { throw new Exception("ไม่พบข้อมูลสินค้าหรือตัวเลือกที่ระบุ"); } // A. ตรวจสอบความถูกต้องของช่วงเวลาเปิดรับจองสินค้า $now = new DateTime(); $start_time = !empty($variant['start_time']) ? new DateTime($variant['start_time']) : null; $end_time = !empty($variant['end_time']) ? new DateTime($variant['end_time']) : null; if ($start_time && $now < $start_time) { throw new Exception("สินค้า '{$variant['product_name']}' ยังไม่เริ่มเปิดรับจอง (จะเปิดในวันที่ " . $start_time->format('d/m/Y H:i') . ")"); } if ($end_time && $now > $end_time) { throw new Exception("ขออภัย สินค้า '{$variant['product_name']}' ปิดรับจองล่วงหน้าไปแล้วเมื่อวันที่ " . $end_time->format('d/m/Y H:i')); } // B. ตรวจสอบและประมวลผลสต็อกสินค้า $is_unlimited = (int)$variant['is_unlimited'] === 1; $current_stock = (int)$variant['stock']; if (!$is_unlimited) { // หากไม่ใช่สินค้าแบบ Made-to-Order ต้องเช็กและตัดสต็อก if ($current_stock < $qty) { throw new Exception("ขออภัย สินค้า '{$variant['product_name']}' ตัวเลือก '{$variant['variant_name']}' มีจำนวนไม่เพียงพอ (เหลืออยู่ {$current_stock} ชิ้น)"); } $new_stock = $current_stock - $qty; // อัปเดตหักสต็อก $update_stmt = $pdo->prepare("UPDATE product_variants SET stock = :new_stock WHERE id = :variant_id"); $update_stmt->execute([ 'new_stock' => $new_stock, 'variant_id' => $v_id ]); } // C. คำนวณราคารวมและบันทึกการจอง $item_total_price = (float)$variant['price'] * $qty; $grand_total += $item_total_price; // บันทึกการจอง $user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null; $insert_sql = "INSERT INTO bookings (user_id, customer_name, customer_phone, product_id, variant_id, quantity, total_price, slip_image_url, payment_method, payment_status, status) VALUES (:user_id, :customer_name, :customer_phone, :product_id, :variant_id, :quantity, :total_price, :slip_image_url, :payment_method, 'Pending', 'Pending')"; $insert_stmt = $pdo->prepare($insert_sql); $insert_stmt->execute([ 'user_id' => $user_id, 'customer_name' => $customer_name, 'customer_phone' => $customer_phone, 'product_id' => $p_id, 'variant_id' => $v_id, 'quantity' => $qty, 'total_price' => $item_total_price, 'slip_image_url' => $slip_image_url, 'payment_method' => $payment_method ]); $booking_details[] = "{$variant['product_name']} [{$variant['variant_name']}] (x{$qty})"; } $pdo->commit(); $_SESSION['success_msg'] = "ส่งคำสั่งจองเรียบร้อย! เจ้าหน้าที่จะตรวจสอบสลิปการโอนเงินของคุณโดยเร็วที่สุด ข้อมูลการจอง: " . implode(', ', $booking_details) . " ราคารวมทั้งหมด ฿" . number_format($grand_total, 2); header("Location: index.php"); exit; } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } // หากจองล้มเหลว ให้ลบไฟล์รูปภาพสลิปที่เพิ่งอัปโหลดเพื่อไม่ให้ขยะรกเซิร์ฟเวอร์ if (file_exists($target_file)) { unlink($target_file); } $_SESSION['error_msg'] = "ไม่สามารถทำรายการจองได้: " . $e->getMessage(); header("Location: index.php"); exit; }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.4 |
proxy
|
phpinfo
|
Settings