Skip to content

錢包(轉帳) ​

本平台使用轉帳錢包:每位玩家在本平台有一個遊戲錢包,你的系統以 API 把金額轉入、轉出。遊戲中的下注、派彩都在這個錢包內自動處理:

  • 下注時本金立即從餘額扣除(牛牛押翻倍時連同預扣一起扣,見牛牛的預扣)。
  • 結算時派彩(含本金;牛牛另含退回的預扣)自動入帳;結果修正或作廢時自動補差額或退款。
  • 所以任何時候的餘額都是可用餘額,未結算注單的本金不會被轉出。
端點用途
POST /wallet/deposit轉入(你的系統 → 遊戲錢包);玩家不存在時自動建立
POST /wallet/withdraw轉出(遊戲錢包 → 你的系統)
GET /wallet/transfer?txnId=查詢一筆轉帳的結果
GET /wallet/balance?username=查詢目前的可用餘額

轉入與轉出 ​

json
{ "username": "alice", "txnId": "dep-20260924-000123", "amount": "1000", "currency": "TWD" }
欄位說明
username玩家帳號。轉入時玩家不存在會以 currency 自動建立;轉出時玩家必須存在
txnId你的轉帳單號,1–64 個字元(英數字與 _ . : -),作為冪等鍵
amount大於 0 的十進位字串,最多 4 位小數,例如 "1000"、"0.5"。不要用浮點數
currency必須等於玩家的幣別,否則回 CURRENCY_MISMATCH

回應:

json
{ "ok": true, "data": { "txnId": "dep-20260924-000123", "status": "done", "balance": "2500.5", "duplicate": false } }
  • 轉帳是單階段的:回應成功就是已經完成(status 一律是 done),沒有「處理中」或需要再確認的步驟。
  • balance 是這筆轉帳完成當下的餘額。
  • 回應成功的轉帳一定查得到:GET /wallet/transfer?txnId=…&username=…。
  • 轉出金額大於可用餘額時回 409 INSUFFICIENT_BALANCE,餘額不變。

冪等:txnId 的規則 ​

情況結果
新的 txnId執行轉帳,duplicate: false
同一個 txnId、同一位玩家、相同金額與方向不會再執行,回傳第一次的結果(balance 為那筆轉帳完成當下的餘額)與 duplicate: true
同一個 txnId,但金額或方向(轉入/轉出)不同409 TXN_CONFLICT,餘額不變
同一個 txnId 在最近兩個月內已用在另一位玩家409 TXN_CONFLICT,餘額不變
  • 請讓 txnId 在你的整個租戶內永久唯一,例如 dep-<你的交易編號>。
  • 每一筆新的轉帳用新的 txnId;重試時則必須沿用同一個 txnId 與完全相同的內容。

逾時與重試 ​

網路逾時、連線中斷、收到 5xx 或 429 時,你無法確定轉帳是否已經執行。正確的處理方式:

  1. 送出前,先在你的資料庫記下這筆轉帳(txnId、金額、方向,狀態「處理中」)。
  2. 呼叫 deposit/withdraw(建議逾時 10 秒)。
  3. 2xx(包含 duplicate: true):標記完成,以回應的 balance 更新顯示。
  4. 4xx(例如 INSUFFICIENT_BALANCE、TXN_CONFLICT、INVALID_AMOUNT):明確失敗,標記失敗;轉出失敗時把金額退回玩家在你系統的餘額。
  5. 逾時、網路錯誤、5xx、429:狀態未知。以相同的 txnId、相同內容重送(每次換新的 nonce 與時間戳,退避例如 1、2、4、8 秒),直到得到 2xx 或 4xx。
js
// 以 sign.js 的 call()(見〈認證與簽章〉)為例
async function transferWithRetry(kind, payload, auth) {
  for (let attempt = 0; ; attempt++) {
    try {
      return await call('POST', `/api/tenant/v1/wallet/${kind}`, payload, auth); // 2xx:完成
    } catch (err) {
      const unknown = !err.status || err.status >= 500 || err.status === 429;
      if (!unknown || attempt >= 8) throw err; // 4xx:明確失敗;或交給人工/排程稍後再試
      await new Promise((r) => setTimeout(r, Math.min(30_000, 1000 * 2 ** attempt)));
      // 同一個 payload(同一個 txnId)重送
    }
  }
}

