🏗️ Wolves Play Game — 三層架構說明

這個站是一個 展示用的聚合大廳。這頁說明畫面背後的三層各自負責什麼、 為什麼要這樣切、以及錢是怎麼走的。

商戶門面 town 聚合層 hub 遊戲平台 game Go + Vanilla JS 展示為主 · 不接金流

💡 一句話

玩家只認得一個網站(大廳),遊戲只認得一個上游(聚合層), 中間那層負責把「商戶的世界」翻譯成「遊戲的世界」。

玩家在大廳點一張卡,背後自動完成「建帳號 → 把餘額轉進那款遊戲 → 取一次性進場券 → 開遊戲」; 按返回時再自動把餘額全部轉回來。整個過程玩家不需要知道有幾個系統。

💬 這是一個 DEMO 站:不接金流、不接超商。玩家餘額的加減一律由後台人工操作, 而且每一筆都會寫進稽核紀錄。

🗺️ 三層圖

                        ┌──────────────────────────────┐
   玩家瀏覽器 ─────────▶ │  wolves_game_town            │  商戶門面(這個網站)
                        │  玩家帳號 · 主錢包 · 遊戲牆    │  對外開放
                        │  iframe · 商戶後台            │
                        └───────────┬──────────────────┘
                                    │  MIS  x-merchant-key(後端對後端)
                                    ▼
                        ┌──────────────────────────────┐
                        │  wolves_game_hub             │  聚合層
                        │  商戶 · 帳號命名空間 · 幣別    │  🔒 不對外
                        │  轉帳流水 · 對帳 · 遊戲目錄    │
                        └───────────┬──────────────────┘
                                    │  GIS  x-api-key(只有 hub 持有)
              ┌─────────────────────┼─────────────────────┐
              ▼                     ▼                     ▼
      wolves_fortune_lotto      SLOT 平台            魚機 / 百人 / 棋牌…
      (彩票,已接)             (規劃中)              (規劃中)
              ▲
              └──── iframe + 玩家的下注、開獎都直接連遊戲,不經過上面兩層

⚠️ 最容易誤會的一點:玩家在遊戲裡的下注與即時連線是 瀏覽器直接連遊戲平台的,完全不經過 town 與 hub。 那兩層一場遊戲只被呼叫三、四次(進場、轉帳、離場)。

📋 誰負責什麼

商戶門面 town聚合層 hub遊戲平台 game
玩家帳號✅ 真正的玩家身分帳號對映(加商戶前綴)平台側帳號(名字由 hub 給)
錢包主錢包,唯一真相 不持有餘額,只記流水遊戲側錢包(轉進去才有錢)
賠付 / RTP✅ 全歸遊戲
幣別換算用商戶幣別換算在這一層只認得遊戲幣
遊戲目錄快取 + 自己排序✅ 唯一來源
注單明細轉發顯示 不落地,現拉✅ 只存在遊戲
對帳自己的帳本✅ 中立的第三方紀錄自己的帳本
客服加減幣✅ 在商戶後台

🎬 一次進場的全程

玩家點一張遊戲卡
   │
   │  town → hub → game
   ├─ ①  建帳號(冪等,已經有就用既有的)
   ├─ ②  把玩家主錢包的餘額全額轉入那款遊戲所屬的平台
   ├─ ③  取一次性進場券(5 分鐘、用一次即失效)
   └─▶ 在站內 iframe 打開遊戲,玩家開始玩
          │
          │  ← 這段時間 town 與 hub 完全沒有參與
          │
玩家按「返回大廳」
   ├─ ④  查玩家在那個平台還剩多少
   ├─ ⑤  全額轉回主錢包
   └─▶ 回到大廳,餘額同步

玩家直接關掉瀏覽器怎麼辦?

錢會暫時留在遊戲那邊。後端有一個回收工作會定期把「太久沒動靜」的場次撈出來, 把餘額收回主錢包 —— 所以玩家的錢不會不見,下次登入就看得到。

🔴 一個玩家同時只能在一款遊戲裡。要換遊戲會先把上一款的餘額收回來 —— 否則自動轉帳會互相打架,錢被分散在好幾個平台。

🧩 為什麼要有中間這層

只有一個商戶、一款遊戲的時候,hub 看起來像多餘的一層。它存在有三個具體理由:

