(文章)

有獎活動平台的帳務、防刷與預算控管

有獎活動的獎勵要當帳處理:每筆有唯一編號、寫入前先鎖會員;防刷不靠叫新用戶等時間;預算寫進後台檢查,超過就存不進去。

磨人開發團隊・2026-10-23

目錄

有獎活動送出去的是成本,不是流量。點數可以兌換,名次可以領現金,抽獎券可以換實物。一旦多發、重發,或被少數人大量領走,出問題的是帳,不是畫面。磨人開發團隊在活動平台的建置與維運上,把每一筆獎勵當成帳來處理。

同一類活動的其餘做法,各有專文:

獎勵能換成成本,錯誤就是帳務問題

活動平台常見的損失有三種,都發生在帳上。第一種是同一筆獎勵發了兩次,多半來自網路重送或排程重跑。第二種是同一位會員在同一瞬間送出兩次請求,餘額被扣成負數,或同一張券被兌換兩次。第三種是少數人用大量帳號領走名次獎,真正的參與者什麼也拿不到。

一人戴著眼鏡,手持指示棒站在畫架旁,架上的長條圖有四根由低到高的長條。

這三種損失有共同點:測試時不會出現,上線後流量一大才出現,事後也很難說清楚是誰多拿了什麼。所以對策不在前端多加提示,而在後端:獎勵怎麼進出要有規則,每一筆都能對帳。以下做法來自開發團隊在有真實成本的活動平台上的實作。

三條帳務規則:唯一編號、先鎖會員、固定加鎖順序

有現金成本的獎勵,開發團隊定了三條規則,寫在共用的帳務程式裡。活動功能一律呼叫這段程式入帳,不能繞過去直接寫點數表。

每一筆都有唯一編號,重送不會重發

每一筆入帳都帶一個唯一編號。同一個編號第二次進來,帳務程式回覆已處理,不再發放。編號要由事件本身組成,例如哪個活動、哪一期、哪位會員,不能每次隨機產生。否則排程重跑時,同一筆獎勵會拿到新編號,被當成新的一筆。這個要求有學理依據:應用層要做到冪等,接收端必須記住已處理過的訊息,因為訊息多採至少一次送達,重送與重複處理是常見情況(Helland,2007)。該文作者註明這是個人立場的論述。

支付服務的做法相同。Stripe 的 API 文件說明,客戶端帶冪等鍵重試,不會因連線錯誤而重複建立物件或重複更新,伺服器也會比對參數,與第一次不一致時回報錯誤(Stripe 文件,Idempotent requests)。文件另註明 API v2 的重試行為不同,這裡只引用冪等鍵的基本概念。IETF HTTPAPI 工作小組另有一份 Idempotency-Key 標頭的草案,讓 POST 這類請求能安全重送(Jena 等,2026)。這份文件仍是工作中的草案,不是 RFC。草案本身並未以金流為例,用同一把鍵避免獎勵重複入帳,是本文的應用。

寫入前先鎖住會員,同時送出的請求排隊

同一位會員在同一瞬間送出兩次請求,是連點與多裝置的日常。如果兩個請求都先讀餘額、再各自扣款,兩次都會通過檢查,餘額就變負。做法是在同一個交易內、寫入帳務之前,先鎖住該會員的資料列。後到的請求排隊,等前一個完成後,才讀到最新的餘額。兌換類的功能都以這條路為標準寫法。

牽涉兩人的交易,固定照同一個順序加鎖

邀請、轉贈這類交易同時動到兩位會員。甲轉給乙、乙同時轉給甲,各自先鎖自己再等對方,兩邊就會互相等待而死鎖。規則是不論誰發起,一律依會員編號由小到大加鎖。對 InnoDB 死鎖的建議是同一個方向:涉及多組列時,每次以相同順序執行操作,讓交易有序排隊,並把這些操作寫在應用程式函式或預存程序中(MySQL 8.0 手冊,2026 年版次)。

鎖負責排隊,唯一鍵負責兜底。例如一筆「已讀」紀錄若用 INSERT … SELECT … NOT EXISTS 一次寫入,可能與另一條會寫同一列資料的流程互等間隙鎖而死鎖。較穩的寫法是逐筆 INSERT,撞到主鍵就略過,遇到死鎖就再試一次。唯一鍵在這裡是最後一道防線,不是第一道。