也可以先用 GET /wallet/transfer?txnId=…&username=… 查詢(請帶 username):

  • 200:轉帳已完成,回應包含方向、金額與完成後的餘額。回應成功的轉帳一定查得到。
  • 404 TXN_NOT_FOUND:這筆轉帳沒有執行(或仍在處理中)。請以同一個 txnId 重送轉帳,直到得到明確結果;不要改用新的 txnId。

絕對不要

逾時後換一個新的 txnId 重送。如果第一次其實已經成功,玩家會被轉兩次。

查詢轉帳 ​

text
GET /api/tenant/v1/wallet/transfer?txnId=dep-20260924-000123&username=alice
json
{
  "ok": true,
  "data": {
    "txnId": "dep-20260924-000123",
    "username": "alice",
    "dir": "in",
    "amount": "1000",
    "status": "done",
    "balance": "2500.5",
    "at": "2026-09-24T03:00:01.250Z"
  }
}
  • dir:in 轉入、out 轉出、adjust Console 的人工上下分。
  • username 選填但建議帶:轉帳一完成,玩家錢包就有紀錄,租戶流水的副本會在稍後送達;帶 username 時兩邊都會查,所以成功的轉帳一定查得到。
  • 租戶流水的查詢範圍是當月與前 3 個月(依 UTC 月份)。更早的轉帳請以 Console 注單與帳務 → 轉帳紀錄 或匯出核對。

查詢餘額 ​

text
GET /api/tenant/v1/wallet/balance?username=alice
json
{ "ok": true, "data": { "balance": "2500.5", "currency": "TWD" } }

餘額是可用餘額,已扣除進行中注單的本金(牛牛翻倍另扣預扣)。要轉出玩家全部的錢時,先查餘額再以這個金額轉出;如果玩家在兩次呼叫之間又下注,轉出會回 INSUFFICIENT_BALANCE,重新查詢後再試即可。

金額與幣別 ​

  • 所有金額都是十進位字串,最多 4 位小數;回應會去掉小數尾端的 0("100"、"0.95"、"1000.5")。
  • 請以字串(或十進位型別)處理金額,不要轉成浮點數再加減。
  • 玩家的幣別在建立時決定,之後不能變更;轉帳的 currency 必須與玩家相同。

每日對帳建議 ​

  1. 轉帳:你的轉帳紀錄中狀態不是「完成」的,每天以同一個 txnId 重送或查詢,直到每一筆都有明確結果。

  2. 餘額:對每位有活動的玩家,驗證

    text
    期初餘額 + 轉入 − 轉出 + Σ 注單輸贏(每張注單取最新版次的 winLoss)± 人工上下分 = 期末餘額

    注單輸贏以注單同步取得;對帳時點若仍有未結算的注單,差額等於那些注單的本金(牛牛再加預扣)。

  3. 每日彙總:以 GET /bets/summary?date= 與你自己彙總的數字核對。

錯誤碼 ​

錯誤碼HTTP原因
INVALID_PARAMETER400缺少欄位,或 txnId 格式錯誤
INVALID_AMOUNT400金額不是大於 0、最多 4 位小數的字串
INVALID_USERNAME400帳號不符合規則
INVALID_CURRENCY400轉入時建立新玩家,但幣別代碼不合法
CURRENCY_MISMATCH400幣別與玩家不同
PLAYER_NOT_FOUND404轉出或查詢的玩家不存在
PLAYER_LIMIT403轉入時要建立新玩家,但玩家數已達方案上限(免費展示方案 50、沙箱 500)
TXN_CONFLICT409txnId 已用在不同的轉帳,或最近兩個月內已用在另一位玩家
INSUFFICIENT_BALANCE409可用餘額不足
TXN_NOT_FOUND404查不到這筆轉帳(帶 username 時表示沒有執行)

其他共通錯誤見錯誤碼。

elite 租戶整合 API v1