---
# System prepended metadata

title: GAME API TW

---

# 更新記錄
### 2024/12/12
- 新增取得分鐘統計資料 api
- 更新餘額功能僅接受小數點後2位
  
# 遊戲API
遊戲相關 API 網址請使用我方提供之 API 網址

# 安全性

## 存取憑證(token)

> **取得進入存取憑證**

每10分鐘會失效

GET

```
/token
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Operator | 字串 | O | 經營方代碼 |
| X-Key | 字串 | O | 經營方API密鑰 |

- 標頭-範例:
```json
{
  "X-Operator": "op1",
  "X-key": "aqA12Wsqa341"
}
```

- 請求-範例:
`http://sample.domain.com/token`

### Success 200

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 物件[] | O | 產生存取憑證 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "token": "55xBgeODPYO7q...fNwXlJTsqRU4NZ4bmKLD"
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| Forbidden | X-Operator Or X-Key 錯誤 | 403 |
| Unauthorized | 驗證未通過 | 401 |
| Bad Request | 提供資料未正確 | 400 |
| Token Expired | 密鑰無效 | 401 |
| Operator invalid | X-Operator無效 | 401 |
| Internal Server Error | 伺服器發生問題 | 500 |
| Ip location not allowed | IP位置不允許 | 403 |

---
# 進入遊戲流程

## 註冊

> **註冊新的使用者**

POST

```
/register
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 參數(Query String)
| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶名.格式: 英數字(禁止使用特殊符號)最小長度: 5, 最大長度:64 |
| email | 字串 | X | 使用者郵箱 |
| birthday | 日期 | X | 使用者出生日期ISO-8601 格式 |
| country | 字串 | X | 國籍代碼預設Taiwan |
| fullname | 字串 | X | 使用者於遊戲中顯示之名稱(未設定以username顯示) |
| language | 字串 | X | 遊戲語言: zh-cn, zh-tw, en, vn, th (預設為商戶設定之語系)|

- 請求-範例:
```json
{
  "username": "myuser123",
  "country": "China",
  "fullname": "My User",
  "language": "zh-cn",
  "email": "myuser123@test.com",
  "birthdate": "1992-02-18"
}
```

- 回應-成功:
```json
HTTP/1.1 200 Created
{}
```

### 錯誤
| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| Unauthorized | 驗證未通過 | 401 |
| Bad Request | 提供資料未正確 | 400 |
| Username is required | 用戶名稱必填 | 400 |
| Email is required | 用戶email必填 | 400 |
| Language not allowed | 未支援語系 | 400 |
| user exists | user 已存在 | 400 |
| invalid parameter | 提供參數錯誤 | 400 |
| Token Expired | 密鑰無效 | 401 |
| Internal Server Error | 伺服器發生問題 | 500 |
| InternalServerError | 伺服器發生問題 | 500 |

---
## 取得所有供應商

> **取得遊戲供應商資料列表**

GET

```
/game-providers
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| rows | 整數 | X | 回傳記錄筆數預設10 |
| page | 整數 | X | 頁數預設1 |
| sidx | 字串 | X | 排序的欄位名稱預設id |
| sord | 字串 | X | 排序樣式預設asc允許值: "asc", "desc" |

- 請求-範例:
```
http://sample.domain.com/game-providers
http://sample.domain.com/game-providers?page=2
http://sample.domain.com/game-providers?page=1&rows=10
http://sample.domain.com/game-providers?page=1&rows=10&sidx=name&sord=desc
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| gameProviders | 物件[] | 遊戲供應商列表 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "gameProviders": [
      {
        "id": "1",
        "name": "Game Provider 1",
        "code": "gp1"
      },
      {
        "id": "2",
        "name": "Game Provider 2",
        "code": "gp2"
      }
    ],
    "meta": {
      "totalRecords": 2,
      "totalPages": 2,
      "currentPage": 1,
      "hasPrevious": false,
      "hasNext": true
    }
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| forbidden Gameprovider | 遊戲商錯誤 | 401 |
| InternalServerError | 內部服務器錯誤 | 500 |

---
## 取得餘額

> **取得用戶在供應商的餘額**

GET

```
/game-providers/:providerId/balance
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
    "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商id |

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶名 |

- 請求-範例:
`http://sample.domain.com/game-providers/1/balance?username=myuser123`

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balance | 字串 | 即時餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balance": "100.00",
      "status": "Enabled",
      "playing": "no"
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| Missing parameter | 參數不可為空 | 403 |
| User not found | 會員不存在 | 403 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---
## 更新餘額

> **更新用戶在供應商的餘額**

POST

```
/game-providers/:providerId/balance
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商 id |

### 參數(Body)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶名 |
| balance | 浮點數(小數後2位) | O | 被加或被減數大於0,僅支援小數後2位 |
| action | 字串 | O | 提款或取款代碼無效代碼預設成IN允許值: "IN", "OUT" |
| transferId | 字串 | O | 經營方轉賬交易單號 |

- 請求-範例:
```json
{
  "username": "sampop01",
  "balance": 500,
  "action": "out",
  "transferId": "sampleTransfercode"
}
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balance | 字串 | 最新余額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "balance": "1.00"
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP Status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 400 |
| invalid parameter | 錯誤參數設定 | 403 |
| IncompleteParameter | 參數設定不完整 | 403 |
| Prohibited to transfer while game in progress | 遊戲中禁止轉入/出 | 403 |
| Missing parameter | 數值不可為空 | 403 |
| Only accepts 2 decimal places | 僅接受小數後2位 | 403 |
| User not found | 會員不存在 | 404 |
| transferId already exists | 交易單號已經存在 | 400 |
| forbidden | 遊戲商錯誤 | 500 |
| Transfer failed | 轉賬失敗 | 400 |
| InternalServerError | 內部服務器錯誤 | 500 |
| Internal Server Error | 內部服務器錯誤 | 500 |
| User balance has been blocked | 用戶餘額鎖定 | 401 |

