# Private Order Page

> 私人訂購頁產生器。收集品牌素材（名稱、LOGO、配色、商品、價格、寄送方式、法律條款、身份角色、專業折扣、付款階段、匯款資料、LINE）後，產出一套完整的私人訂購頁：動態密碼閘門（可關閉）、商品多圖輪播與分色選購、課程日曆預約、身份折扣、運費試算、名片上傳、強制閱讀條款、多階段付款、HTML 排版訂單通知信＋Google 試算表訂單總表，並指導部署到 Cloudflare Workers 與自訂網域。凡使用者提到「展場預購網站」「快閃訂購頁」「幫某品牌做預購/訂購表單網站」「限定販售頁」「展會收單」「私人邀請預購」「私人訂購頁」，或想為品牌建立含動態密碼、名片上傳、訂金分期的活動網站時，都應使用本技能——即使對方沒有明說要「網站」，只要情境是限量／限期／受邀才能買，就適用。

- Skill: `clouluo/private-order-page` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add clouluo/private-order-page`
- Raw SKILL.md: https://api.skillmd.com/api/skills/clouluo/private-order-page/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- License: MIT
- Author: clouluo (https://skillmd.com/u/clouluo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/clouluo/private-order-page

---


# 私人訂購頁產生器

為任何品牌複製一套經實戰驗證的預購系統。
架構固定、內容全配置：Cloudflare Worker 承載「動態密碼閘門＋訂購頁＋名片與圖示 KV 儲存」，
Google Apps Script 負責「寫入訂單總表＋寄 HTML 通知信」。每月成本 $0。

實戰來源：曾實際用於精品品牌展場活動與工作室的「私人邀請・限量預購」場景。

## 這套系統能做什麼

決定要不要用這個技能時，先看功能對不對得上：

- **動態密碼閘門**——每 30 秒更新的 6 位數，現場人員報給客人才進得去。
  未通過閘門連 HTML 原始碼都拿不到，網址單獨外流沒有用
- **商品卡**：一項多張圖左右滑動輪播、分色選購（每色可獨立定價）、固定顏色、數量加減
- **課程／體驗**：日曆選場次，可設每場名額；`makeSessions(n)` 能自動排未來 n 個月的週末場次
- **身份折扣**：專業身份（設計師、企業採購等）上傳名片，全站價格自動切換成折扣價
- **寄送**：宅配／超商兩種運費，材積超限的商品自動鎖住超商選項；不需寄送的品項不問地址
- **多階段付款**：百分比自動試算，最後一段吃四捨五入尾差
- **強制閱讀條款**：捲到底才能勾同意
- **收單雙保險**：HTML 排版通知信（公司收件＋客人副本）＋ Google 試算表總表

## 工作流程總覽

1. **素材訪談** → 讀 `references/intake.md`，逐項收集（缺必要項不要硬做，先問）
2. **產出網站檔案包** → 本文件〈產出步驟〉
3. **部署與驗收** → 讀 `references/deployment.md`（含歷史教訓，部署前必讀）
4. **交接** → 產出品牌專屬交接說明

第 2 步可完全離線完成；若使用者只要檔案包（自行部署），做完第 2 步即可交付。

## 產出步驟

工作目錄建議：`<品牌代號>-preorder/`。

### 1. 客製訂購頁：assets/template.html → site.html

模板所有 `{{…}}` 都是待替換欄位，全部換掉才算完成。主要區塊：

| 位置 | 改什麼 |
|---|---|
| `:root` CSS 變數 | 品牌配色（--bg 背景、--ink 文字、--accent 輔色、--sale 強調價色等）|
| `<link rel="icon">` | 指向 `/fav/light`、`/fav/dark`、`/fav/touch`，圖檔另外上傳 KV（見部署文件）|
| hero 區 | LOGO 圖片網址、英文標語、主標題、副標兩行、三個賣點 |
| `const SHIPPING` | 宅配／超商的名稱、運費、說明 |
| `const PAY_STAGES` | 付款階段：`{name, pct, note, short, highlight, remainder}`，pct 總和 100，最後一段設 remainder:true 吃尾差 |
| `const ROLES` | 身份角色：`{name, pro}`，pro=true 須填公司＋名片 |
| `HINT_PRO / HINT_BASIC / HINT_NONE` | 身份提示文案 |
| `const PRO_RATE` | 專業身份折數（0.9＝9 折；不打折就設 1，並把 HINT 文案改掉）|
| `makeSessions()` | 課程場次自動排程規則（時段組合、每月開幾天）|
| `const PRODUCTS` | 商品陣列，模板內有三種型態的示範，照著填 |
| terms-box | 法律條款全文（分節 `<p>`，粗體小標）|
| 匯款資料（成功頁 `dl.bank`）＋ `hidden("匯款資訊")` | 戶名、銀行代碼、帳號 |
| LINE 按鈕與線上刷卡連結 | 官方帳號 ID＋加好友連結、金流收款連結 |
| 訂單編號前綴 | 預設兩碼英文，例如 `RY-260802-1234` |
| footer | 公司名、統編、信箱、LINE |

**有專業折扣時，條款要加一條**：名片查核不符者改以一般價計算並通知補差額，
買方不同意可於三日內取消全額退款。頁面承諾了折扣，條款就要接得住。

改完必做（自動驗收）：
```
node scripts/check_site.js site.html
```
會檢查：JS 語法、`{{…}}` 佔位是否清空、舊品牌字樣殘留、角色／階段／商品設定完整性、
付款百分比總和＝100、各種總額下的金額加總一致性、條款鎖定與送單驗證機制是否完好。
未通過就修到通過再繼續——這些都是實際踩過的坑。

### 2. 建置 Worker：assets/build_worker.py

複製到工作目錄，改頂部 CONFIG（SITE_HTML_PATH、GATE_ENABLED、品牌名、
閘門文案、LOGO 網址、閘門配色、`SESSION_HOURS`），執行後產出 worker.js。
`SESSION_HOURS` 是訪談時問到的放行時效，不要沿用範例預設值。
`GATE_ENABLED: False` 時網站直接公開、名片上傳不驗 session，其餘功能不變。
產出後跑 `node --input-type=module --check < worker.js`。

Worker 內建 `/fav/<key>` 路由，從 KV 讀網站圖示。圖示不要 base64 內嵌進 HTML——
會讓 worker.js 肥好幾十 KB，改版重傳很慢。

### 3. 收單程式：assets/apps-script-template.gs

改頂部 CONFIG：試算表 ID、收件信箱、品牌名、寄件顯示名、HEADERS、預設狀態。
HEADERS 的付款欄位命名必須是「階段名＋pct＋%」（與網頁 hidden 欄位一致），
例如 PAY_STAGES 有「訂金50%」就要有欄位 `訂金50%`。

**HEADERS 與網頁送出的欄位名要逐一對齊**。交付前用這個檢查：
把 site.html 裡所有 `name="…"` 與 `hidden("…")` 收集起來，比對 HEADERS，
兩邊都不該有對方沒有的欄位（`時間`、`處理狀態` 除外，那兩欄由程式自己填）。

`sendOrderMail_()` 產生 HTML 排版的通知信，分區塊呈現，不是一長串純文字。
要調整分區就改 `MAIL_SECTIONS`。
`formatSheet()` 設定試算表版面（欄寬、自動換行、狀態下拉、隔行底色），
建表時自動執行，也可手動跑一次套用到既有的表。

### 4. 交付前最終驗收

```
node scripts/check_site.js site.html          # 頁面 40 項檢查
node --input-type=module --check < worker.js  # Worker 語法
python3 -c "import ast;ast.parse(open('build_worker.py',encoding='utf8').read())"
```
全部通過才交付。剩下的 ⚠️ 警告（條款待補、圖片未接圖床）要明確告知使用者哪些還沒補齊。

### 5. 金鑰與 QR：scripts/gen_keys.py

`python3 gen_keys.py <品牌> <網址> <輸出目錄> <QR色碼>`（無閘門版跳過驗證器 QR，仍要入口 QR）。
**網址之後若有變更，記得重產入口 QR。**

### 6. 品牌交接說明

寫 `專案交接說明.md`：網址與帳號表、架構圖、現場怎麼用、改東西的三步驟、
常用設定對照表、上線前待補項目、緊急操作（換金鑰＝全場踢人、刪 Worker＝下架）、歷史教訓。
若使用者打算長期接案，再加一節「未來要幫新客戶開一個站」的步驟。

## 設計原則（為什麼這樣做）

- **內容伺服器端保護**：訂購頁 HTML 內嵌在 Worker，未通過閘門連原始碼都拿不到——
  這是「防外流」的實質保證，不只是擋 UI。
- **確認成功才顯示成功**：送單讀到後端 `ok` 才給成功頁。第三方出事時客人看到的是明確錯誤
  而非假成功，訂單不會無聲消失。
- **只依賴 Cloudflare＋Google**：寄信一律 Apps Script MailApp。不要用 FormSubmit 等免費表單
  轉信服務（曾發生過三次事故，詳見 deployment.md 歷史教訓）。
- **雙保險收單**：信箱（單筆通知＋名片連結）＋試算表（總覽對帳），一邊漏了另一邊還在。
- **金額只算一次**：總額＝商品＋運費，付款階段從總額拆。置底總計列、成功頁、訂單信、
  試算表必須是同一個數字——分開算過一次，結果少收運費。
- **狀態要誠實反映**：沒選商品時，置底總計列不寫「已選 0 項，合計 NT$ 0」——那是一排沒有意義的零。
  改成「尚未選擇商品」，金額淡化、送出鍵降對比，選了東西才亮起來。使用者一眼知道現在能不能送。
- **主要按鈕用描邊而非實心**：深色底上一整塊白會像促銷 banner。細框透明底、hover 才填滿，
  同樣醒目但不吵，和限量、受邀的調性比較合。
- **失敗要留退路，但重試必須是安全的**：送單失敗先自動重試一次；仍失敗就給訂單編號、
  請對方別重送。但**前端重試一定要搭配後端去重**——寄信慢會拖住回應，
  前端誤判成失敗而重試，同一筆訂單就會寫進試算表兩次（實際發生過）。
  `doPost` 先上鎖，再用訂單編號查是否已寫過，寫過就回 `ok-dup` 不重複寫也不重複寄信。
- **承諾與實作要對齊**：頁面上寫的（折扣、時限、運費）就是系統實際會做的事。
  不確定就先問使用者，不要先寫個好聽的文案再說。
- **會出錯的欄位要在送出前擋**：地址少了郵遞區號或門牌號碼就是寄不到，
  超商只填「711台北濟新」工作人員也分不出是哪一家。這類錯誤事後補問很花時間，
  而且客人通常已經匯完款。能用選的就不要讓對方自由填寫，必須填寫的就檢查完整性，
  錯誤訊息要明講「缺什麼」並附一個正確範例，不要只說「格式錯誤」。
- **送出要快，慢的部分先做掉**：Apps Script 容器閒置後會被回收，下次請求得先冷啟動。
  實測同一支什麼都不做的 `doGet`，冷的 24～49 秒、熱的 1.7 秒——差距不在你的程式碼。
  作法是客人**一進站就在背景打一次無副作用的 GET**（`mode:"no-cors"`）把容器叫醒，
  填表時再視互動補打（節流 60 秒）。等他按送出，容器已經熱了。
  同理，`autoResizeRows()` 這種純外觀的操作不要放在客人等待的路徑上。
- **送不出去要看得出為什麼**：表單設 `novalidate`，改由 `validateOrder()` 一次收集
  所有問題，再用置底的錯誤總覽一次列完，並捲到第一個有問題的欄位、加深該欄框線。
  瀏覽器原生的驗證泡泡一次只講一個欄位、講得又籠統（「請填寫這個欄位」），
  客人按了沒反應會直接放棄。錯誤總覽平常完全隱藏，按下送出才出現，版面才不會一開始就髒。

## 交付物清單

`site.html`（原始碼）、`worker.js`（部署產物）、`apps-script.gs`（已填品牌 CONFIG）、
`build_worker.py`（已填 CONFIG，供日後改版重建）、`TOTP_SECRET.txt`＋兩張 QR、
`專案交接說明.md`。全部打包成 zip 用 present_files 交給使用者保存。

## 關於作者

**羅巧雲 Clou Luo**｜入雲視覺設計工作室 負責人／創意總監

台北的平面設計工作室，做品牌識別與視覺設計。客戶專案橫跨品牌、包裝、網站與社群，同時是設計教育工作者與獎項評審。

這套系統是接案途中被客戶的難題逼出來的產物。現成方案不是沒有，但預算和時程對不上，只好自己來，最後成了一套能重複使用的工具。

- 網站：clouluo.com
- Instagram：@clouluo ／ 工作室 @clou.dy
- 合作洽詢：hi@clouluo.com

使用這套技能產出的網站與程式碼，內容與資料屬於使用者本人；歡迎自由修改與商業使用。

