作者:平白設計編輯部|發布與最後查證:2026-10-03|適用:台灣(中華民國)|技術資訊,不保證搜尋排名
網路慢的時候,使用者習慣連點結帳按鈕,後台就可能出現兩筆一模一樣的訂單或報名資料。這類重複請求問題在電商與表單系統很常見,卻常被忽略。本文整理前端送出鎖定、後端冪等檢查與上線後紀錄比對的排查流程,協助維護網站的人逐步找出漏洞並補強。內容僅供技術參考,不保證任何轉換率或排名結果。
文章重點
- 先用瀏覽器限速重現連點情境,確認是否送出兩次請求
- 前端送出後立刻停用按鈕,失敗時才恢復可點擊狀態
- 後端以冪等鍵擋下重複請求,確保同一鍵只建立一筆訂單
- 付款回呼與伺服器日誌都要抽樣比對,避免遺漏重複通知
辨識症狀:幾秒內出現重複訂單
最典型的症狀是同一位客人在幾秒內產生兩筆相同金額的訂單或報名紀錄。客服收到反映時,通常會附上兩組相鄰的訂單編號,時間戳記只差一到三秒。這種情況多半不是客人真的想買兩次,而是網路延遲讓他們以為沒按到。
要確認是不是連點造成的,第一步是在瀏覽器開發者工具把網路限速設為慢速 3G,然後在結帳頁面快速點擊送出按鈕兩次以上。觀察 Network 面板是否出現兩個相同的 POST 請求,若確實送出兩次,就代表前端缺乏防呆機制。
重現問題後,先別急著改程式碼,把測試步驟寫成文件,讓團隊其他人也能照做。這跟 改版後 favicon 路徑怎麼抽樣 的概念一樣,建立標準流程才能確保每次排查結果一致。

前端防線:送出鎖定與狀態管理
前端最直接的做法是按下送出後立刻將按鈕設為 disabled,同時把文字換成「處理中」或顯示載入動畫。這樣即使使用者狂點,瀏覽器也不會再觸發第二次 click 事件。記得要在請求成功或失敗的 finally 區塊裡判斷是否要恢復按鈕。
很多人只靠 CSS 把按鈕變灰或加上 pointer-events: none,但這只能擋滑鼠點擊,無法阻止鍵盤 Enter 或螢幕閱讀器觸發。真正可靠的方式是用 JavaScript 控制按鈕屬性,搭配一個布林值旗標記錄目前是否正在送出中。
如果表單有多個步驟,建議在進入最後一步時就生成一個暫存編號,跟著表單一起送出。這個編號後續會成為後端冪等檢查的關鍵。前端鎖定的細節可以參考 改版後 og:locale 怎麼抽樣 的排查精神,逐一驗證每個狀態。
{
"idempotency_key": "cart_abc123",
"order_id": null,
"status": "pending",
"created_at": "2026-10-03T10:00:00Z"
}
後端防線:冪等鍵與重複請求阻擋
前端鎖定只是第一道防線,真正的安全網在後端。當伺服器收到建立訂單的請求時,應該先檢查是否已經存在相同的冪等鍵(Idempotency Key)。這個鍵可以是前端傳來的暫存編號,或是由使用者 ID 加購物車雜湊組合而成。
具體做法是在資料庫建一張冪等紀錄表,欄位包含鍵值、對應訂單編號與建立時間。收到請求時先查這張表,若鍵值已存在就直接回傳原本的訂單資訊,不再重複建立。記得幫鍵值欄位加上唯一索引,避免併發寫入時出現競態條件。
冪等鍵的有效期限也要設定,通常保留二十四小時即可,過期後自動清除。這樣既能擋住短時間內的連點,又不會讓舊的暫存編號永遠佔用空間。後端邏輯寫好後,務必用自動化測試模擬併發請求,確認同一鍵只會產生一筆訂單。