#理由沒有它會怎樣
1 帳號命名空間隔離 🔴 遊戲側的帳號名是全平台唯一的。兩個商戶各有一個 test001 就撞號 —— 而且要補救得去每一款遊戲的資料庫改帳號名。這是唯一一個「晚做會非常痛」的點,所以一開始就做。
2 API 金鑰託管 沒有 hub 就得把每一款遊戲的金鑰發給每一個商戶:無法單獨撤銷、無法限額, 一個商戶外洩就全部遊戲受害。
3 中立的對帳 進出金額記在商戶自己那邊,等於「自己報自己的帳」。放在中間層才有第三方紀錄。

順帶的好處

  • 吸收各平台的差異:每個自研平台的 API 長得不一樣(有的走 POST + JSON、有的走 GET + 簽章), 差異全部關在 hub 的轉接器裡,商戶只看到一組介面。
  • 商戶不用懂遊戲:接一組 API 就有全部遊戲;之後平台加新遊戲,商戶零改動。
  • 新增一個商戶=在 hub 開一組憑證,遊戲那邊完全不用動。

💱 幣別怎麼換(不是匯率)

每種幣別對「遊戲幣」有一個固定比例,跟匯率無關 —— 因為這裡沒有出金。

設計目標很明確:讓幣別對遊戲的影響是零。遊戲那邊永遠只看到一種東西(遊戲幣), 不需要幣別欄位、不需要為了不同幣別多開機器或多套資料庫。換算全部發生在 hub。

幣別換算最小帶入單位
USD / EUR / AUD1 元 = 1000 遊戲幣0.01
CNY / PHP / HKD / MYR1 元 = 100 遊戲幣0.1
TWD / THB / JPY / INR1 元 = 10 遊戲幣1
KRW / MMK1 元 = 1 遊戲幣1
IDR / VND10 元 = 1 遊戲幣10

🔴 除不盡的零頭不會消失。轉回來時無條件捨去到整數, 剩下的餘額留在遊戲那邊,下次進場它還在 —— 不進位(那會憑空生錢)、也不捨去(那會吃掉玩家的錢)。

比 MMK 更小的幣別(IDR / VND)沒辦法用 1:1,所以改用「最小帶入單位」處理: 一次至少要帶 10 元進遊戲。這樣比把比例硬湊成 1 誠實得多 —— 後者會讓這些幣別憑空貴十倍。

要接一個新幣別,就是在 hub 加一列設定,不用改任何程式碼

🧾 注單為什麼不存在 hub

分界線是「帳同構、單不同構」:

  • (錢的移動)跨平台長得一模一樣,只有金額與方向 → hub 存得起、也對得起帳。
  • (注單 / 局 / 開獎明細)每款結構完全不同 —— 彩票是門與賠率、拉霸是盤面與線、魚機是砲台與魚種。hub 要存就得為每款做一套資料表, 等於把遊戲邏輯搬進中間層。

所以細單一律用單號回遊戲現拉,不在中間層留副本 —— 遊戲那邊本來就有自己的稽核機制,再存一份只會製造「兩份可能不一致的真相」。

🔐 關於錢的三條紅線

  1. 帳本只增不改:每一筆加減都留一列,永不修改、永不刪除。 隨時可以驗「餘額 = 帳本總和」。
  2. 重送不會重複記帳:每筆轉帳有唯一流水號,網路超時重送同一筆, 結果只會記一次。
  3. 🔴 結果未知時不猜:呼叫遊戲逾時而不知道成功與否, 系統會把它標成「待查明」而不是自動回沖,交由對帳查明。 寧可卡住一筆,也不要猜錯方向。

📈 容量與部署

因為玩家的遊戲流量不經過前兩層,會被人數壓垮的永遠是遊戲平台本身。 商戶門面與聚合層都很輕,可以跟其中一台遊戲共處。

元件對外說明
商戶門面 town玩家入口,需要網域與 HTTPS
聚合層 hub🔒 只綁本機。所有遊戲的金鑰都在它身上,不對外就少一整個攻擊面
遊戲平台玩家的 iframe 直接連它,各自獨立擴充

⚠️ 關閉服務的順序:門面 → 聚合層 → 遊戲(跟啟動相反)。 先關中間層的話,正在進行的轉帳會變成「結果未知」,得走對帳處理。