Phayoune API Forms
📘 คู่มือการใช้งาน: Phayoune API Forms (V31)
🚀 1. ปลั๊กอินนี้ทำอะไรได้บ้าง? (Key Features)
นี่คือระบบฟอร์มเชื่อมต่อ Google Sheet ที่ครบเครื่องที่สุดที่เราพัฒนาร่วมกันครับ:
-
เชื่อมต่อ Google Sheet อัตโนมัติ: สร้าง Sheet ใหม่ให้เองตามชื่อที่ตั้ง ไม่ต้องสร้างล่วงหน้า
-
อัปโหลดไฟล์ (File Upload): ลูกค้าแนบสลิป/รูปภาพได้ ไฟล์จะถูกเก็บใน Google Drive และสร้างลิงก์ลง Sheet
-
แจ้งเตือนอีเมล (Auto Email): ส่งเมลหาลูกค้าทันทีพร้อม แนบไฟล์จริง (Attachment) ไปในเมล
-
ระบบตอบกลับ (Admin Reply): กดปุ่ม "📧 ตอบ" ในหน้า Admin เพื่อส่งเมลตอบกลับหาลูกค้าได้ทันที
-
ความปลอดภัย (Security): มีระบบ API Key ป้องกันคนแอบใช้ URL ของเรายิงข้อมูลขยะเข้ามา
-
ตรวจสอบสถานะจริง (Real-Check): เช็คว่าข้อมูลถึง Google จริงหรือไม่ ถ้า URL ผิดจะแจ้งเตือนทันที
-
Auto Scan: หน้า Admin มีปุ่มสแกนหาฟอร์มในเว็บให้อัตโนมัติ ไม่ต้องคอยจำ URL
-
Design: ปรับแต่งปุ่มกดได้ละเอียด (สี, เงา, เส้นขอบ, ความมน) ผ่าน Elementor
🛠️ วิธีติดตั้ง (Installation)
STEP 1: ฝั่ง Google (Back-end)
-
ไปที่ Google Apps Script สร้างโปรเจกต์ใหม่
-
นำโค้ด Google Apps Script นี้ ไปวาง
// Google Apps Script V27 (Final) var SECURITY_KEY = "PhayouneKey"; // <--- ตรวจสอบรหัสตรงนี้ function doGet(e) { if (e.parameter.key !== SECURITY_KEY) return ContentService.createTextOutput(""); var ss = SpreadsheetApp.getActiveSpreadsheet(); var sendJSON = function(d) { return ContentService.createTextOutput(JSON.stringify(d)).setMimeType(ContentService.MimeType.JSON); }; if (e.parameter.action == 'get_sheets') { var sheets = ss.getSheets(); var names = []; for (var i=0; i<sheets.length; i++) names.push(sheets[i].getName()); return sendJSON({status: "success", sheets: names}); } var sheetName = e.parameter.sheet || "Sheet1"; var sheet = ss.getSheetByName(sheetName); if (!sheet) return sendJSON([]); if (e.parameter.action == 'delete') { var row = parseInt(e.parameter.row); if (row > 1) { try { sheet.deleteRow(row); return sendJSON({status: "success"}); } catch (err) { return sendJSON({status: "error", message: err.toString()}); } } } var data = sheet.getDataRange().getValues(); var result = []; if (data.length > 1) { var headers = data[0]; for (var i = 1; i < data.length; i++) { var rowObj = { '_row_index': i + 1 }; for (var j = 0; j < headers.length; j++) rowObj[headers[j]] = data[i][j]; result.push(rowObj); } } return sendJSON(result); } function doPost(e) { if (e.parameter.key !== SECURITY_KEY) return ContentService.createTextOutput(""); var ss = SpreadsheetApp.getActiveSpreadsheet(); // *** ส่วนสำคัญ: คำสั่ง Reply ต้องมีตรงนี้ *** if (e.parameter.action == 'reply') { try { if(e.parameter.to && e.parameter.subject && e.parameter.message) { GmailApp.sendEmail(e.parameter.to, e.parameter.subject, e.parameter.message, { name: e.parameter.sender_name || "Admin" }); return ContentService.createTextOutput("Sent"); } return ContentService.createTextOutput("Error: Missing Data"); } catch(err) { return ContentService.createTextOutput("Error: " + err); } } // --- SAVE --- var sheetName = e.parameter.sheet || "Sheet1"; var sheet = ss.getSheetByName(sheetName); if (!sheet) { try { sheet = ss.insertSheet(sheetName); var h = ["Timestamp"]; var ignore = ['action','sheet','key','file_base64','file_name','file_mime','config_email_field','config_sender_name','config_subject','reply_to','reply_subject','reply_message']; for (var k in e.parameter) { if (!ignore.includes(k)) h.push(k); } sheet.appendRow(h); } catch (err) { return ContentService.createTextOutput("Error Create Sheet"); } } if (e.parameter.action !== 'save') return ContentService.createTextOutput("Error Action"); // <--- นี่คือจุดที่แจ้ง Error ถ้าไม่เจอคำสั่ง reply ข้างบน var fileUrl = ""; var fileBlob = null; if (e.parameter.file_base64 && e.parameter.file_name) { try { var folderName = "Phayoune Uploads"; var folders = DriveApp.getFoldersByName(folderName); var folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(folderName); var decoded = Utilities.base64Decode(e.parameter.file_base64); var mime = e.parameter.file_mime || MimeType.JPEG; fileBlob = Utilities.newBlob(decoded, mime, e.parameter.file_name); var file = folder.createFile(fileBlob); file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); fileUrl = file.getUrl(); } catch(fErr) { fileUrl = "Error Upload"; } } var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; var nextRow = sheet.getLastRow() + 1; var newRow = []; var emailField = e.parameter.config_email_field || "email"; var customerEmail = ""; for (var i = 0; i < headers.length; i++) { var key = headers[i]; var val = e.parameter[key] || ""; if (key === emailField || key.toLowerCase() === 'email') customerEmail = val; if (key === 'Timestamp') newRow.push(new Date()); else { if (fileUrl && (key === e.parameter.file_field_name || key.toLowerCase().includes('file') || key.toLowerCase().includes('slip'))) newRow.push(fileUrl); else newRow.push(val); } } sheet.getRange(nextRow, 1, 1, newRow.length).setValues([newRow]); if (customerEmail && customerEmail.includes("@")) { try { var senderName = e.parameter.config_sender_name || "Phayoune Form"; var subject = e.parameter.config_subject || "ยืนยันการรับข้อมูล"; var body = "เรียนคุณลูกค้า,\n\nได้รับข้อมูลเรียบร้อยแล้ว\n\nรายละเอียด:\n"; for (var i = 0; i < headers.length; i++) { var hKey = headers[i]; if(hKey !== 'Timestamp' && !hKey.includes('file_')) { var dVal = e.parameter[hKey] || "-"; if (fileUrl && (hKey === e.parameter.file_field_name || hKey.toLowerCase().includes('file'))) dVal = "see attach image"; body += "• " + hKey + ": " + dVal + "\n"; } } body += "\nขอบคุณครับ\n" + senderName; var mailOptions = { name: senderName }; if (fileBlob) mailOptions.attachments = [fileBlob]; GmailApp.sendEmail(customerEmail, subject, body, mailOptions); } catch (e) { } } return ContentService.createTextOutput("Saved"); } function askPermission() { // ฟังก์ชันนี้มีไว้กด Run เพื่อขอสิทธิ์เท่านั้น (ไม่ได้ใช้งานจริง) // 1. ขอสิทธิ์อีเมล GmailApp.getInboxThreads(0, 1); MailApp.getRemainingDailyQuota(); // 2. ขอสิทธิ์ Google Drive (อัปโหลดรูป) DriveApp.getRootFolder(); // 3. ขอสิทธิ์ Spreadsheet SpreadsheetApp.getActiveSpreadsheet(); } -
สำคัญ: แก้ไขบรรทัดที่ 2
var SECURITY_KEY = "PhayouneKey";ให้เป็นรหัสลับที่คุณต้องการ (จำรหัสนี้ไว้นะครับ) -
กดปุ่ม Run (เรียกใช้) 1 ครั้ง เพื่อกดอนุญาตสิทธิ์ (Authorize) ให้เข้าถึง Gmail/Drive/Sheet
-
การ Deploy (สำคัญมาก):
-
กดปุ่ม Deploy (ทำให้ใช้งานได้) > Manage deployments (จัดการ...)
-
กดรูปดินสอ ✏️
-
ช่อง Version เลือก "New version" (เวอร์ชันใหม่) ทุกครั้งที่มีการแก้โค้ด
-
ช่อง Who has access เลือก "Anyone" (ทุกคน)
-
กด Deploy แล้วคัดลอก Web app URL เก็บไว้
-
STEP 2: ฝั่ง WordPress (Front-end)
-
สร้างโฟลเดอร์ชื่อ
phayoune-api-formsในwp-content/plugins/ -
สร้างไฟล์
index.phpแล้ววางโค้ด Plugin V31 ลงไป -
ไปที่หน้า Admin WordPress > Plugins > กด Activate ปลั๊กอิน "Phayoune API Forms (V31 Final Integrity)"
🎮 วิธีใช้งาน (Usage Guide)
1. การสร้างฟอร์มด้วย Elementor
-
เปิดหน้าเว็บที่ต้องการแก้ไขด้วย Elementor
-
ค้นหา Widget ชื่อ "Phayoune Form (V31)" แล้วลากวาง
-
ตั้งค่า Tab 1: Fields (ข้อมูล)
-
เพิ่ม/ลบ ช่องกรอกข้อมูลตามต้องการ
-
เคล็ดลับ: ช่องที่เป็นไฟล์อัปโหลด ให้เลือก Type เป็น File Upload
-
-
ตั้งค่า Tab 2: Email (อีเมล)
-
กำหนดชื่อผู้ส่ง และหัวข้ออีเมลที่จะส่งหาลูกค้า
-
-
ตั้งค่า Tab 3: Connection (การเชื่อมต่อ)
-
Script URL: วาง URL ที่ได้จาก STEP 1
-
Security Key: ใส่รหัสให้ตรงกับใน Google Apps Script (เช่น
PhayouneKey) -
Sheet Name: ตั้งชื่อ Sheet ที่ต้องการเก็บข้อมูล (เช่น
OrderForm2026)
-
-
ตั้งค่า Tab Style:
-
ปรับสีปุ่ม, ขนาด, เส้นขอบ (Border), และเงา (Box Shadow) ให้สวยงาม
-
2. การดูข้อมูลและตอบกลับ (Admin Panel)
-
ไปที่เมนู "Phayoune Data" ในหน้า Admin WordPress
-
การดึงข้อมูล:
-
วิธีง่าย: กดปุ่ม "🚀 สแกนหาฟอร์ม" ระบบจะไปดึง URL และ Key จากหน้าเว็บมาให้เอง แล้วเลือกจาก Dropdown ได้เลย
-
วิธีกรอกเอง: ใส่ URL, Key, Sheet Name แล้วกด "ดูข้อมูล"
-
-
ตารางแสดงผล:
-
ถ้ามีรูปภาพ จะแสดงเป็นรูปย่อ (Thumbnail)
-
คอลัมน์ขยะทางเทคนิคจะถูกซ่อนไว้ อัตโนมัติ
-
-
การตอบกลับลูกค้า:
-
กดปุ่ม "📧 ตอบ" (ปุ่มจะขึ้นเฉพาะแถวที่มีอีเมล)
-
พิมพ์ข้อความใน Popup แล้วกดส่ง ระบบจะส่งเมลผ่าน Gmail ของคุณทันที
-
⚠️ ข้อควรระวัง (Troubleshooting)
-
Error Action: แปลว่า Google Apps Script ยังเป็นเวอร์ชันเก่า -> ให้ไปกด Deploy เป็น New version
-
Connection Failed: แปลว่า URL ผิด หรือใส่ Security Key ไม่ตรงกัน
-
ไม่เห็นรูปในตาราง: ตรวจสอบว่าใน Widget ตั้งชื่อ Field (Name Eng) ว่า
file,slip,imageหรือไม่ (ระบบจะจับคำพวกนี้เพื่อแสดงรูป) - ไม่ได้รับอีเมล์ ให้รัน ฟังชั่น
askPermission ใน App script คลิก อนุญาตทั้งหมด แล้วลองดูใหม่