付款回呼:金流重複通知的處理
除了使用者連點,金流服務商的付款回呼(Webhook)也可能因為網路重試而送出多次通知。如果你的系統每次收到回呼都建立一筆訂單或更新狀態,就會出現重複扣款或重複出貨的問題。這跟前端連點的成因不同,但結果一樣麻煩。
處理方式是從回呼的 payload 取出交易編號,在執行任何寫入動作前先查詢資料庫是否已有該編號的紀錄。若已存在且狀態為完成,就直接回傳 200 給金流商,告訴他們不需要再重試。千萬不要因為覺得通知重複就回傳錯誤代碼。
建議把所有收到的回呼原始資料完整寫進日誌表,包含時間戳記、交易編號與處理結果。日後排查時,這些紀錄能幫你快速釐清是金流商重試還是自家系統邏輯有誤。定期抽樣檢查這些日誌,是維持系統穩定的基本功夫。
上線驗證:訂單紀錄與日誌抽樣比對
程式碼部署上線後,不能假設問題就解決了。你需要定期從資料庫撈出訂單資料,用客戶信箱或購物車 ID 分組,找出時間間隔極短且金額完全相同的紀錄。這些就是疑似重複訂單,需要進一步比對伺服器存取日誌確認來源。
比對時重點看兩個欄位:請求的時間戳記與來源 IP。若兩筆訂單來自同一 IP 且相差不到兩秒,幾乎可以確定是連點造成。接著去查冪等紀錄表,確認該次請求是否有正確被攔截。若沒有,代表後端邏輯仍有漏洞需要修補。
抽樣頻率建議上線後第一週每天做一次,穩定後改為每週一次。這套方法跟 改版後 web manifest 怎麼抽樣 的節奏類似,都是透過規律檢查來及早發現異常,而不是等客訴才行動。
| 檢查項目 | 執行方式 | 預期結果 | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 前 | 端 | 按 | 鈕 | 鎖 | 定 | ||||||||||||||
| 點 | 擊 | 後 | 設 | 為 | d | i | s | a | b | l | e | d | 並 | 更 | 換 | 文 | 字 | ||
| 無 | 法 | 再 | 次 | 點 | 擊 | 直 | 到 | 請 | 求 | 結 | 束 | ||||||||
| 網 | 路 | 限 | 速 | 重 | 現 | ||||||||||||||
| 開 | 發 | 者 | 工 | 具 | 設 | 為 | S | l | o | w | 3 | G | 連 | 點 | |||||
| 確 | 認 | 只 | 送 | 出 | 一 | 個 | P | O | S | T | 請 | 求 | |||||||
| 後 | 端 | 冪 | 等 | 鍵 | 檢 | 查 | |||||||||||||
| 資 | 料 | 庫 | 查 | 詢 | 暫 | 存 | 編 | 號 | 是 | 否 | 存 | 在 | |||||||
| 同 | 一 | 鍵 | 值 | 只 | 建 | 立 | 一 | 筆 | 訂 | 單 | |||||||||
| 付 | 款 | 回 | 呼 | 去 | 重 | ||||||||||||||
| 比 | 對 | 金 | 流 | 交 | 易 | 編 | 號 | 是 | 否 | 已 | 處 | 理 | |||||||
| 重 | 複 | 通 | 知 | 直 | 接 | 回 | 傳 | 2 | 0 | 0 | 不 | 寫 | 入 | ||||||
| 日 | 誌 | 抽 | 樣 | 比 | 對 | ||||||||||||||
| 每 | 週 | 撈 | 取 | 短 | 時 | 間 | 內 | 相 | 同 | 金 | 額 | 訂 | 單 | ||||||
| 確 | 認 | 無 | 未 | 攔 | 截 | 的 | 重 | 複 | 請 | 求 |
可以先做
- 先在開發環境用限速工具重現連點情境
- 前端改用 disabled 屬性而非純 CSS 隱藏
- 後端加入冪等鍵查詢與唯一索引
- 上線後每週抽樣比對訂單與伺服器日誌
不要當成已完成
- 不要只靠 CSS pointer-events 擋點擊
- 不要假設金流回呼只會收到一次
- 不要在未確認冪等邏輯前直接上線
- 不要把排查流程只放在某個人腦中
常見問題
為什麼只用 CSS 隱藏按鈕不夠?
CSS 只能擋滑鼠點擊,無法阻止鍵盤 Enter 或輔助科技觸發事件。必須用 JavaScript 將按鈕設為 disabled 才能真正阻止重複送出。
冪等鍵應該由前端還是後端產生?
兩者皆可。前端可在進入結帳頁時取得暫存編號,後端也可依使用者與購物車內容生成雜湊值。重點是同一操作必須對應同一鍵值。
金流回呼重複通知會造成什麼影響?
若未做去重檢查,系統可能重複更新訂單狀態或觸發多次出貨流程。應在收到回呼時先查詢交易編號是否已處理過。
抽樣比對多久做一次比較合適?
剛上線或改版後建議每天抽樣,運行穩定後可改為每週一次。若發現異常比例上升,應立即提高檢查頻率並追溯原因。
重點摘要
- 前端送出後須立即停用按鈕並顯示處理中狀態
- 後端以冪等鍵確保同一請求只建立一筆訂單
- 金流回呼需檢查交易編號避免重複處理
- 上線後定期抽樣比對訂單紀錄與伺服器日誌