---
## 取得遊戲列表

> **取得遊戲列表**

GET

```
/games
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| provider | 整數 | O | 遊戲供應商id |
| category | 字串 | X | 遊戲分類 |
| rows | 整數 | X | 回傳記錄筆數預設50 |
| page | 整數 | X | 頁數預設1 |
| sidx | 字串 | X | 排序的欄位名稱預設id |
| locale | 字串 | X | 篩選指定語言遊戲, 支援 zh-cn, zh-tw, en, jp, vn, th |
| sord | 字串 | X | 排序樣式預設asc允許值: "asc", "desc" |
| type | 字串 | X | 遊戲類型預設blank允許值: "desktop", "mobile" |
- 請求-範例:

```
http://sample.domain.com/games
http://sample.domain.com/games?category=slot
http://sample.domain.com/games?provider=1
http://sample.domain.com/games?page=2
http://sample.domain.com/games?page=1&rows=10
http://sample.domain.com/games?page=1&rows=10&sidx=name&sord=desc
http://sample.domain.com/games?type=desktop
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| games | 物件[] | 遊戲列表 |

- 回應-成功:
    - gameType 1: slot, 2: poker, 3: lottery
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "games": [
      {
        "category": "slot",
        "name": "天狗祭",
        "providerId": 4,
        "code": "tengu-festival",
        "gameId": 82,
        "actived": true,
        "locales": [
            "zh-cn",
            "zh-tw",
            "en",
            "jp",
            "vn"
        ],
        "id": 82,
        "disabled": 0,
        "gameType": 1,
        "isNew": false,
        "isHot": false,
        "url": "https://play.godeebxp.com/egames/50addb64f3e1285277a4917d71736b94/game/images/platform/zh-tw/game_icon.png"
      },
      {
        "category": "poker",
        "name": "搶莊妞妞",
        "providerId": 4,
        "code": "banker-bull",
        "gameId": 68,
        "actived": true,
        "locales": [
            "zh-cn",
            "zh-tw",
            "en",
            "jp"
        ],
        "id": 68,
        "disabled": 0,
        "gameType": 2,
        "isNew": false,
        "isHot": false,
        "url": "https://play.godeebxp.com/egames/5ded8101bcdb4fa7864ca42873aac888/game/images/platform/zh-tw/game_icon.png"
      }
    ],
    "meta": {
      "totalRecords": 2,
      "totalPages": 2,
      "currentPage": 1,
      "hasPrevious": false,
      "hasNext": true
    }
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---

## 取得遊戲金鑰

> **取得遊戲金鑰**

GET

```
/game-providers/:providerId/games/:gameCode/key
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```
### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商 id |
| gameCode | 字串 | O | 遊戲代號 |

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶名 |
| fullname | 字串 | X | 使用者於遊戲中顯示名稱(未設定以 username 顯示) |
- 請求-範例:

`http://sample.domain.com/game-providers/1/games/gmcde/key?username=myuser123`

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| key | 字串 | 產生的金鑰 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "key": "67a17dd934c01fffcf119...beca27acdcc233168efb1cbb3c6a74dc846"
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| Missing Parameter | 參數不可為空 | 403 |
| User not found | 會員不存在 | 401 |
| Game not found | 錯誤的遊戲代號 | 401 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---
## 玩遊戲

> **遊戲進入點**

GET

```
/game-providers/:providerId/play
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```
### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商 id |

### 參數(Query String)

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| key | 字串 | 遊戲金鑰 |
| type | 字串 | 產生鏈接類型預設desktop允許值: "desktop", "mobile" |
| locale | 字串 | 語系預設operator經營方語系允許值: "zh-cn", "zh-tw", "en", "vn", "th"（簡，繁，英，越南文，泰文） |
- 請求-範例:

`http://sample.domain.com/game-providers/1/play?key=67a17dd934c01...4dc846`

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| url | 字串[] | 遊戲鏈接 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "url": "http://testproviderdomain.com/game/playgame?parameter=RTYUFGHJ" 
  }
}
```

### 錯誤

| 名稱 | 描述 |
| --- | --- |
| In maintenance | 系統維護中 | 400 |
| Forbidden | 遊戲商錯誤 |
| InternalServerError | 內部服務器錯誤 |

---

## 取得遊戲大廳

> **取得遊戲大廳的連結**

GET

```
/game-providers/:providerId/lobby
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```
### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商 id |

### 參數(Query String)

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| username | 字串 | 用戶名 |
| headless | 0 or 1 | 是否隱藏表頭， 預設否:0 |
| dark | 0 or 1 | 是否暗色風格， 預設否:0 |

- 請求-範例:

`http://sample.domain.com/game-providers/1/lobby?username=myuser123&headless=1&dark=1`

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| url | 字串[] | 遊戲大廳鏈接 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "url": "http://testproviderdomain.com/egames/lobby/game/?t=..." 
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---






