← 所有文章

工程 · 2026.09.30 · 3 min · 更新於 2026.10.26

SaaS 從績效抽成改成年繳訂閱:方案上限、促銷與停權的帳單設計

自動放貸 SaaS 的收費從績效抽成改成固定價訂閱。方案上限比收費先落地,促銷加送在開票當下凍結在帳單上,30 天通知只擋被動收費,停權不碰使用者自己的放貸開關。

閱讀設定
主題
字級
寬度

Bot 幫你多賺多少,就從裡面抽一部分,這是績效抽成的想法。我的 Bitfinex 自動放貸 SaaS 把這種收費改成了固定價的訂閱制。

訂閱帳單有幾個主要的設計決定。方案上限比收費先落地;促銷加送的月數在開票當下凍結在帳單上,之後改規則不會回頭改寫已成立的權益。

「收費前 30 天通知」只擋使用者沒有主動參與的收費路徑;停權交給引擎排除,不去動使用者自己的放貸開關。

價格和方案細節這篇不放。

績效抽成的計費問題

績效抽成的想法很直覺,我先做了一版只量不收的 shadow ledger,只記帳、不真的收錢。

問題是績效費要精準量出 Bot 的貢獻、還沒平倉的收益和實際入帳的金額,計費語意很複雜,帳面上一點誤差就會影響收費。

改成訂閱制也不是換個價格就好。方案上限、帳單狀態機、付款地址監控、到期提醒、管理端的修正、對使用者的通知,全部都要有。

方案上限先落地,再收費

訂閱的資料模型和方案上限先落地,之後才開始收費,避免先收了錢、再回頭補限制。

方案上限後來還修過一次。

同一個使用者可以有多個放貸設定,原本各自檢查上限,等於上限被放大好幾倍。現在同一輪的多個設定共用同一份額度。

示意圖:修正前同一個使用者的多個放貸設定各自檢查方案上限,加起來上限被放大好幾倍;現在同一輪的設定共用一份額度,加起來不超過方案上限。 示意圖,長條長度不代表實際額度。

付款走帳單加上 movements watcher(movements 是 Bitfinex 帳戶的入出金紀錄)。watcher 直接對接 Bitfinex 的真實資料,讓收款地址、入金事件和帳單狀態可以互相對帳。

自建這段的好處是少依賴一個外部付款服務,代價是對帳的責任落在自己身上。

付款頁、通知和後台操作一起上,使用者和營運端看到的是同一套狀態。

促銷:加送凍結在帳單上

年繳促銷做成資料模型和規則,不寫死在前端文案裡。

最重要的決定是,加送的月數在開票當下就凍結在帳單的 period_end 裡。

之後活動規則怎麼改,都不會回頭改寫已經成立的帳單權益。

示意圖:開票當下,方案期間加上促銷加送的月數就寫進帳單的 period_end;之後活動規則改了,已成立帳單的 period_end 不回頭改寫。 示意圖,長度不代表實際的方案或加送月數。

其他幾條規則:

  • 符合多個活動可以疊加,但同一個人對同一個活動只能兌換一次。
  • 有兌換紀錄的活動不能刪除,保留帳務的稽核鏈。
  • 帳單逾期作廢時,也會釋放這張帳單用掉的活動兌換。
  • 活動只套在使用者自己按的結帳,不套自動續訂,避免長期的收費規則被一個舊活動綁住。

權限也分開。管理活動要能管理費用的權限,只讀取的話,能看使用者的權限就夠,營運查詢和價格控制不混在一起。

30 天通知只擋被動收費

開始收費前要先通知 30 天。這道 gate 管的是使用者沒有主動參與的路徑:自動開續訂帳單,以及試用到期。

使用者自己按結帳,刻意不受這個 gate 限制。

主動購買不是被動開始收費。

停權不碰使用者的開關

停權原本是直接把使用者的放貸開關關掉。

問題是使用者的意圖和帳務資格混在同一個旗標裡,停權的人自己按恢復,就能繼續放貸。

現在改成引擎挑設定時直接排除停權的使用者,放貸開關完全不動。付款恢復之後,使用者自己按過的暫停照樣保留。

流程對照:修正前使用者的暫停、恢復和停權都寫進同一個放貸開關,停權的人自己按恢復就能繼續放貸;現在放貸開關只記使用者的意圖,停權記在帳務狀態,引擎挑設定時排除停權的使用者,付款恢復後使用者自己按的暫停照樣保留。

使用者的意圖和系統的帳務狀態要分開存,停權這種系統動作,不要去改使用者自己設定的值。

#SaaS#系統設計#訂閱制

留言

載入留言中…

下一篇

工程

Java 搬到 Rust 後的 code review:15 條 finding,6 條是從 Java 照搬的 bug

後端從 Java 換成 Rust 之後做了一次完整 code review。這篇挑四條最有教訓價值的:照註解搬錯的交易語意、sqlx 對失敗交易回 Ok、client 斷線砍掉 handler,以及停權跟放貸開關共用旗標。