兩人站在輸送帶兩側,帶上排著幾個黃色蓋子的容器,兩人各伸手處理一個。

規則只寫在文件裡不夠。開發團隊用靜態掃描的檢查腳本,找出繞過共用帳務程式的寫法。對照現有程式的檢查,常見的缺口有三類:兌換流程沒有加鎖,同一位會員連點可能把餘額扣成負數;排行榜獎勵沒有防重複編號,排程同一天跑兩次可能重發;對帳補發沒有檢查是否已領過。券兌換可被重複兌換這類問題,修法是整段包進交易、鎖住會員列,並在帳務流水加上防重複編號。

圖 1獎勵服務只能經由共用帳務程式入帳,不能直接寫點數表

獎勵服務

  • 兌換與抽獎呼叫帳務程式,不碰點數表
  • 名次結算與補發排程執行,每筆帶由事件組成的編號
獎勵請求與唯一編號入帳結果:成功、重複或拒絕

共用帳務程式

  • 唯一編號檢查重複的編號回覆已處理,不再發放
  • 會員鎖與加鎖順序先鎖會員,兩人交易依編號由小到大
  • 統一的調整函式所有入帳與扣款都經過這一處
同一交易內的寫入最新餘額

資料庫

  • 會員資料列在交易內被鎖住
  • 帳務流水每筆流水帶防重複編號

不跨過邊界點數表的寫入權限只留在共用帳務程式內,獎勵服務不能直接寫入。檢查腳本會掃描繞過的寫法。

範圍:有現金成本的點數、券與名次獎

防刷不該叫新用戶等時間

一人開多個帳號,是有獎活動最老的問題。Douceur 針對沒有中央權威的分散式系統論證,單一實體可以偽造大量身分,進而控制系統的大部分(Douceur,2002)。活動平台有中央的帳號系統,情境不同,但問題的形狀一樣:帳號的數量不等於人的數量,獎勵若按帳號發,少數人就能領走大部分。

最容易想到的擋法是「帳號註冊滿幾天才算數」。這個門檻簡單,代價是擋到所有真正的新用戶,而且擋在新用戶最需要被留住的第一天。開發團隊不用時間門檻當主要手段,時間門檻最多只留在新用戶本來就碰不到的功能上,天數也不超過約三天。改用四個不傷新用戶的做法:

  1. 每一對好友的轉移設上限。成對帳號之間能互相轉移的獎勵有固定額度,用分身互送也送不出規模。
  2. 同一裝置、同一網路的帳號合併計算。登入後由前端產生匿名的裝置代碼,存在瀏覽器本機,不做指紋辨識,隨每個請求送出。後端記下每個帳號最近的網路位址與裝置代碼,保留約三個月,共用任一個的帳號併成一群。
  3. 高價獎勵延遲入帳。名次獎先記成待入帳,預設約一天後才正式發放,期間後台可以追回。
  4. 異常偵測加人工複核。同一群帳號只發名次最高的那一個,其餘轉為待審核,由營運人員判斷。

名次獎另有人數門檻:榜上人數低於門檻時,前幾名只發沒有成本的外觀獎勵,門檻預設為獎項數的三倍。人數太少的榜,很容易被少數帳號包下。所有閾值都是後台參數,包括轉移上限、延遲時間與人數門檻,各有上下限護欄,每次調整都留下稽核軌跡。誰設的門檻、什麼時候改、改成多少,都查得到。

做法擋的是什麼對新用戶的影響
註冊滿一定天數才算數剛註冊的分身新用戶在第一天什麼都領不到
每一對好友的轉移上限成對帳號互相轉移獎勵影響很小,只限制成對的大量轉移
同裝置、同網路合併計算一人多帳號新用戶照常使用,只在發獎時併成一群計算
高價獎勵延遲入帳領走後才被發現的異常新用戶照常使用,高價獎勵晚約一天到帳
異常帳號人工複核漏網的成群帳號正常帳號不受影響,被標記的帳號晚一步領獎