## 遊戲地址query string字段說明
> **遊戲地址query string字段說明**

```
http://sample.domain.com/egames/800a4250f7a03739160a549ee65de3a5/?
t=4bea87bc4c644c82b1eee7e49ea90532&gn=vampire-hunter&l=zh-cn&ct=slot&
gt=slot3&socket_url=socket.game.bx&ts=1581908023924&view_mode=portrait& goback_url=https%3A%2F%2Fmy-goback.url%2Fpath%2Fto%2Fback
```

| Query String  | Required | Description | Value |
|--------------|----------|-------------|-------|
| client_type  | 必填 | 用戶端裝置類型，API 取得遊戲地址後會包含此字段，如返回值與實際用戶端不一致，請用戶端 app 修正為正確的值 | ios: ios app<br>android: android app<br>web: 桌上型電腦瀏覽器<br>wap: 手機瀏覽器<br>unknown: 未知 |
| goback_url   | 選填 | wap/web 返回鏈結 | {Url string} 提供 web/wap 遊戲端點擊離開按鈕時鏈結之 url，未提供 url 遊戲將直接關閉視窗。`goback_url=disable` 時，則使⽤⽆效。 |
| uniwebview   | 選填 | 是否使用 unity uniwebview 套件 | 1 如用戶端 app 使用 unity uniwebview 需接收需設置此字段，提供遊戲端正確調用開關 |
| view_mode    | 必填 | 橫屏 / 豎屏 | landscape: 橫屏<br>portrait: 豎屏 |


## Web(電腦瀏覽器) 及 Wap(手機瀏覽器) 離開遊戲
> **當遊戲地址 query string 上的 client_type=wap 或 client_type=web 時，且用戶端在遊戲地址後的 query string 加上 goback_url={YOUR GO BACK URL}，用戶點擊離開按鈕後遊戲會直接轉跳至 goback_url。**
> 
> **若未提供 goback_url ，遊戲將直接關閉視窗。**
> 
> **若 goback_url=disable 時，則關閉遊戲內"離開"按鈕功能，使返回無效。**

```
http://play-game.bx/egames/800a4250f7a03739160a549ee65de3a5/?
t=4bea87bc4c644c82b1eee7e49ea90532&gn=vampire-hunter&l=zh-cn&ct=slot&qt=slot3&socketunl=socket.game.bx&ts=1581908023924&view_mode=portrait&goback_url=https%3A%2F%2Fmygoback.url%2Fpath&2Fto%2Fback
```


## Web 及 Wap 中使用 iframe 接入 離開遊戲
> **當遊戲地址 query string 上的 client_type=wap 或 client_type=web 時，且用戶端使用 iframe接入遊戲時，在遊戲地址後的 query string 加上goback_url=javascript:window.parent.location.href="指定轉跳的地址"**


