# มาตรฐานคุณภาพเว็บและขอบเขต commerce สำหรับหลักสูตร

> สืบค้นและตรวจสอบเมื่อ 9 กันยายน 2026 — เอกสารนี้เป็นฐานอ้างอิงของบทเรียน 04–07 ไม่ใช่ข้อกำหนดกฎหมายหรือคำแนะนำ PCI DSS

## เป้าหมายคุณภาพร่วมกัน

ตัวอย่างทั้งสามใช้รูปแบบเดียวกัน: static UI อยู่ที่ `/examples/company/`, `/examples/rental/`, และ `/examples/store/`; Cloudflare Worker รับเฉพาะ `/api/*`; D1 เก็บข้อมูลที่ผ่านการตรวจสอบแล้ว; ไฟล์อื่นส่งต่อไป static assets. การ deploy แบบ Worker Static Assets ทำให้ asset และ Worker ถูก deploy เป็นหน่วยเดียว และ Worker เรียก `env.ASSETS.fetch(request)` เพื่อคืนไฟล์ได้ ([Cloudflare Static Assets](https://developers.cloudflare.com/workers/static-assets/)). ตั้ง `run_worker_first: ["/api/*"]` เพื่อให้ API เข้าสู่ Worker ก่อน asset ตามตัวอย่าง binding ของ Cloudflare ([Configuration and Bindings](https://developers.cloudflare.com/workers/static-assets/binding/)).

ทุก lab ต้องยอมรับเกณฑ์ต่อไปนี้ก่อนส่งขึ้น production:

| ด้าน | เกณฑ์ที่ตรวจได้ | วิธีตรวจในบทเรียน |
| --- | --- | --- |
| การเข้าถึง | ใช้คีย์บอร์ดได้, focus มองเห็น, label/error ของ form ชัด, alt ที่มีความหมาย | Tab/Shift+Tab, เปิด DevTools accessibility tree, ทดลอง reader-friendly copy |
| pointer | เป้าหมายสัมผัสอย่างน้อย 24×24 CSS px หรือมีข้อยกเว้นตาม WCAG | ตรวจ computed box ของปุ่ม/ไอคอน |
| motion | ใช้ `prefers-reduced-motion`; motion ที่ไม่จำเป็นหยุด/ลดได้ | เปิด Reduce Motion ของ OS แล้ว reload |
| ความเร็ว | field data ที่ p75: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1 | PageSpeed Insights/CrUX เมื่อมี traffic และ Lighthouse เป็น lab signal |
| server | ตรวจ schema, authorization และ business rule ที่ Worker; bind parameter ทุก SQL | `curl` payload ผิดรูปและทดสอบ duplicate/conflict |
| commerce | เบราว์เซอร์ไม่เป็นผู้ตัดสินราคา, stock, หรือ payment สำเร็จ | Worker คำนวณ demo quote ใหม่; เปลี่ยนราคาใน DevTools แล้วส่งซ้ำ |

WCAG 2.2 เป็น W3C Recommendation ตั้งแต่ 5 ตุลาคม 2023; เกณฑ์ใหม่ระดับ AA ที่เกี่ยวข้องโดยตรงคือ **Target Size (Minimum)** และ **Dragging Movements** ([W3C: WCAG 2.2](https://www.w3.org/TR/WCAG22/), [What’s new](https://www.w3.org/WAI/standards-guidelines/wcag/new-in-22/)). การลากจึงต้องมีปุ่ม/stepper หรือ field เป็นทางเลือก ไม่ใช่บังคับลากอย่างเดียว.

Core Web Vitals ใช้ข้อมูลผู้ใช้จริงที่ percentile 75 แยก mobile/desktop. LCP วัดเวลาจน image/text block ที่ใหญ่สุดใน viewport render และเกณฑ์ดีคือ ≤2.5 s; INP วัด responsiveness ของ interaction ตลอด visit และเกณฑ์ดี ≤200 ms; CLS วัดการเลื่อนของ layout และเกณฑ์ดี ≤0.1 ([LCP](https://web.dev/articles/optimize-lcp), [INP](https://web.dev/articles/optimize-inp), [CLS](https://web.dev/articles/optimize-cls)). FCP และ TTFB เป็น diagnostic metrics จึงอย่าเรียกว่า Core Web Vitals.

## วิธีใช้ AI/vibe coding โดยไม่ยกความรับผิดชอบให้ AI

Vibe coding มีประโยชน์เมื่อแบ่งงานให้ AI เป็นชิ้นที่ตรวจได้: ให้ออกแบบหน้าเดียว, เขียน test case, อธิบาย diff, หรือปรับ error state. ห้ามยอมรับโค้ดเพียงเพราะหน้าตาสวย. ผู้เรียนต้องระบุ acceptance criteria, อ่าน diff, run test, ทดสอบ form/keyboard/mobile และตรวจ secret ก่อน merge ทุกครั้ง. Prompt ที่ดีระบุ **ผู้ใช้และเป้าหมาย, ขอบเขตไฟล์, state/data contract, ข้อห้าม, acceptance checks**.

วงจรการทำงานในหลักสูตร:

1. เขียน brief และ UI states (loading, empty, error, success) ก่อนสร้าง code.
2. ขอ AI ให้เสนอแผนและรายชื่อไฟล์; ตัดงานให้เหลือหนึ่ง vertical slice.
3. ให้ AI สร้าง แล้วอ่าน diff เพื่อหา hard-coded secret, client-trusted price, invalid HTML และ dependency ที่ไม่ได้ขอ.
4. รัน build/typecheck/test และ manual checklist; ให้ AI แก้จาก output จริงเท่านั้น.
5. Deploy preview ก่อน production และทดสอบ URL จริง, `/api/*`, headers และ form error.

## ความปลอดภัยและข้อมูล

รับ input จาก URL, form, cookie และ API เป็น untrusted เสมอ. OWASP แนะนำให้ validate ตั้งแต่ต้นทางทั้ง **syntactic** (รูปแบบ date/email/enum) และ **semantic** (วันเริ่มก่อนวันจบ, จำนวนไม่เกิน stock, price อยู่ใน product catalog) ([Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)). Validation ไม่ได้แทนการป้องกัน SQL injection: D1 query ต้องใช้ prepared statement และ `.bind(...)`; OWASP ให้ parameterized query เป็นการป้องกันหลัก เพราะแยก SQL code ออกจาก data ([SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html), [D1 Worker Binding API](https://developers.cloudflare.com/d1/worker-api/)).

อย่าส่ง API secret, D1 credential, webhook secret หรือ private customer data ไป browser, git หรือ prompt. ใช้ Worker secret/environment binding และ log เฉพาะ request id กับ error category. สำหรับ form รับ inquiry ให้ rate limit/anti-abuse ตามบริบทก่อนเปิดรับ traffic จริง และตอบข้อความเดียวกันสำหรับกรณีที่ไม่ควรเปิดเผยข้อมูล.

## ขอบเขต demo และ payment ที่ปลอดภัย

**สิ่งที่ current demo ทำจริง:** `wrangler.jsonc` ตั้ง `DEMO_MODE=true`. `POST /api/contact`, `/api/rentals` และ `/api/quotes` ตรวจ input, Same-Origin, honeypot และ idempotency แล้วเขียน local D1 โดย redacts `name`/`email`/`message` ก่อน persist. Rental อ่าน equipment/price/stock จาก D1, สร้าง `reserved-demo` และใช้ `rental_days` trigger เพื่อนับ capacity; quote อ่าน product price จาก D1, สร้าง `quote-demo` และ `quote_items`. เมื่อ D1 binding พร้อม `/api/catalog` คืน catalog จาก D1 และต้องตอบข้อผิดพลาดเมื่อ binding ไม่มี. ใช้ข้อมูลสมมติเท่านั้น; demo ไม่มี payment, authentication, notification หรือ commercial fulfilment.

![แผนภาพขอบเขตอำนาจของใบเสนอราคา แสดง browser ส่งเฉพาะรหัสสินค้าและจำนวน แล้ว Worker อ่านราคาจาก D1 คำนวณยอดและบันทึก quote-demo](/diagrams/quote-authority.svg)

[เปิดแผนภาพ quote authority แบบเต็มหน้า](/diagrams/quote-authority.html) เพื่อใช้ trace trust boundary ก่อนอ่านรายละเอียด payment extension ด้านล่าง

Cart อยู่ใน browser เพื่อ UX แต่ Worker รับ `productId` และ `quantity` แล้วอ่าน product price จาก D1 เพื่อคำนวณ `totalPrice` integer satang และ snapshot `unit_price` ลง D1. หน้า “สร้างใบเสนอราคาสาธิตแล้ว” ไม่ใช่ใบเสนอราคา, order, payment หรือ stock commitment ทางธุรกิจ. ห้ามเก็บเลขบัตร/CVC หรือประกาศว่า checkout ใช้งานจริงจนกว่าจะออกแบบและเปิดใช้ provider เป็นงานใหม่.

**เมื่อเลือกทำ production ภายหลัง:** ออกแบบ source of truth ของ catalog, state quote/order/hold/cancel, staff authorization, privacy/retention, tax/stock และ fulfillment ก่อนนำแนวคิดไปใช้. สิ่งเหล่านี้เป็น conceptual extension ไม่ใช่ requirement ของ lab ปัจจุบัน.

ถ้าต่อ Stripe ภายหลัง ให้ Worker สร้าง Checkout Session บน server และส่ง URL กลับ browser; Checkout Sessions จัดการ state ของ checkout และมี Stripe-hosted/embedded UI ([Checkout Sessions](https://docs.stripe.com/payments/checkout-sessions), [prebuilt Checkout](https://docs.stripe.com/payments/checkout)). `success_url` เป็นเพียง navigation ของลูกค้า ไม่ใช่หลักฐาน fulfillment. Worker webhook ต้อง verify **raw event payload**, `Stripe-Signature` และ endpoint secret ก่อนเปลี่ยนสถานะ order; Stripe ระบุสามค่านี้เป็นส่วนของการตรวจสอบ ([Receive Stripe events](https://docs.stripe.com/webhooks)). ฝั่ง server ควร map internal order id ผ่าน metadata และทำ fulfillment แบบ idempotent จาก verified event.

## Source inventory

### Official platform and payment docs

- [Cloudflare Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/) — routing, asset binding และ caching; ตรวจวันที่ 2026-09-09.
- [Cloudflare Assets configuration and bindings](https://developers.cloudflare.com/workers/static-assets/binding/) — `run_worker_first` สำหรับ `/api/*`; ตรวจวันที่ 2026-09-09.
- [Cloudflare D1 Worker Binding API](https://developers.cloudflare.com/d1/worker-api/) — prepare/bind/run flow; ตรวจวันที่ 2026-09-09.
- [Stripe Checkout Sessions API](https://docs.stripe.com/payments/checkout-sessions) และ [Webhook verification](https://docs.stripe.com/webhooks) — boundary ระหว่าง browser/server/payment event; ตรวจวันที่ 2026-09-09.

### Web quality and accessibility

- [W3C WCAG 2.2](https://www.w3.org/TR/WCAG22/) และ [WAI WCAG overview](https://www.w3.org/WAI/standards-guidelines/wcag/) — standard และ quick reference.
- [web.dev LCP](https://web.dev/articles/optimize-lcp), [INP](https://web.dev/articles/optimize-inp), [CLS](https://web.dev/articles/optimize-cls) — current metric definitions/thresholds, last updated 2025.
- [MDN `prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/%40media/prefers-reduced-motion) — widely available since January 2020 and its accessibility purpose.

### Security

- [OWASP Input Validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html) and [OWASP SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) — validation and SQL defenses.

### Motion documentation

- [GSAP `context()`](https://gsap.com/docs/v3/GSAP/gsap.context%28%29/), [GSAP `matchMedia()`](https://gsap.com/docs/v3/GSAP/gsap.matchMedia%28%29/), [ScrollTrigger](https://gsap.com/docs/v3/Plugins/ScrollTrigger/) and [`ScrollTrigger.refresh()`](https://gsap.com/docs/v3/Plugins/ScrollTrigger/static.refresh%28%29/) — behavior verified against current v3 documentation on 2026-09-09.