有條件的獎勵,條件要讓參與者看得到。待入帳、人工複核與轉移上限,都應寫進活動辦法。公平交易委員會的網路廣告處理原則要求,促銷或抽獎類的網路廣告須正確描述活動規則,並充分揭示附帶的條件、義務或限制,不得以版面編排讓消費者不易察覺(公平交易委員會,2023)。這是行政機關的處理原則,不是法律條文。活動辦法的寫法應由主辦方的法務確認。

圖 2每一筆獎勵請求,最後只會落在入帳、待確認或拒絕三種結果之一
  1. 檢查唯一編號編號已處理過,回覆重複,不再發放
  2. 鎖住會員同一位會員的請求排隊
  3. 套用規則轉移上限、同裝置合併、預算與餘額
  • 規則通過,且獎勵不屬高價
    直接入帳寫入帳務流水
  • 獎勵屬高價,或帳號落在異常群組
    先記為待確認延遲後入帳,或交營運人員複核;期間後台可追回
  • 超過上限、編號重複,或餘額不足
    拒絕回覆原因,不寫入帳

預算寫成驗證器,超過就存不進去

有現金成本的獎項,營運人員會在後台調整數值。如果預算只靠營運人員記得,填錯一個數字,超支要到結算才被發現。開發團隊把預算寫成程式裡的檢查:獎項單價乘上預估人數,超過預算,後台就不讓存。結算前再驗一次。

獎項表另有兩條驗證。名次區間必須連續,不能漏掉任何名次。不同獎項要折成同一個計價單位,才能比較與加總;現金、點數與實物沒有共同單位,總成本就沒有辦法檢查。

價值錨點也要寫成規則。同一張抽獎券,以兌換價換算與以大獎預算換算,價值可能差到數倍。開發團隊的規定是:門檻一律用大獎預算除以預計投入的張數重算,不沿用舊值。取捨是預算由輸入端擋住,超出預算的設定一律存不進去,要加獎項就得先調預算。這個摩擦是刻意保留的。

活動的狀態是產品的一部分,每個狀態都要有畫面與開關

活動型平台的狀態分為預熱、開放、結束、封存。遊戲化活動有明確的起訖,結束後還要保留成果頁,開發團隊的遊戲化活動實作案例就屬於這一類,該活動從規劃、設計到開發完成不到 2 個月。只設計開放狀態,等於把結束後的畫面留給意外。

開發團隊從第一天就把每個狀態做成畫面與開關。預熱期間整站是倒數頁:首頁回應正常內容,分享預覽卡與搜尋引擎拿到的是完整頁面,其餘頁面回 503。倒數頁不載入 3D 的程式碼,3D 的效能做法見網頁 3D 效能工程。結束後,排行榜與成果仍可查看,抽獎等功能自動關閉。封存則是關閉活動與排行榜計算的開關。前端建置時會呼叫後端的設定介面,所以上線順序固定為後端先、前端後。

圖 3每個狀態都由前台畫面、後台開關與營運動作三方同步切換
預熱
開放
結束
封存
前台
後台與設定
營運
1
前台倒數頁首頁仍可分享與被搜尋,其餘頁面回 503
2
後台與設定設定開展時間由執行期變數控制,到點才放行
3
營運核對獎項與預算獎項表通過預算驗證後才開放
4
前台活動頁與排行榜獎勵一律經共用帳務程式入帳
5
後台與設定調整後台參數每個閾值有護欄,改動留下稽核軌跡
6
營運看儀表板、做複核處理待審核帳號與待確認獎勵
7
前台成果頁與排行榜仍可查看,抽獎等功能關閉
8
後台與設定關閉會動帳的功能結算前再驗一次預算
9
營運結算與追回待確認的獎勵在期限內可追回
10
後台與設定關閉活動與排行榜計算
11
營運保留帳務流水供日後對帳

狀態:預熱、開放、結束、封存

數字要可信:瀏覽量靠行為,同時在線靠心跳

瀏覽量只在真的換頁時才算