## Android 原生 APP 離開遊戲
> **當遊戲地址 query string 上的 client_type=android 及未設定 uniwebview 時會調用此方法**
> 
> **使用第三方框架 WVJBWebView**
> **build.gradle/dependencie**
> ![image](https://hackmd.io/_uploads/HJRgoHxK1g.png)
> **註冊監聽 TwGameBack, 即可收到 TwGameBack callback**
> ![image](https://hackmd.io/_uploads/r1yVjBxtkx.png)



## iOS 原生APP 離開遊戲
> **當遊戲地址 query string 上的 client_type=ios 及未設定 uniwebview 時會調用此方法。**
> **實現 WKScriptMessageHandler, 可於 userContentController 接收到 message。**
> ![image](https://hackmd.io/_uploads/SkF9jSeFkx.png)
> **使用 configuration.userContentController 註冊監聽"TwGameBack"**
> ![image](https://hackmd.io/_uploads/SkMpsBlY1g.png)


## Unity - UniWebview 離開遊戲
> **當用戶端使用 Unity3D 開發並使用 UniWebview 套件，且遊戲地址query string 上包含 uniwebview=1 時會調用此方法。**
> ![image](https://hackmd.io/_uploads/HkdW2rxt1g.png)



## 強制踢線玩家

> 踢除正在遊戲中的指定玩家或所有玩家

DELETE
```
/play/:username
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶遊戲帳號(使用 all 為踢除全部線上玩家) |

- 請求-範例:

```
http://sample.domain.com/play/myusername
http://sample.domain.com/play/all
```

### Success 200

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success"
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| Forbidden | 遊戲商錯誤 | 403 |
| User not exists | 用戶不存在 | 404 |
| InternalServerError | 內部服務器錯誤 | 500 |

---


# 獲取數據
## 取得大獎彩金

> **取得大獎彩金之點數**

GET

```
/jackpot
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

- 請求-範例:

```
http://sample.domain.com/jackpot
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| jp-mini | 數字(float) | 小獎彩金 |
| jp-minor | 數字(float) | 次獎彩金 |
| jp-major | 數字(float) | 大獎彩金 |
| jp-grand | 數字(float) | 巨獎彩金 |


- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "jp-minor": 33942.5,
    "jp-mini": 565856.67,
    "jp-major": 547010.59,
    "jp-grand": 993936.18
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| forbidden | 系統異常 | 400 |


---
## ~~取得指定分鐘範圍統計資料~~ (已廢除)

1. 可查詢的範圍為目前時間14分鐘前，最多可查詢到72小時內，例:目前時間為 2024-12-01 12:14，可查詢範圍為 2024-11-28 12:00 至 2024-12-01 11:59 

GET

```
/statistics?start=2024-11-28%2012:00&end=2024-12-01%2011:59
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```
### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| start | 字串(string) | O | 開始時間(yyyy-MM-dd HH:mm) |
| end | 字串(string) | O | 結束時間(yyyy-MM-dd HH:mm) |
| game | 整數(integer) | X | 遊戲 gameId (從遊戲列表取得) |


- 請求-範例:

```
http://sample.domain.com/statistics?start=2024-11-28%2012:00&end=2024-12-01%2011:59
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| totalBet | 數字(float) | 總投注點數(小數後兩位) |
| totalWin | 數字(float) | 總贏分主數(小數後兩位) |
| totalBetCount | 數字(integer) | 總投注次數 |
| totalWinCount | 數字(integer) | 總贏分次數 |
| startTime | 字串(string) | 開始時間 |
| endTime | 字串(string) | 結束時間 |
| featureBet | 數字(float) | 總購買特色遊戲點數 |
| featureWin | 數字(float) | 總購買特色遊戲贏分 |
| featureCount | 數字(integer) | 總購買特色遊戲次數 |
| currency | 字串(string) | 使用幣別(P130: 130比1, P100: 100比1... 以此類推) |


- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "totalBet": 123456.78,
    "totalWin": 123456.78,
    "totalBetCount": 12345,
    "totalWinCount": 1234,
    "startTime": '2024-11-01 13:00',
    "endTime": '2024-11-01 13:10',
    "currency": 'TWD'
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| forbidden | 系統異常 | 400 |
| user not found | 查無用戶 | 400 |
| game not found | 查無遊戲 | 400 |
| time range too large | 時間範圍過長 | 400 |
| invalid time range | 參數的時間範圍 | 400 |

---

## 取得指定日期統計資料

> **取得指定日期統計資料**

GET

```
/statistics/:date?time=00
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```
### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| time | 數字(Integer) | X | 指定取得的第幾個小時 (例: 設定為 00 可查詢當日的 00:00 - 00:59 的資料) |
| game | 數字(Integer) | X | 遊戲 gameId (從遊戲列表取得) |

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| date | 字串 | O | 指定日期(YYYY-MM-DD) |

- 請求-範例:

```
http://sample.domain.com/statistics/2024-05-01
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| totalBet | 數字(float) | 總投注點數 |
| totalWin | 數字(float) | 總贏分主數 |
| totalBetCount | 數字(integer) | 總投注次數 |
| totalWinCount | 數字(integer) | 總贏分次數 |
| featureBet | 數字(float) | 總購買特色遊戲點數 |
| featureWin | 數字(float) | 總購買特色遊戲贏分 |
| featureCount | 數字(integer) | 總購買特色遊戲次數 |
| profit | 數字(float) | 淨利潤點數 |
| totalPlayerCount | 數字(integer) | 不重複玩家數 |
| newPlayerCount | 數字(integer) | 新註冊玩家數(不分遊戲) |


- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "jp-minor": 33942.5,
    "jp-mini": 565856.67,
    "jp-major": 547010.59,
    "jp-grand": 993936.18
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| forbidden | 系統異常 | 400 |

---

## 取得未出餘額用戶名單

> **取得未出餘額用戶名單**

GET

```
/game-providers/:providerId/balance-users
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |

- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| providerId | 整數 | O | 遊戲供應商id |

- 請求-範例:

```
http://sample.domain.com/game-providers/:providerId/balance-users
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| username | 字串 | 用戶帳號 |
| amount | 數字(float) | 餘額小數點後2位 |
| status | 字串 | Enabled: 啟用中, Blocked: 鎖定中 |
| updatedAt | 數字(integer) | 最後更新時間 utc timestamp(ms) |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "users": [
        {
            "username": "superman",
            "amount": 12345.67,
            "updatedAt": 1724059573433
        },
        {
            "username": "superwoman",
            "amount": 12345.67,
            "updatedAt": 1724059573433
        },
        ...
    ]
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| forbidden | 系統異常 | 400 |

---

## 下注記錄

> **利用API取得注單(限3秒內僅能執行一次,查詢區間限制為五分鐘)**

POST

```
/transaction
```

### 注單查詢參數

| 參數              | 類型       | 必要 | 描述 |
| ----------------- | ---------- | ---- | ----------------------------------------------------------------------------------- |
| Operator          | 字串       | O    | X-Operator 經營方代碼 |
| Key               | 字串       | O    | X-Key 經營方API金鑰 |
| SDate             | 時間與日期  | O    | 格式 : YYYY-MM-DD HH:mm:ss, 時區為: 經營賬號時區(預設GTM+8) 查詢起始時間(大於等於)  |
| EDate             | 時間與日期  | O    | 格式 : YYYY-MM-DD HH:mm:ss, 時區為: 經營賬號時區(預設GTM+8) 查詢結束時間(小於等於，時間範圍最長5分鐘) |
| Type              | 字串       | X    | 注單時間過濾方式 settledate(預設), bettingdate |
| Provider          | 字串       | X    | 遊戲供應商 |
| PlayerID          | 字串       | X    | 玩家賬號 |
| TransactionNumber | 字串       | X    | 注單編號 |
| BetDataGameCode   | 字串       | X    | 指定 gameCode 返回詳情(僅限特定遊戲)，同時指定多個遊戲以","分隔,例: mahjong,bubble-gum  |

- 成功-回應
```json
HTTP/1.1 200 OK
[
  {            
    "gameprovider": "p1",         // 遊戲供應名稱 {String}
    "membername": "user1",        // 用戶名稱 {String}
    "gamename": "game1",          // 遊戲名稱 {String}
    "bettingid": "667e6037fff4",  // 注單ID {String}
    "bettingcode": 1124,          // 注單編號 {Integer}   
    "bettingdate": "1695474361",  // 投注時間 {String}       
    "gameid": 11111,              // 遊戲ID {Integer}
    "gamecode": "bubble-gum",     // 遊戲代碼 {String}
    "roundno": "null",            // 遊戲回合(部分遊戲提供) {String}
    "result": "null",             // 注單結果(部分遊戲提供) {String}  
    "bet": "null",                // 投注內容(部分遊戲提供) {String}
    "winloseresult": "W",         // 勝負狀態(W: 勝, L: 負) {String}
    "totalstake": "1.000",        // 下注點數 {String}
    "bettingamount": 1.000,       // 打碼點數 {Float}
    "validbet": "1.000",          // 有效注單點數            
    "winloseamount": 1.000,       // 勝負點數 {Float}    
    "balance": "null",            // 投注後餘額(部分遊戲提供) {String}            
    "currency": "MYR",            // 貨幣 {String}
    "isfree": "0",                // 是否為免費遊戲 0: 否, 1: 是  {String}
    "isfeature": "0",             // 是否為購買特色遊戲 0: 否, 1: 是  {String}
    "handicap": "null",           // 讓分(部分遊戲提供) {String}
    "status": "close",            // 狀態(close: 已結算, open: 未結算, refund: 退款, cancel: 撤單) {String}
    "gamecategory": "slot",       // 遊戲類別(lottery: 彩票, slot: 老虎幾, poker: 棋牌, arcade: 街機小遊戲) {String}
    "settledate": "1695474361",   // 結算時間(Unix timestamp second) {String} 
    "remark": "null",             // 備註 {String}
    "betdata": "json string",     // 注單詳情(特定遊戲，以 jsonstring 返回) {String}, 預設值為 "null"
    "replayurl": "string"         // 該注單的重播連結(部份遊戲提供) {String}
  }
]
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| (Operator name) must be provided | 必要入operator | 403 |
| (Operator Key) must be provided | 必要入Key | 403 |
| (Start Date) must be provided | 時間格式錯誤 | 403 |
| (End Date) must be provided | 時間格式錯誤 | 403 |
| SDate cannot be greater than EDate | SDate 大於 EDate 時間條件錯誤 | 403 |
| Transaction is no allowed for over then 5 minuntes | 抄單時間範圍不得超過5分鐘 | 403 |
| Request time limit. Try again latter. | 抄單頻率過快,預設兩次間隔不得少於3秒 | 405 |

---

## 轉賬記錄

> **利用API取得額度轉換(限3秒內僅能執行一次)**

POST > FORM-DATA

```
/transfer
```

### 查詢參數

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| Operator | 字串 | O | X-Operator 經營方代碼 |
| Key | 字串 | O | X-Key 經營方API金鑰 |
| SDate | 時間與日期 | O | 格式 : YYYY-MM-DD HH:MM:SS, 時區為 : 經營賬號時區(預設GTM+8) "查詢區間限制為十分鐘" |
| EDate | 時間與日期 | O | 格式 : YYYY-MM-DD HH:MM:SS, 時區為 : 經營賬號時區(預設GTM+8) "查詢區間限制為十分鐘" |
| Provider | 字串 | X | 遊戲供應商 |
| PlayerID | 字串 | X | 玩家賬號 |
| TransferCode | 字串 | X | 交易紀錄編號(客戶提供的備註或系統生成皆可) |
| Exact | 布林 | X | 預設TrueTrue - PlayerID 精確查詢False - PlayerID 模糊查詢允許值: "True", "False" |

- 成功-回應

```json
HTTP/1.1 200 OK
[
  {            
    "provider": "p1",            
    "id": "10",            
    "username": "user1",            
    "amount": "112",            
    "currency": "MYR",            
    "actions": "IN",            
    "balance": "50",            
    "transfercode": "mytransfercode",            
    "createtime": "1695474361"            
  }
]
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| (Operator name) must be provided | 必要入operator | 403 |
| (Operator Key) must be provided | 必要入Key | 403 |
| (Start Date) must be provided | 時間格式錯誤 | 403 |
| (End Date) must be provided | 時間格式錯誤 | 403 |

---

## 單筆轉賬記錄

> **以 transferId 取得單筆交易記錄**

GET

```
/Transfer/:transferId
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 域名(Parameters)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| transferId | 字串 | O | 交易id |

- 請求-範例:
```
http://sample.domain.com/Transfer/223120810qPPlSr
```


- 成功-回應

```json
HTTP/1.1 200 OK

{
    "status": "success",
    "data": {
        "provider": "p1",
        "id": "10",
        "username": "user1",
        "amount": "112",
        "currency": "TWD",
        "actions": "IN",
        "balance": "50",
        "transfercode": "223120810qPPlSr",      
        "createtime": "2022-11-08T08:10:36.233Z"
    }
}

```

### 錯誤
```json
HTTP/1.1 404 Not Found

{
    "status": 404,
    "message": "Record not found."
}
```

| 名稱 | 描述 | HTTP Status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| Transfer in progress | 交易處理中 | 403 |
| Internal Server Error | 內部服務器錯誤 | 500 |
| Record not found. | 查無相關記錄 | 404 |

---

## 取得注單下載連結

> **提供注單檔案連結，匯出 CSV 檔並壓縮成 ZIP(12分鐘後的注單，存活時間最長二小時)**
> **商戶需提出申請，才能使用此功能**

GET

```
/transaction/link
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": 200,
  "message": "success",
  "data": {
    "list": [
      {
        "startTime": 1762999140000,
        "endTime": 1762999199999,
        "betCount": 2,
        "link": "https://abc.com/1762999140000.zip"
      }
    ]
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---

## 登記注單匯出任務

> **提供登記注單下載任務，匯出完成後，在 取得注單下載連結 可取得連結**
> **商戶需提出申請，才能使用此功能**

POST

```
/transaction/link/register
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 注單查詢參數

| 參數              | 類型       | 必要 | 描述 |
| ----------------- | ---------- | ---- | ----------------------------------------------------------------------------------- |
| startTime         | Timestamp(13碼)  | O    | 預設GTM+8 |
| endTime           | Timestamp(13碼)  | O    | 預設GTM+8 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": 200,
  "message": "success"
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |
| Start time is require | 開始時間為必填 | 400 |
| End time is require | 結束時間為必填 | 400 |
| End time must be greater than start time | 結束時間必需大於開始時間 | 400 |
| Time range must not be greater than 2 hours | 時間範圍最長二小時 | 400 |

---
# 其他

## 取得所有玩家

> **取得玩家列表**

GET
```
/players
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| X-Token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
  "X-Token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| rows | 整數 | X | 回傳記錄筆數預設10 |
| page | 整數 | X | 頁數預設1 |
| sidx | 字串 | X | 排序的欄位名稱預設id |
| sord | 字串 | X | 排序樣式預設asc允許值: "asc", "desc" |

- 請求-範例:

```
http://sample.domain.com/players
http://sample.domain.com/players?page=2
http://sample.domain.com/players?page=1&rows=10
http://sample.domain.com/players?page=1&rows=10&sidx=name&sord=desc
```

### Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| players | 字串[] | 玩家列表 |

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
    "players": [
      {
        "id": 1,
        "username": "DUMMYACCT2X",
        "fullname": "DUMMY ACCT 2ND",
        "email": "DUMMY.ACCT2@TEST.COM",
        "country": "PHILIPPINES",
        "currency": "CNY",
        "birthdate": "1992-02-18T00:00:00.000Z",
        "operatorId": 2,
        "createdAt": "2017-06-01T21:07:05.000Z",
        "updatedAt": "2017-06-01T21:07:05.000Z"
      },
      {
        "id": 2,
        "username": "DUMMYACCT3X",
        "fullname": "DUMMY ACCT 3RD",
        "email": "DUMMY.ACCT3@TEST.COM",
        "country": "JAPAN",
        "currency": "CNY",
        "birthdate": "1990-05-18T00:00:00.000Z",
        "operatorId": 2,
        "createdAt": "2017-06-01T21:07:05.000Z",
        "updatedAt": "2017-06-01T21:07:05.000Z"
      }
    ],
    "meta": {
      "totalRecords": 5,
      "totalPages": 2,
      "currentPage": 2,
      "hasPrevious": true,
      "hasNext": false
    }
  }
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| invalid token | 無效Token | 401 |
| Forbidden | 遊戲商錯誤 | 403 |
| InternalServerError | 內部服務器錯誤 | 500 |

---

# 單一錢包 (建議使用V2版本)
此版本即將棄用，建議使用V2版本可提升執行效率
## 說明
1. 由貴方提供「單一錢包交易API」, 讓我方串接進行餘額異動。
2. 當貴方收到我方發出 API 請求時，需將 headers 中的 token 及 body 中的 data，先發送驗証請求到我方的「[驗証錢包參數](#wallet-token)」API，確認 token 及資料完整性，token 為一次性，驗証完即失效。
3. body 中的 data，解密方式為 AES 128 CBC，key 及 iv 長度皆為 16 碼，不足16位，後面補0，超過16位就取前16位。
4. 當我方呼叫貴方的 API, 如果網路出現異常(例如：Timeout)，我方會重試三次，每次間隔 50 ms，此 n 次請求的 token 皆相同，可用於判斷是否重覆請求。

### 營運商需提供以下資訊

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| url | 字串 | API base url |

### AES 解密資訊

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| iv | 字串(16 碼) | O | 經營方代碼(X-Operator) |
| key | 字串(16 碼) | O | 經營方API密鑰(X-Key) |

- JavaScript 範例:

```JavaScript
const decrypt = (key, iv, str) => {
    const key2 = key.slice(0, 16)
    const iv2 = iv.padEnd(16, '0')
    const crypted = Buffer.from(str, 'base64').toString('binary')
    const decipher = crypto.createDecipheriv('aes-128-cbc', key2, iv2)
    let decoded = decipher.update(crypted, 'binary', 'utf8')
    decoded += decipher.final('utf8')

    return decoded
}
```

- PHP 範例:

```php
function decrypt($key, $iv, $str) {
    // 將 key 截取為前 16 個字符
    $key2 = substr($key, 0, 16);
    // 將 iv 補充為 16 個字符
    $iv2 = str_pad($iv, 16, '0');
    // 將 base64 字符串轉換為二進制數據
    $crypted = base64_decode($str);
    
    // 使用 openssl_decrypt 進行解密
    $decoded = openssl_decrypt($crypted, 'AES-128-CBC', $key2, OPENSSL_RAW_DATA, $iv2);

    // 返回解密後的字符串
    return $decoded;
}

// 測試
$key = "your_key_here";
$iv = "your_iv_here";
$str = "your_encrypted_string_here";
echo decrypt($key, $iv, $str);
```

---

## 取得餘額

> **取得用戶在營運商的餘額**

GET
```
{單一錢包交易API網址}/balance
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 交易驗証憑證 |
- 標頭-範例:

```json
{
    "token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| username | 字串 | O | 用戶名 |

- 請求-範例:
`https://sample.domain.com/balance?username=myuser123`

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balance | 浮點數 | 即時餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 下注扣款

> **用戶在營運商的下注扣款**

POST
```
{單一錢包交易API網址}/betting
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
    "token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 下注點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 扣款前餘額 |
| balance | 浮點數 | 扣款後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 500,
      "balance": 400
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 結算派獎

> **用戶在營運商的結算派獎**
> **如玩家斷線，系統有自動關單機制，請勿限制玩家離線時不可結算派獎**

POST
```
{單一錢包交易API網址}/settlement
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
    "token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 贏分點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 派獎前餘額 |
| balance | 浮點數 | 派獎後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 400,
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 退款

> **用戶在營運商的退款，此為彩票類遊戲使用，如開獎異常或和局時，遊戲自行判斷退款**
> **例如：賽馬的百家樂下莊閒，開和局時，會觸發退款**

POST
```
{單一錢包交易API網址}/refund
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
- 標頭-範例:

```json
{
    "token": "aqA12Wsqa341...ASDasd123asd"
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 退款點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 退款前餘額 |
| balance | 浮點數 | 退款後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 400,
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 驗証錢包參數
<a id="wallet-token"></a>

> 驗証營運商單一錢包所回傳的 token 及 data

POST
```
/wallet/authorize
```

### 參數(Query String)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | wallet token |
| data | 字串 | X | 單一錢包所傳送的資料(如 GET 不需傳 data) |

- 請求-範例:
```json
{
  "token": "664ee129a697acf53d3b6e14",
  "data": "aofYPahYUsANEuXfhwBjeWTUgYtIFRlw0PErKZexkVw="
}
```

### Success 200

- 回應-成功:
```json
HTTP/1.1 200 OK
{
  "status": "success"
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "message": "錯誤訊息"
}
```

### 錯誤

| 名稱 | 描述 | HTTP status |
| --- | --- | --- |
| In maintenance | 系統維護中 | 400 |
| Internal Server Error | 伺服器發生問題 | 500 |
| Forbidden | 驗証錢包 token 錯誤或過期 | 403 |

---

# 單一錢包 V2

## 說明
1. 由貴方提供「單一錢包交易API」, 讓我方串接進行餘額異動。
2. 當貴方收到我方發出 API 請求時，需執行 token 的驗証，以確保資料無誤。
2.1 取出 headers 中的 timestamp，以及 body 中的 data 跟 iv 字串相接後做 md5 編碼，與 token 是否相符，範例：token === md5(iv + timestamp + data)。
2.2 timestamp 為有效期限，判斷 timestamp 未過期時，再處理此封包，範例：now() < timestamp。
3. body 中的 data，解密方式為 AES 128 CBC，key 及 iv 長度皆為 16 碼，不足16位，後面補0，超過16位就取前16位。
4. 當我方呼叫貴方的 API, 如果網路出現異常(HTTP Status code：5xx)，我方會重試三次，每次間隔 50 ms，此 n 次請求的 token 皆相同，可用於判斷是否重覆請求。
5. 我方呼叫 API 的 Timeout 設定值為 10 秒，如果呼叫逾時，不會觸發重試機制。

### 營運商需提供以下資訊

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| url | 字串 | API base url |

### AES 解密資訊

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| iv | 字串(16 碼) | O | 經營方代碼(X-Operator) |
| key | 字串(16 碼) | O | 經營方API密鑰(X-Key) |

- JavaScript 範例:

```JavaScript
const decrypt = (key, iv, str) => {
    const key2 = key.length > 16 ? key.slice(0, 16) : key.padEnd(16, '0')
    const iv2 = iv.length > 16 ? iv.slice(0, 16) : iv.padEnd(16, '0')
    const crypted = Buffer.from(str, 'base64').toString('binary')
    const decipher = crypto.createDecipheriv('aes-128-cbc', key2, iv2)
    let decoded = decipher.update(crypted, 'binary', 'utf8')
    decoded += decipher.final('utf8')

    return decoded
}
```

- PHP 範例:

```php
function decrypt($key, $iv, $str) {
    $key2 = strlen($key) > 16 ? substr($key, 0, 16) : str_pad($key, 16, '0');
    $iv2 = strlen($iv) > 16 ? str_pad($iv, 16, '0') : str_pad($iv, 16, '0');
    
    // 將 base64 字符串轉換為二進制數據
    $crypted = base64_decode($str);
    
    // 使用 openssl_decrypt 進行解密
    $decoded = openssl_decrypt($crypted, 'AES-128-CBC', $key2, OPENSSL_RAW_DATA, $iv2);

    return $decoded;
}
```

---

## 解密範例
 
加密所需參數
| 參數 | 值 |
| --- | --- |
| iv | iv1 |
| key | key1 |
| timestamp | 1733797877 |

加密後參數
| 參數 | 值 |
| --- | --- |
| token | f0a7a81001350206304b370684de63b2 |
| data | Ce6M+q7tjSab7lrIvzgYd9EEM8YzvoS4IhaSxLHieebcrD15YJWfKNC2EzoJ1Yjm3AvoWtYZUMnQKqEJHyL5u9oLSHC9lILuQUj67/XO0/U= |


解密後參數
| 參數 | 值 |
| --- | --- |
| uuid | b99ad91c19004e28a37c1771c625b3c5 |
| username | username1 |

- curl 範例:

```curl
curl -X POST http://localhost/balance 
     -H 'Content-Type: application/json' 
     -H 'token: f0a7a81001350206304b370684de63b2' 
     -H 'timestamp: 1733797877' 
     -d '{"data":"Ce6M+q7tjSab7lrIvzgYd9EEM8YzvoS4IhaSxLHieebcrD15YJWfKNC2EzoJ1Yjm3AvoWtYZUMnQKqEJHyL5u9oLSHC9lILuQUj67/XO0/U="}'
```

- JavaScript 解密範例:

```JavaScript
const decryptData = async (token, timestamp, data) => {

  const md5 = crypto.md5(`${iv}${timestamp}${data}`)
  if (token !== md5) {
    console.log('check token error')
  }

  const now = moment().unix()
  if (now > parseInt(timestamp)) {
    console.log('timestamp expried')
  }

  const decryptedStr = aes.decrypt(key, iv, data)
  const decrypted = JSON.parse(decryptedStr)
  
  return decrypted
}
```

---

## 取得餘額

> **取得用戶在營運商的餘額**

POST
```
{單一錢包交易API網址}/balance
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 交易驗証憑證 |
| timestamp | 整數(10碼) | O | 有效期限 |
- 標頭-範例:

```json
{
    "token": "667e60419dc6d6428a99ee0e",
    "timestamp": 1704070800
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| username | 字串 | O | 用戶名 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "username": "myuser123"
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balance | 浮點數 | 即時餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 下注扣款

> **用戶在營運商的下注扣款**

POST
```
{單一錢包交易API網址}/betting
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
| timestamp | 整數(10碼) | O | 有效期限 |
- 標頭-範例:

```json
{
    "token": "667e60419dc6d6428a99ee0e",
    "timestamp": 1704070800
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 下注點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 扣款前餘額 |
| balance | 浮點數 | 扣款後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 500,
      "balance": 400
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 結算派獎

> **用戶在營運商的結算派獎**
> **如玩家斷線，系統有自動關單機制，請勿限制玩家離線時不可結算派獎**

POST
```
{單一錢包交易API網址}/settlement
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
| timestamp | 整數(10碼) | O | 有效期限 |
- 標頭-範例:

```json
{
    "token": "667e60419dc6d6428a99ee0e",
    "timestamp": 1704070800
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 贏分點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 派獎前餘額 |
| balance | 浮點數 | 派獎後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 400,
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```

---

## 退款

> **用戶在營運商的退款，此為彩票類遊戲使用，如開獎異常或和局時，遊戲自行判斷退款**
> **例如：賽馬的百家樂下莊閒，開和局時，會觸發退款**

POST
```
{單一錢包交易API網址}/refund
```

### 標頭(Header)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| token | 字串 | O | 產生存取憑證 |
| timestamp | 整數(10碼) | O | 有效期限 |
- 標頭-範例:

```json
{
    "token": "667e60419dc6d6428a99ee0e",
    "timestamp": 1704070800
}
```

### 解壓後資料(data)

| 參數 | 類型 | 必要 | 描述 |
| --- | --- | --- | --- |
| uuid | 字串 | O | 識別碼 |
| betId | 字串 | O | 注單ID |
| gameCode | 字串 | O | 遊戲代碼 |
| username | 字串 | O | 用戶名 |
| amount | 浮點數 | O | 退款點數 |

- 請求-範例:
```json
{
  "uuid": "664ee129a697acf53d3b6e14",
  "betId": "6661868717e794f36effc59a",
  "gameCode": "climb-stairs",
  "username": "myuser123",
  "amount": 100
}
```

### 成功 Success 200

| 參數 | 類型 | 描述 |
| --- | --- | --- |
| balanceOld | 浮點數 | 退款前餘額 |
| balance | 浮點數 | 退款後餘額 |

- 回應-成功
```json
HTTP/1.1 200 OK
{
  "status": "success",
  "data": {
      "balanceOld": 400,
      "balance": 500
  }
}
```

- 回應-失敗
```json
HTTP/1.1 200 OK
{
  "status": "fail",
  "data": {
      "message": "錯誤訊息"
  }
}
```
