กรณีศึกษาที่ 1: หน้า Landing Page หลายภาษา + ระบบชำระเงิน
ไอเดีย
เพื่อนคนหนึ่งขายคู่มือ PDF ราคา 29 ดอลลาร์ให้กับชาวต่างชาติที่ย้ายมาอยู่เกาหลี เธอต้องการหน้าเว็บเพจเดียวที่โหลดเร็วทั่วโลก พูดได้ทั้งภาษาอังกฤษ เกาหลี และจีน และรับชำระเงินด้วยบัตรได้โดยที่เธอไม่ต้องแตะต้องเซิร์ฟเวอร์เลย
สเปก
เราเขียนสเปกให้อยู่ในหนึ่งย่อหน้าก่อนจะเขียนโค้ดสักบรรทัด:
Build a single-page marketing site for a $29 digital product.
Three languages (en/ko/zh), auto-detected from the browser but
switchable with a toggle. One "Buy" button that opens Stripe
Checkout and redirects to a thank-you page on success. No backend
server I have to maintain. Must load fast from anywhere.
สังเกตสิ่งที่สเปกนี้ ไม่ได้ พูดถึง ไม่มีเฟรมเวิร์ก ไม่มีดีไซน์ระบบ ไม่มีระบบวิเคราะห์ข้อมูล ไม่มีการเก็บอีเมล แต่ละสิ่งเหล่านั้นล้วนเป็นฟีเจอร์ที่สมเหตุสมผลได้ แต่ทุกอันก็จะทำให้การเปิดตัวครั้งแรกช้าลงเช่นกัน วินัยของสเปกหนึ่งย่อหน้าส่วนใหญ่แล้วก็คือวินัยของการ "ตัดออก" นั่นเอง
สแต็กที่เลือกใช้
"ไม่มีแบ็กเอนด์ที่ต้องดูแลเอง" บวกกับ "เร็วทั่วโลก" ชี้ตรงไปที่การโฮสต์แบบสแตติกบน CDN (Content Delivery Network — เครือข่ายกระจายเนื้อหา คือสำเนาไฟล์ของคุณที่เก็บไว้ตามศูนย์ข้อมูลทั่วโลก เพื่อให้ผู้เข้าชมแต่ละคนโหลดจากศูนย์ที่ใกล้ที่สุด) พร้อมฟังก์ชัน เล็ก ๆ หนึ่งตัวสำหรับสิ่งเดียวที่ ต้องการ ความลับ นั่นคือการสร้าง Stripe เราเลือกเว็บไซต์สแตติกบน Cloudflare Pages พร้อม Pages Function เดียว สถาปัตยกรรมทั้งหมดอธิบายได้ในหนึ่งประโยค คือหน้าเว็บสแตติกที่เรียกใช้ฟังก์ชันเดียวที่คุยกับ Stripe และความเรียบง่ายนี่แหละที่ทำให้ดีบักได้ง่ายในภายหลัง
ทั้งระบบมีสามกล่อง และมีเพียงกล่องตรงกลางเท่านั้นที่แตะต้องความลับ:
┌─────────────┐ POST /api/checkout ┌──────────────┐ secret key ┌────────┐
│ STATIC PAGE │─────────────────────▶│ PAGES │─────────────▶│ STRIPE │
│ (HTML/JS) │◀─────────────────────┤ FUNCTION │◀─────────────┤ │
│ en/ko/zh │ checkout URL │ (the only │ session URL └────────┘
└─────────────┘ │ secret-side)│
served from CDN edge └──────────────┘
พรอมต์สำคัญ
เราเริ่มแบบกว้าง ๆ แล้วให้ AI เสนอโครงสร้างเอง:
Scaffold a static landing page (plain HTML/CSS/JS, no framework)
with a language toggle for en/ko/zh. Store the copy in a single
JS object keyed by language. Detect the default from
navigator.language. Keep it to one index.html plus one main.js.
จากนั้นเป็นเส้นทางการชำระเงิน ซึ่งเป็นจุดที่ความลับอาศัยอยู่:
Add a Cloudflare Pages Function at /api/checkout that creates a
Stripe Checkout session for a single $29 product and returns the
redirect URL. Read STRIPE_SECRET_KEY from the environment, never
hardcode it. The Buy button should POST to this function and then
window.location to the returned URL.
วลี "never hardcode it" (อย่าฝังค่าตายตัวลงในโค้ด) นั้นคุ้มค่าที่จะพูดออกไปตรง ๆ ถ้าปล่อยให้เป็นไปตามค่าเริ่มต้นของมันเอง AI จะเผลอใส่คีย์ตัวอย่างลงในโค้ดโดยตรงเพื่อให้ตัวอย่าง "ใช้งานได้" และคีย์ตัวอย่างก็มีวิธีกลายเป็นคีย์จริงที่หลุดออกไปพร้อมกับการเปิดตัว การพูดออกมาให้ชัดเจนในพรอมต์เพียงครั้งเดียว ถูกกว่าการมาจับผิดตอนรีวิวโค้ดทีหลังมาก
อุปสรรค
การทดสอบใช้งานจริงครั้งแรกล้มเหลว ปุ่ม Buy ไม่ทำอะไรเลย และคอนโซลของเบราว์เซอร์แสดงข้อผิดพลาด CORS (Cross-Origin Resource Sharing — กฎของเบราว์เซอร์ว่าเว็บไซต์ไหนได้รับอนุญาตให้เรียกเว็บไซต์ไหนได้บ้าง) เราไม่ได้เดา แต่วางข้อความ error ตัวจริงกลับไปให้ AI ดู:
Clicking Buy logs: "Access to fetch at '/api/checkout' blocked
by CORS policy." The function and page are on the same domain.
Here's the function code: [pasted]. What's actually wrong?
AI จับจุดได้ทันที ฟังก์ชันกำลังส่ง Stripe กลับมาเป็น (JavaScript Object Notation — รูปแบบข้อความง่าย ๆ ที่อ่านได้ ซึ่งโปรแกรมใช้แลกเปลี่ยนข้อมูลแบบมีโครงสร้างกัน) แต่ตัว fetch ถูกเรียกก่อนที่ preflight แบบ OPTIONS ของฟังก์ชันจะถูกจัดการ เพราะเราดันดีพลอยหน้าเว็บสแตติกกับฟังก์ชันไปคนละโปรเจกต์ Pages โดยไม่ตั้งใจ ทางแก้ที่แท้จริงคือเรื่องโครงสร้างการดีพลอย ไม่ใช่โค้ด เราย้ายฟังก์ชันเข้าไปอยู่ในโฟลเดอร์ /functions ของโปรเจกต์เดียวกัน แล้ว error ก็หายไป
บทเรียนตรงนี้ควรค่าแก่การครุ่นคิด อาการ ที่เห็นอยู่ในโค้ด (error CORS จาก fetch) แต่ สาเหตุ อยู่ที่โครงสร้างพื้นฐาน (สองโปรเจกต์แทนที่จะเป็นหนึ่ง) ถ้าเราปล่อยให้ AI "แก้ error CORS" โดยไม่วางบริบทการตั้งค่าให้ดู มันคงจะแปะเฮดเดอร์ Access-Control-Allow-Origin เข้าไปแทน ซึ่งเป็นการแก้ที่คอมไพล์ผ่านแต่กลบอาการไว้เฉย ๆ ทิ้งบั๊กตัวจริงไว้เหมือนเดิม วางข้อความ error ตัวจริง พร้อมกับ บริบทรอบข้าง และอย่ารับคำอธิบายแรกที่ฟังดูสมเหตุสมผลถ้ามันไม่ตรงกับสถานการณ์จริงของคุณ
การเปิดตัว
เราเพิ่มคีย์ Stripe ตัวจริงเป็นตัวแปรสภาพแวดล้อมแบบเข้ารหัสในแดชบอร์ดของ Pages (ไม่เคยอยู่ใน repo) ชี้ ( Name System สมุดที่อยู่ของอินเทอร์เน็ตที่แปลงชื่อโดเมนให้กลายเป็นเซิร์ฟเวอร์) ของโดเมนเธอไปที่ Pages และทำการซื้อจริงหนึ่งครั้งด้วยบัตรจริงมูลค่า 29 ดอลลาร์ แล้วคืนเงินทีหลัง เรายังลองคลิกผ่านเส้นทางที่ล้มเหลวโดยตั้งใจ ด้วยบัตรทดสอบที่ถูกปฏิเสธ เพื่อยืนยันว่าเธอไม่ถูกเรียกเก็บเงิน และเห็นข้อความที่สมเหตุสมผลแทนที่จะเป็นหน้าจอว่างเปล่า ขึ้นใช้งานจริงได้ภายในบ่ายวันเดียว เธอขายได้ครั้งแรกในเย็นวันนั้นเอง