活動期間,主辦方看的是人潮與成效,數字怎麼算比數字本身重要。常見的瀏覽量去重,是同一人 2 秒內只算一次,這是用時間在猜。開發團隊的做法是讓前端記住目前頁面的身分,只在身分改變時送出一筆。換到另一個頁面算一次。同一頁換分頁、關掉彈窗回到原頁,都不送。直接用網址進站,進站那一筆就是那一頁,不重送。沒有自己網址的彈窗送另一種事件,算畫面打開,不算瀏覽量。瀏覽量的條件只寫一份,所有查詢共用,所以各頁加總剛好等於總瀏覽量。

追蹤上線前的時段,用各頁的打開事件估計,以第一筆精確事件為分界。圖上估計的格子畫斜線,說明欄寫明分界時間,不與精確資料混為一談。

同時在線用心跳計算

瀏覽器在前景時約每 30 秒回報一次,初始化與切回前景那一刻各補一次,背景不送。所以切到背景、鎖螢幕、關分頁,短時間內就不再計入。登入的會員按人算,同一人兩台裝置算一位;沒登入的訪客按裝置算。同時在線最高的那一格,會標出是哪一分鐘。心跳上線前的時段,用一段時間內有操作的人數估計,結果偏低,同樣畫斜線。

和 GA4 差在哪要寫清楚

兩套數字對不齊是常態,要先分清口徑。在這類專案的設定裡,GA4 只計入同意統計的訪客,數字偏低,也接不到會員帳號;自建追蹤涵蓋全部訪客,登入後可以歸戶到會員。GA4 的工作階段數本身也是估計值:Google 的說明頁註明,報表的工作階段數是依不重複工作階段 ID 估計的近似值,與 BigQuery 精確計算會有小幅差異(Google Analytics 說明,About Analytics sessions)。所以每個指標旁都附完整算法,並寫明和 GA4 差在哪。

前後端各有一份的名單,用腳本守住

追蹤事件的白名單,前端和後端各有一份,靠記得同步一定會漏。開發團隊寫一支腳本逐字比對,兩邊不一致、或找不到對方時直接失敗,不會悄悄略過。檢查本身也會過期:事件欄位增加之後,沒有跟著改的斷言會讓檢查一直失敗,所以腳本要定期對照改動檢查。

不適合這樣做的情況

  • 只辦一次性的小型抽獎。現成的抽獎工具就夠,不需要自建帳務。
  • 獎項沒有真實成本,例如只發虛擬徽章。重發與多領的代價很低,三條帳務規則可以簡化。
  • 不願意保留任何延遲入帳,只接受即時發放。拿掉延遲入帳與人工複核之後,防刷只剩門檻和事後追查:前者會傷到新用戶,後者追不回已經發出的獎勵。

各項做法的實際效果取決於活動規模與對手,閾值是否合適,要在上線後靠儀表板觀察與調整。

下一步:先備齊四項資料

  • 獎項清單與每一項的預算
  • 預估參與人數
  • 活動期間
  • 是否需要會員登入

備齊之後,可以聯絡開發團隊,說明活動內容與預計規模。評估開發廠商本身,另見挑選軟體開發廠商的方法。

(聯絡)

Start a project.

三個步驟,取得可判斷的做法與預算區間。

  1. 01預約第一場會議:選擇時段,不需要完整規格。
  2. 02線上諮詢 15 至 30 分鐘:取得做法方向、預算區間與時程草案。
  3. 03檢視素材後,約 3 至 5 個工作天提供報價單暨交付規格明細表。
信箱
hello@polish-design.com.tw
通訊地址
(104) 台北市中山區植福路191-1號3F

預約第一場會議

週一至週五 16:00–18:00線上 30 分鐘

先選時段,再用 Google 帳號或信箱確認,預約完成可加入行事曆。

選擇時段

或先送出資料

還沒想好時段,可以先留下專案內容,專人以信箱回覆。送出後也能接著預約。

補充資料(選填):服務項目、目前階段、現有連結、得知管道

資料僅用於本次專案評估。

ISO/IEC 27001:2022 證書

(Certificate)

ISO/IEC 27001:2022

驗證機構
ARES International Certification
驗證範圍
互動沉浸式 3D 系統之開發流程,及網路與基礎設施之維運與管理。

到驗證機構網站查證