---
# System prepended metadata

title: 'Domain:'

---

#### 1.0業主平台商名稱
ua6

#### 後台url:
[后台管理系统 ](https://sw-lottery-uat.caynne.com/#/)

#### 後台帳號:
ua6111,ua6111a

#### 後台密碼:
ua6111,ua6111a

#### token:
eyJhbGciOiJIUzI1NiJ9.eyJhZ2VudElEIjo0NiwiYWNjb3VudCI6InVhNjExMSIsIm5hbWUiOiJ1YTYifQ.6dvgI7Ss64VQ6M5vWOiYH0lB4ycjOUn41NvFB5oMk8E

#### salt: 
LZBinadMFw9D2bwkHWU  (取餘額時加密用)

#### 測試帳號:
      
account:UA6test123 
password:"UA6test123


 

#### postman get gameLink curl

 


```
curl --location --request POST 'https://ig-callback.ycmpos.com/user/game-link' \
--header 'Content-Type: application/json' \
--header 'Authorization: eyJhbGciOiJIUzI1NiJ9.eyJhZ2VudElEIjo0NiwiYWNjb3VudCI6InVhNjExMSIsIm5hbWUiOiJ1YTYifQ.6dvgI7Ss64VQ6M5vWOiYH0lB4ycjOUn41NvFB5oMk8E' \
--data-raw '{
    "account": "UA6test123",
    "password": "UA6test123",
    "currency": "CNY",
    "device": "PC",
    "lang": "zh_CN",
    "entryLotteryType": "ssc",
    "avatar": 18,
    "wallets": [
        "CNY"
    ]
}'
```
 
#### device參數


| 裝置名稱 | 代碼 | 
| -------- | -------- | 
| PC     | 0     | 
| MOBILE     | 1     | 
| IOS     | 2     | 
| ANDROID     | 3     | 
| IOS_PROFILE     | 4     | 



除了guestGameLink 及 gameLink 平台方是帶String過來外其他時候都是用數字溝通

 

#### 語系列表



|語系代號 | 語系名稱 | 
| -------- | -------- | 
| zh_CN     |簡中     | 
| en_US    | 英文    | 
| id_ID   | 印尼文    | 
| ms_MY    | 馬來文   | 
| vi_VN  | 越南文     | 
| zh_HK     | 繁中     | 
| ja_JP   | 日文     | 
| ko_KR   | 韓文     | 
| hi_IN    | 印第文     | 
| th_TH     | 泰文     | 



# Domain:  

測試：https://ig-callback.ycmpos.com
正式：https://ig-callback.yjygfwh.com

## 1. 註冊用戶

http method : POST

Path : /user/register

Header :

Authorization:{Token} // 遊戲商token

Content-Type:application/json 

RequestBody :

example


```
{
   "account":"prod1Austin",
   "password":"string",
   "showName":"Austin"
}
```
showName: 顯示名稱(現行平台account為showName加上業主名稱)

Response body :

example


```
{
    "code": 1,
    "msg": "ok"
}
```
## 2. 遊戲連結
http method : POST

Path : /user/game-link

Header :

Authorization:{Token} // 遊戲商token

Content-Type:application/json 


RequestBody :


```
{
    "account": "jeff",
    "password": "jeff1234",
    "entryLotteryId": 1,
    "currency": "CNY",
    "device": "PC",
    "lang": "zh_CN",
    "entryLotteryType": "ssc",
    "avatar": 18,
    "betEnable": true,
    "wallets": [
        "IDR",
        "BTC"
    ],
    "languages": [
        "en-us"
    ]
}
```
avatar: 頭貼id

entryLotteryId: 導向的彩種頁面，例如8為香港六合彩,選填

entryLotteryType: 導向的彩種類別頁面，例如six為六合彩,選填


currency: 當前使用幣別

wallets: 會員錢包的「所有幣別」

languages: 系統設定前台語系

betEnable: 該用戶是否可以下注

*如果entryLotteryId跟entryLotteryType都有的話則會導向的彩種頁面

Response body :


```
{
    "code": 1,
    "msg": "ok",
    "data": {
        "gameLink": "https://www.3rdlottery.com?uuid=aMMYTD8z7DeDLanQyrJ&lang=zh_CN&currency=CNY&device=0&entryLotteryType=ssc&avatar=18"
    }
}
```
平台接到gameLink後，在串接這些callback url

由於彩票是第三方單錢包遊戲，所以某些功能只有平台處會有，經由這些callback url可以導向特定的平台前台頁面

home: 平台首頁url

wallets: 錢包頁面url 

deposit: 存款頁面url

withdraw: 提款頁面url

serviceUrl: 客服頁面url (7/25新增)

## 3. 試玩連結
http method : POST

Path : /user/guest-game-link

Header :

Authorization:{Token} // 遊戲商token

Content-Type:application/json 


RequestBody :


```
{
    "entryLotteryId": 1,
    "entryLotteryType": "ssc",
    "currency": "CNY",
    "device": "PC",
    "lang": "zh_CN",
    "languages": [
        "en-us"
    ]
}
```
entryLotteryId: 導向的彩種頁面，例如8為香港六合彩,選填

entryLotteryType: 導向的彩種類別頁面，例如six為六合彩,選填

languages: 系統設定前台語系

*如果entryLotteryId跟entryLotteryType都有的話則會導向的彩種頁面

Response body :


```
{
    "code": 1,
    "msg": "ok",
    "data": {
        "gameLink": "https://www.3rdlottery.com?uuid=aMMYTD8z7DeDLanQyrJ&lang=zh_CN&currency=CNY&device=0&entryLotteryType=ssc&avatar=18"
    }
} 
```
平台接到gameLink後，在串接這些callback url讓彩票前端可以正確導向至平台特定頁面，試玩相較於正式帳號只需要平台首頁的call bakc url即可

home: 平台首頁url
serviceUrl: 客服頁面url (9/1新增)

 

## 4. 拉注單(目前平台的注單資訊是打平台交易時帶過去的，拉注單api只是資料有問題時的補拉機制)
http method : GET

Path : /game/wg-report

Header :

Authorization:{Token} // 遊戲商token

Query Param :

startTime : (必填)開始時間 10碼timestamp

endTime : (必填)結束時間 10碼timestamp

queryType: (必填)1:投注時間, 2:結算時間

pageIndex : (必填)頁碼

pageSize: (必填)單頁筆數

endTime - startTime 必須小於3600秒


Response body :


```
{
   "code":1,
   "msg":"ok",
   "data":[
     
      {
         "orderNum":"619b495f1e41b5000121a262",
         "account":"dv5austinDev1",
         "lotteryID":52,
         "gameCategoryID":12201,
         "fullName":"前后2D 一奖前2D",
         "content":"5|4@98.0",
         "originalContent":"5;4",
         "round":"202111220941",
         "number":"{\"code\":[\"5,0,7,2,2\"],\"code1\":[\"1,6,3,3,3\"],\"code2\":[\"1,3,7,8,3\",\"6,8,6,7,0\"],\"code3\":[\"6,2,0,8,4\",\"4,3,7,9,8\",\"9,0,3,7,1\",\"9,0,1,6,5\",\"4,7,3,7,3\",\"2,3,2,2,5\"],\"code4\":[\"4,1,3,1\",\"2,9,9,4\",\"0,0,5,2\",\"3,9,3,8\"],\"code5\":[\"0,9,7,5\",\"1,8,2,7\",\"2,3,7,2\",\"4,7,9,5\",\"4,4,1,1\",\"8,0,4,9\"],\"code6\":[\"9,8,0\",\"5,1,2\",\"5,2,3\"],\"code7\":[\"0,2\",\"8,6\",\"3,2\",\"4,0\"]}",
         "amountTotal":10.00000000,
         "winningAmount":0E-8,
         "waterAmount":0E-8,
         "status":1,
         "version":2,
         "createTime":1637566815,
         "validTotal":10.00000000,
         "device":0,
         "settleTime":1637566860,
         "language":"zh-cn",
         "currency":"CNY",
         "possibleWinningAmount":970.000000000,
         "odds":[
            98.0
         ]
      },
      {
         "orderNum":"619b49791e41b5000121a26d",
         "account":"dv5austinDev1",
         "lotteryID":48,
         "gameCategoryID":13101,
         "fullName":"组选 二中二",
         "content":"12@15.68,13@15.68",
         "originalContent":"12,13",
         "round":"20211126",
         "number":"",
         "amountTotal":10.00000000,
         "winningAmount":0E-8,
         "waterAmount":0E-8,
         "status":0,
         "version":1,
         "createTime":1637566841,
         "validTotal":0E-8,
         "device":0,
         "settleTime":0,
         "language":"zh-cn",
         "currency":"CNY",
         "possibleWinningAmount":146.8000000000,
         "odds":[
            15.68
         ]
      }
   ]
}
```
account: 玩家帳號

orderNum: 注單號

device: 投注裝置 PC=0, MOBILE=1, IOS=2, ANDROID=3,IOS_PROFILE=4

lotteryID: 彩票代碼

round: 期號

number: 開獎號碼   格式問題可以看 1. 交易申請(餘額變更) - 接入方實作這之api

fullName:玩法名稱

amountTotal: 投注額

content: 投注內容

winningAmount: 派彩金額

waterAmount: 退水金額

validTotal: 有效投注額

status: 注單狀態 0-未結算,1-已結算, 3-後台註銷,8-撤銷,10-派彩異常

version: 注單版本號

createTime: 投注時間

settleTime: 結算時間, 未結算為0

currency: 幣別:CNY,IDR,MYR,VND

possibleWinningAmount: 玩家單注可贏額

gameCategoryID: 玩法代碼

odds: 賠率陣列

originalContent: 原始的投注內容




## 5. 取得彩票方玩法i18n

http method : GET

Path : /game/type

Header :

Authorization:{Token} // 遊戲商token

煩請貴平台重啟專案時彩這隻api更新i18值


Response body :


```
{
   "code":1,
   "msg":"ok",
   "data":{
      "12205":{
         "en-us":"2 Digits - Last 2D 7th Prize",
         "zh-hk":"前後2D 七獎後2D",
         "ko-kr":"앞뒤2개씩 - 7등의 뒤숫자2개",
         "ms-my":"2 Digit - 2D Belakang Hadiah Ketujuh",
         "id-id":"2 Digit - Last 2D Hadiah Ke-7",
         "hi-in":"2 डी से पहले और बाद में 2 डी 2 डी",
         "zh-cn":"前后2D 七奖后2D",
         "vi-vn":"Đánh Đề - Đề Giải 7",
         "ja-jp":"2桁 - 最後の2D 7賞",
         "th-th":"หน้าและหลัง 2D - 2D หลังจากเจ็ดรางวัล"
      },
      "4052":{
         "en-us":"Second Ball, Single Number",
         "zh-hk":"第二球 單码",
         "ko-kr":"두 번째 공, 단일 번호",
         "ms-my":"Bola Kedua, Nombor tunggal",
         "id-id":"Bola Kedua, Nomor tunggal",
         "hi-in":"दूसरी गेंद, सिंगल नंबर",
         "zh-cn":"第二球 單码",
         "vi-vn":"Bóng thứ hai, Đơn số",
         "ja-jp":"セカンドボール、シングルナンバー",
         "th-th":"ลูกที่สองหมายเลขเดียว"
      },
      "4054":{
         "en-us":"Second Ball, Two Sides",
         "zh-hk":"第二球兩面",
         "ko-kr":"두 번째 공, 양면",
         "ms-my":"Bola Kedua, Dua Belah",
         "id-id":"Bola Kedua, Dua Sisi",
         "hi-in":"दूसरी बॉल, टू साइड्स",
         "zh-cn":"第二球 两面",
         "vi-vn":"Bóng thứ hai, Hai bên",
         "ja-jp":"セカンドボール、両面",
         "th-th":"ลูกที่สองสองด้าน"
      },
      "4056":{
         "en-us":"Second Ball, Position",
         "zh-hk":"第二球方位",
         "ko-kr":"두 번째 공, 위치",
         "ms-my":"Bola Kedua, Jawatan",
         "id-id":"Bola Kedua, Posisi",
         "hi-in":"दूसरी गेंद, स्थिति",
         "zh-cn":"第二球 方位",
         "vi-vn":"Bóng thứ hai, Chức vụ",
         "ja-jp":"セカンドボール、ポジション",
         "th-th":"ลูกที่สองตำแหน่ง"
      },
      "4058":{
         "en-us":"First Ball, Number range",
         "zh-hk":"第一球中發白",
         "ko-kr":"첫 번째 공, 번호 범위",
         "ms-my":"Bola Pertama, Julat nombor",
         "hi-in":"पहली गेंद, संख्या सीमा",
         "id-id":"Bola Pertama, Rentang angka",
         "vi-vn":"Bóng đầu tiên, Dãy số",
         "zh-cn":"第一球 中发白",
         "ja-jp":"最初のボール,番号の範囲",
         "th-th":"ลูกแรกช่วงหมายเลข"
      },
      "4059":{
         "en-us":"Fourth Ball, Number range",
         "zh-hk":"第四球中發白",
         "ko-kr":"넷째 공, 숫자 범위",
         "ms-my":"Bola Keempat, Julat nombor",
         "hi-in":"चौथी बॉल, नंबर रेंज",
         "id-id":"Bola Keempat, Rentang angka",
         "vi-vn":"Bóng thứ tư, Dãy số",
         "zh-cn":"第四球 中发白",
         "ja-jp":"4番目のボール,番号の範囲",
         "th-th":"ลูกที่สี่ช่วงหมายเลข"
      },
      "4060":{
         "en-us":"Sixth Ball, Number range",
         "zh-hk":"第六球中發白",
         "ko-kr":"여섯 번째 공, 숫자 범위",
         "ms-my":"Bola Keenam, Julat nombor",
         "hi-in":"छठी गेंद, संख्या सीमा",
         "id-id":"Bola Keenam, Rentang angka",
         "vi-vn":"Bóng thứ sáu, Dãy số",
         "zh-cn":"第六球 中发白",
         "ja-jp":"6番目のボール,番号の範囲",
         "th-th":"ลูกที่หกช่วงหมายเลข"
      }
   }
}
```
數字部分為下注時會帶的gameCategoryID

## 6. 取得彩票方投注內容i18n
http method : GET

Path : /game/content

Header :

Authorization:{Token} // 遊戲商token

煩請貴平台重啟專案時彩這隻api更新i18值

Response body :


```
{
   "code":1,
   "msg":"ok",
   "data":{
      "zh-hk":{
         "10240":"41",
         "10241":"42",
         "10242":"43",
         "10243":"44",
         "10244":"45",
         "10245":"46",
         "6":"萬位,千位,百位,十位,個位",
         "10246":"47",
         "10247":"48",
         "4104":"大,小,單,雙,合單,合雙,尾大,尾小,龍,虎",
         "10248":"49",
         "4106":"東,南,西,北",
         "4108":"中",
         "13":"萬位,千位,百位,十位,個位",
         "4109":"發",
         "14":"萬位,千位,百位,十位,個位",
         "4110":"白",
         "21":"大,小,單,雙,大單,小單,大雙,小雙,極大,極小,紅波,藍波,綠波,豹子,對子,順子",
         "28":"豹子,順子,對子",
         "29":"豹子,順子,對子"
      }
   }
}
```
彩票方會提供content i18n轉換的code放在各平台中，需要傳入下注時所帶的originalContent,lotteryID,gameCategoryID

## 7. 取得彩票方彩種i18n
http method : GET

Path : /game/lottery

Header :

Authorization:{Token} // 遊戲商token


Response body :


```
{
   "code":1,
   "msg":"ok",
   "data":{
      "65":{
         "zh-hk":{
            "lotteryID":65,
            "name":"達農",
            "remark":"每週六開獎",
          
         },
         "zh-cn":{
            "lotteryID":65,
            "name":"达农",
            "remark":"每周六开奖",
           
         }
      },
      "66":{
         "zh-hk":{
            "lotteryID":66,
            "name":"昆嵩",
            "remark":"每週日開獎",
            
         },
         "zh-cn":{
            "lotteryID":66,
            "name":"昆嵩",
            "remark":"每周日开奖",
            
         }
      },
      "67":{
         "zh-hk":{
            "lotteryID":67,
            "name":"中越5分彩",
            "remark":"每5分鐘一期",
            
         },
         "zh-cn":{
            "lotteryID":67,
            "name":"中越5分彩",
            "remark":"每5分钟一期",
       
         }
      },
      "68":{
         "zh-hk":{
            "lotteryID":68,
            "name":"中越1分彩",
            "remark":"每1分鐘一期",
        
         },
         "zh-cn":{
            "lotteryID":68,
            "name":"中越1分彩",
            "remark":"每1分钟一期",
          
         }
      }
   }
}
```
lotteryID: 彩種ID

name: 彩種名稱

remark: 彩種說明


## 8. 踢用戶
http method : PUT

Path : /user/kick

Header :

Authorization:{Token} // 遊戲商token

Content-Type:application/json 

RequestBody :

example


```
{
   "devices":[0,1],
   "userNames":["devAustin","devAustin1"]
}
```
devices:裝置，mapping詳見上面的裝置列表

userNames: 用戶名稱

這兩個參數擇期一傳就可以了，如果傳device代表踢除某業主某裝置下的所有會員；傳userNames代表踢除某業主下的特定會員

Response body :

example


```
{
    "code": 1,
    "msg": "ok"
}
```

## 9. 該業主已開啟彩種類別

http method : GET

Path : /game/openLotteryType

Header :

Authorization:{Token} // 遊戲商token
Accept-Language:{language} //語系

Response body :

```
{
    "code": 1,
    "msg": "ok",
    "data": [
        {
            "lotteryTypeId": 622,
            "lotteryTypeCode": "nvn",
            "lotteryTypeName": "越南北部"
        },
        {
            "lotteryTypeId": 623,
            "lotteryTypeCode": "mvn",
            "lotteryTypeName": "越南中部"
        },
        {
            "lotteryTypeId": 113,
            "lotteryTypeCode": "syxw",
            "lotteryTypeName": "11选5"
        },
        {
            "lotteryTypeId": 229,
            "lotteryTypeCode": "klsf",
            "lotteryTypeName": "快乐十分"
        },
        {
            "lotteryTypeId": 251,
            "lotteryTypeCode": "k3",
            "lotteryTypeName": "快三"
        }
    ]
}
```
這些參數代表的是彩種類別跟gamelink的entryLotteryType是一樣的，這些彩種類別在現行平台是在打gameLink之前會先打的，目前只有在pc有做，wap跟移動端沒有這一頁


| id  | code | name |
| -------- | -------- | -------- |
| 1     | ssc     | 时时彩     |
| 65     | pk10     | PK10    |
| 113     | syxw     | 11选5     |
| 229     | klsf     | 快乐十分     |
| 251     | k3     | 快三    |
| 271     | 3d     | 3D     |
| 298     | kl8     | 快乐8    |
| 334     | six     | 六合彩    |
| 621     | pcdd     | 幸运28    |
| 622     | nvn     | 越南北部    |
| 623     | mvn     | 	越南中部  |   |
| 624     | svn     | 越南南部    |


## 10. 該業主已開啟彩種
http method : GET

Path : /game/openLottery

Header :

Authorization:{Token} // 遊戲商token
Accept-Language:{language} //語系


Response body :

```
{
    "code": 1,
    "msg": "ok",
    "data": [
        {
            "lotteryId": 44,
            "lotteryCode": "privjsk3",
            "lotteryName": "极速快三",
            "lotteryTypeId": 251,
            "lotteryTypeCode": "k3",
            "lotteryTypeName": "快三"
        },
        {
            "lotteryId": 2,
            "lotteryCode": "bjkl8",
            "lotteryName": "快乐8",
            "lotteryTypeId": 298,
            "lotteryTypeCode": "kl8",
            "lotteryTypeName": "快乐8"
        },
        {
            "lotteryId": 37,
            "lotteryCode": "privjsklsf",
            "lotteryName": "极速快乐十分",
            "lotteryTypeId": 229,
            "lotteryTypeCode": "klsf",
            "lotteryTypeName": "快乐十分"
        },
        {
            "lotteryId": 43,
            "lotteryCode": "privk3",
            "lotteryName": "快三",
            "lotteryTypeId": 251,
            "lotteryTypeCode": "k3",
            "lotteryTypeName": "快三"
        },
        {
            "lotteryId": 38,
            "lotteryCode": "privklsf",
            "lotteryName": "快乐十分",
            "lotteryTypeId": 229,
            "lotteryTypeCode": "klsf",
            "lotteryTypeName": "快乐十分"
        }
    ]
}
```

# 接入方實作
## 1. 交易申請(餘額變更) - 接入方實作
#### 提交會員餘額變更申請及對應的注單號，單筆交易的狀態都會是一樣的且會是同一個user同一個幣別

http method : POST

Header :

Authorization:{Token} // 業主token

RequestBody :

下注


``` java
{
    "account": "devaustintestCNY1",
    "transferType": -1,
    "transferID": "5fc759c445028f72c089ee5f",
    "amountTotal": 20,
    "orderNum": [
        "5fc759c445028f72c089ee60"
    ],
    "orderList": [{
        "orderNum": "5fc759c445028f72c089ee60",
        "account": "devaustintestCNY1",
        "lotteryID": 32,
        "gameCategoryID":11096,
        "round": 202012021030,
        "roundTime": 1599445500,
        "amountTotal": 20,
        "status": 0,
        "version": 1,
        "device": 1,
        "originalContent":"0",
        "possibleWinningAmount": 48.00,
        "createTime": 1610098267,
        "fullName":"總和單",
        "content":"單@1.96",
        "currency": "CNY",
        "lang":"zh-cn",
        "odds":[
          48.02
         ],
        "balanceChanged": true
    }],
    "balanceInquired": 1,
    "currency": "CNY"
}
```

撤銷


```
{
    "account": "devaustintestCNY1",
    "transferType": -1,
    "transferID": "5fc87760b634ff88f801dbfb",
    "amountTotal": 19.60000,
    "orderNum": [
        "5fc8585a2015020001e20037"
    ],
    "balanceInquired": 1,
    "orderList": [{
        "orderNum": "5fc8585a2015020001e20037",
        "number": "",
        "winningAmount": 0,
        "waterAmount": 0,
        "version": 3,
        "validTotal": 0,
        "settleTime": 0,
        "validTotalChange": -100.00000,
        "status": 2,
        "balanceChanged": true
    }],
    "currency": "CNY"
}
```

註銷


```
{
    "account": "devaustintestCNY1",
    "transferType": 1,
    "transferID": "5fc87aa4b634ff88f801dc31",
    "amountTotal": 20.00000,
    "orderNum": [
        "5fc8585a2015020001e20037"
    ],
    "balanceInquired": 1,
    "orderList": [{
        "orderNum": "5fc8585a2015020001e20037",
        "payAmount": 20.00000,
        "gameStatus": 0,
        "status": 3,
        "version": 4,
        "balanceChanged": true
    }],
    "currency": "CNY"
}
```

 

結算


```
{
    "account": "dv2austintestIDR1",
    "transferType": 1,
    "transferID": "5febed40e1319c441ccfc069",
    "amountTotal": 19.60000,
    "orderNum": ["5febecbafa4a640001f07143"],
    "orderList": [{
        "orderNum": "5febecbafa4a640001f07143",
        "number": "5,3,0,2,6",
        "winningAmount": 19.60, 和局:0
        "waterAmount": 0.00,
        "payAmount": 19.60,
        "gameStatus": 1,
        "status": 1,
        "validTotal": 10.00000, 和局:0E-8
        "validTotalChange": 10.00000, 和局:0E-8
        "settleTime": 1610098384,
        "version": 2,
        "balanceChanged": true  和局:true
    }],
    "balanceInquired": 1,
    "currency": "CNY"
}
```

account:會員帳號

transferType: 1:會員餘額增加,-1:會員餘額減少

amountTotal: 該交易單餘額變更量

transferID: 交易單ID

balanceInquired: 1:返回該會員交易完成後的餘額. 0:不用返回餘額

currency: 幣別

orderNum:注單單號列表

orderList:  注單資訊    
         &emsp; orderNum: 注單單號
         &emsp; account:會員帳號
         &emsp; lotteryID:彩種ID
         &emsp; gameCategoryID:玩法ID
         &emsp; round:期號
         &emsp; amountTotal:該張注單金額
         &emsp; payAmount:平台派彩金額
         &emsp; gameStatus: 遊戲狀態 0-輸,1-贏, 2-和,
         &emsp; status: 注單狀態 0-未結算,1-已結算, 2-後台撤銷, 3-後台註銷,
         &emsp; version:版本號
         &emsp; device:投注裝置 投注裝置 PC=0, MOBILE=1, IOS=2, ANDROID=3,IOS_PROFILE=4
         &emsp; originalContent:原始投注內容
         &emsp; possibleWinningAmount:最大可贏金額
         &emsp; createTime:下注時間
         &emsp; currency:該注單幣別
         &emsp; odds:賠率陣列，因應多賠率系列玩法
         &emsp; balanceChanged:餘額是否有更改
         &emsp; fullName:玩法語系依下注語系
         &emsp; content:投注內容語系依下注語系
         &emsp; lang:下注時玩家語系

  winningAmount: 派彩金額(不含退水)
  waterAmount: 退水金額
  validTotal: 有效投注額(不包含和局)
  validTotalChange: 有效投注額變化
  settleTime: 結算時間

撤銷:取消派彩
註銷:取消注單

     
      
 越南彩系列開獎號碼，code 是特獎，code1 ~ code7 是一獎 ~ 七獎




```
{
  "code": [
    "7,6,3,4,9"
  ],
  "code1": [
    "6,8,4,0,0"
  ],
  "code2": [
    "1,6,4,7,5",
    "5,0,7,0,6"
  ],
  "code3": [
    "3,3,9,1,2",
    "6,6,5,6,6",
    "2,2,8,8,3",
    "5,1,6,7,6",
    "4,5,1,0,5",
    "3,2,1,8,2"
  ],
  "code4": [
    "0,8,6,9",
    "0,3,6,0",
    "0,1,7,9",
    "8,4,0,5"
  ],
  "code5": [
    "2,8,5,0",
    "6,2,7,2",
    "4,0,4,3",
    "5,3,8,6",
    "6,4,2,3",
    "4,2,3,1"
  ],
  "code6": [
    "2,8,7",
    "8,6,5",
    "2,9,2"
  ],
  "code7": [
    "9,7",
    "2,6",
    "4,2",
    "3,3"
  ]
}
```
 

Response body :

請求時 balanceInquired = 0


```
{
    "code":1,
    "msg":"ok"
}
```
 請求時 balanceInquired = 1


```
{
    "code":1,
    "msg":"ok",
    "data":{
        "balance":123.00
    }
}
```
code: 0-交易失敗,1-交易成功,2-餘額不足,3-重複訂單

## 2. 餘額查詢 - 接入方實作
#### 查詢會員餘額

http method : GET

Header :

Authorization:{Token} // 會員餘額查詢token,這個token為下面的JWt body 加彩票方給的業主所屬的salt加密而成

JWT body:

```
{
    "account":"dv3austinDev1",
    "loginTime":1637042764,
    "platformName":"aaaDev3",
    "exp": 1637048164
}
```
加密範例
source:

```
{
  "account": "dv3austinDev1",
  "loginTime": 1640065357,
  "platformName": "innoDev3",
  "exp": 1640070757
}
```

salt:FlNmflcmC9zhcLnSiQ6

其中JWT加密的演算法為SHA-256

result:
eyJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50IjoiZHYzYXVzdGluRGV2MSIsImxvZ2luVGltZSI6MTY0MDA2NTM1NywicGxhdGZvcm1OYW1lIjoiaW5ub0RldjMiLCJleHAiOjE2NDAwNzA3NTd9.invmuhnZj1fl3g3eJpSOYc3iBuul6FpL7fnUng1XXdQ


platformName: 平台名稱，目前貴業主設定名稱為 ua6
exp:該user的loginTime+90分鐘
這兩個參數我看我們現行平台的code都沒有用到，只有接到我們請求時把請求印下來而已
現行平台是用account dv3austinDev1 的前三個字串來判斷業主的，判斷以前可能是用platformName來判斷業主的

sample在最下面

Response body :


```
{
   "msg":"",
   "code":1,
   "data":{
      "wallets":[
         {
            "balance":"99895800.80",
            "currency":"CNY",
            "display":true
         },
         {
            "balance":"0.00",
            "currency":"IDR",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"MYR",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"VND",
            "display":false
         },
         {
            "balance":"9988244.75",
            "currency":"USDT_ERC20",
            "display":true
         },
         {
            "balance":"0.00",
            "currency":"USD",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"HKD",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"THB",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"JPY",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"KRW",
            "display":false
         },
         {
            "balance":"0.00",
            "currency":"INR",
            "display":false
         }
      ]
   }
}
code10203為token expire
```
balance: 餘額

currency: 幣別

display: 該用戶所有有的前包為true,該用戶沒有擁有的錢包為false

## 3. 更換大頭貼 - 接入方實作
#### 更換會員大頭貼，大頭貼的代碼所對應的圖片現在是寫死在前端的

http method : PUT

Header :

Authorization:{Token} // 業主token

Request body :

```
{
    "account":"dv3austinDev1",
    "avatar":1
}
```

Response body :


```
{
   "msg":"",
   "code":1,
   "data":{
      
   }
}
```
## 4. 更換錢包 - 接入方實作

http method : PUT

Header :

Authorization:{Token} // 業主token

Request body :


```
{
    "account":"dv3austinDev1",
    "active":CNY
}
```

active: 啟用錢包名稱

Response body :


```
{
   "msg":"",
   "code":1,
   "data":{
      "balance":"99895840.00210544"
   }
}
```
## 5. 確認平台方交易是否完成 - 接入方實作
#### 打交易平台回應逾時等不可知平台交易是否成功的狀態下彩票會打此api跟平台確認該交易是否成功
彩票後端傳的url 會像這樣/checkTransaction/{account}/{transferID}

http method : GET

Header :

Authorization:{Token} // 業主token

Path Param :

account: 玩家帳號

transferID: 交易單號

Response body :


```
{
    "code":3,
    "msg":"transfer id already exist",
}
```
code: 3-該交易存在於平台,8-該交易不存在於平台, 0-該查詢失敗，平台出exception之類的


## 餘額查詢 - sample code

```
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.lang.reflect.Field;
import java.time.Instant;
public class JwtVerifyTest {


    public static void main(String[] args) {
        String key = "FlNmflcmC9zhcLnSiQ6";

        long now = Instant.now().getEpochSecond();
        SWToken swtoken = new SWToken("dev111", now,"dev",now+5400);
        String token = generateToken(swtoken,key);

        try{
            Claims claims =  getClaimByToken(token,key);
            System.out.println(claims.get("account"));
        }catch(Exception e){
            e.printStackTrace();
        }
    }


    public static Claims getClaimByToken(String token, String signingKey) {
        return Jwts.parser()
                .setSigningKey(signingKey)
                .parseClaimsJws(token)
                .getBody();
    }

    public static String generateToken(SWToken swToken, String swSignKey) {
        JwtBuilder builder = Jwts.builder();
        try {
            for (Field field : swToken.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                builder.claim(field.getName(), field.get(swToken));
            }
            return builder.signWith(SignatureAlgorithm.HS256, swSignKey).compact();
        } catch (IllegalAccessException e) {
           e.printStackTrace();
            return null;
        }
    }
}
class SWToken {

    public SWToken(String account, long lastLoginTime, String platformName, long expireSeconds) {
        this.account = account;
        this.loginTime = lastLoginTime;
        this.platformName = platformName;
        this.exp = lastLoginTime + expireSeconds;
    }

    private String account;
    private long loginTime;
    private String platformName;
    private long exp;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public long getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(long loginTime) {
        this.loginTime = loginTime;
    }

    public String getPlatformName() {
        return platformName;
    }

    public void setPlatformName(String platformName) {
        this.platformName = platformName;
    }

    public long getExp() {
        return exp;
    }

    public void setExp(long exp) {
        this.exp = exp;
    }
}
```

## 投注內容轉換util - sample code

```
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.testng.util.Strings;

import java.math.BigDecimal;
import java.util.*;


@Component
public class LotteryBillTransferUtil {

    private  Map<String, Map<Integer, String>> gameContentMap;



    private final String SEMICOLON = ";";
    private final String PIPELINE = "|";
    private final String PIPELINE_WITH_SPACE = " | ";
    private final String COMMA = ",";
    private final String VERSUS = " vs ";
    private final String EMPTY = "";
    private final String CN = "zh-cn";

    private final List<Integer> convertBillContentGameCategoriesList = Arrays.asList(9502,9504,9506,9508,9510,9512,9514,9516,9518,9520,9552,9554,9556,9558,9560,9562,9564);

    List<Integer> SIX_GAMES = Arrays.asList(8,16,36);

    //todo init時請求彩票api /game/content 或是彩票方有變動時打api予貴平台，貴平台重打一次彩票的api

    public void setGameContentMap(Map<String, Map<Integer, String>> gameContentMap){
        this.gameContentMap = gameContentMap;
    }


    public String buildContent(Integer lotteryID, Integer gameCategoryID, String billContent, String language) throws Exception {
        String gameContent;
        try {
            gameContent = SIX_GAMES.contains(lotteryID)
                    ? buildSixContent(gameCategoryID, billContent, language)
                    : buildContent(gameCategoryID, billContent, language);

            if(null==gameContent){
                gameContent="";
            }
        } catch (Exception e) {
            String exceptionInfo = e.getClass().getSimpleName() + ": " + e.getMessage() + ", last call stack: " + e.getStackTrace()[0];
            String errMsg = String.format(
                    "catch exception at 'buildContent', " +
                            "lotteryID: %d, gameCategoryID: %d, billContent: %s, gameFullName: %s, language: %s, errMsg: %s\n",
                    lotteryID, gameCategoryID, billContent, language, exceptionInfo
            );
            throw new Exception(errMsg);// 拋出此例外，讓外層接到方能略過此筆注單
        }
        return gameContent.replaceAll(SEMICOLON, PIPELINE);
    }

    private String buildContent(Integer gameCategoryID, String billContent, String language) {
        // billContent 為空字串，即為平攤過的玩法
        if (StringUtils.isBlank(billContent)) {
            return getGameContentDescription(gameCategoryID, language);
        } else {
            String gameContent = getGameContentDescription(gameCategoryID, language);
            switch (gameCategoryID) {

                case 2201://pk10 lhd
                    return pk10LongHuDou(billContent, gameContent, language);
                case 3101: // 11選五龍虎鬥
                case 4401:
                    return longHu(billContent, gameContent, language, true);
                case 5102:
                    return k3Butong(billContent);
                case 5104:
                    return k3Duizi(billContent);
                case 5152:
                    return k3Danxuan(billContent);
                case 5351:
                    return k3PairAndSingle(billContent);
                default:
                    if (StringUtils.isBlank(gameContent)) return billContent;
                    return convertBillContentWithSemicolon(billContent, gameContent);
            }
        }
    }

    /**
     * 生成 K3 二同單選玩法的下注內容轉譯
     * ex: 1;2 -> 11|2
     * ex: 1,2;3,4 -> 11,22|3,4
     */
    private String k3PairAndSingle(String billContent) {
        StringBuilder result = new StringBuilder();
        String[] pairAndSingle, pairs, singles;
        pairAndSingle = billContent.split(SEMICOLON);
        pairs = pairAndSingle[0].split(COMMA);
        singles = pairAndSingle[1].split(COMMA);
        // pair concat
        for (String pair : pairs) result.append(pair).append(pair).append(COMMA);
        result.deleteCharAt(result.lastIndexOf(COMMA));
        // separate pair and single
        result.append(PIPELINE);
        // single concat
        for (String single : singles) result.append(single).append(COMMA);
        result.deleteCharAt(result.lastIndexOf(COMMA));
        return result.toString();
    }

    private String buildSixContent(Integer gameCategoryID, String billContent, String language) {

        String gameContent = getGameContentDescription(gameCategoryID, language);

        if(convertBillContentGameCategoriesList.contains(gameCategoryID)){
            return convertBillContent(billContent, gameContent);
        }

        if(Strings.isNullOrEmpty(gameContent)){
            return billContent;
        }
        return gameContent;
    }

    private String longHu(String billContent, String gameContent, String language, boolean isCredit) {

        String[] gameContents = StringUtils.split(gameContent, COMMA);
        String[] billContents = StringUtils.split(billContent, SEMICOLON);

        List<String> transferContents = new ArrayList<>();
        for(String ele : billContents) {
            StringBuilder transferContent = new StringBuilder();
            String[] eleArr = StringUtils.split(ele, COMMA);
            int firstEle = Integer.parseInt(eleArr[0]);
            int secondEle = Integer.parseInt(eleArr[1]);
            if (firstEle < secondEle) {
                transferContent.append(gameContents[firstEle]).append(VERSUS).append(gameContents[secondEle]).append(" ");
                if (isCredit) {
                    transferContent.append("DRAGON"); // 時時彩信用盤有和
                } else {
                    transferContent.append(gameContents[firstEle]);
                }
            } else if (firstEle > secondEle) {
                transferContent.append(gameContents[secondEle]).append(VERSUS).append(gameContents[firstEle]).append(" ");
                if (isCredit){
                    transferContent.append("TIGER"); // 時時彩信用盤有和
                } else {
                    transferContent.append(gameContents[firstEle]);
                }
            }
            transferContents.add(transferContent.toString());
        }

        return String.join(PIPELINE_WITH_SPACE, transferContents);
    }

    /**
     * pk10龍虎鬥, 因為跟時時彩的龍虎鬥content轉換method差異過大，所以另開方法
     *
     * @param billContent
     * @return
     */
    private String pk10LongHuDou(String billContent, String gameContent, String language) {
        StringBuilder contentBuilder = new StringBuilder();
        contentBuilder.append(billContent);
        List<String> transferContents = new ArrayList<>();
        String[] gameContents = StringUtils.split(gameContent, COMMA);

        for(int i = 0; i < gameContents.length; i++){
            String betItem = StringUtils.substringBefore(contentBuilder.toString(), SEMICOLON);
            String[] betItemArray = betItem.split(COMMA);
            if(!betItem.equals("")){
                for(int j = 0 ;j < betItemArray.length ; j++){
                    StringBuilder transferContent = new StringBuilder();
                    transferContent.append(gameContents[i]);
                    transferContent.append(betItemArray[j].equals("1")
                            ? "DRAGON"
                            : "TIGER");
                    transferContents.add(transferContent.toString());
                }
            }
            contentBuilder.delete(0, 1 + betItem.length());
        }

        return String.join(PIPELINE_WITH_SPACE, transferContents);
    }

    private String k3Butong(String content) {
        return content.replace(COMMA, EMPTY).replace(SEMICOLON, COMMA);
    }

    private String k3Duizi(String billContent) {
        return convertBillContentByMultiply(billContent, 11);
    }

    private String k3Danxuan(String billContent) {
        return convertBillContentByMultiply(billContent, 111);
    }

    /**
     * 將billContent內儲存的數字乘上數字
     *
     * @param billContent Bill資料表存的content資料(數字)
     * @param multiplier 乘數
     * @return String
     */
    private String convertBillContentByMultiply(String billContent, int multiplier) {
        String[] contents = billContent.split(COMMA);
        StringBuilder sb = new StringBuilder();
        for(String num : contents) {
            if(sb.length() > 0) {
                sb.append(COMMA);
            }
            sb.append(Integer.parseInt(num) * multiplier);
        }
        return sb.toString();
    }

    /**
     * 將billContent內儲存的數字轉換為gameContent的文字(多語系)
     *
     * @param billContent Bill資料表存的content資料(數字)
     * @param gameContent gameContent資料表存的content資料(文字)
     * @return String
     */
    private String convertBillContent(String billContent, String gameContent) {
        StringBuilder sb = new StringBuilder();
        String[] billContents = billContent.split(COMMA);
        String[] gameContents = gameContent.split(COMMA);
        int billContentsLength = billContents.length;
        int lastBillContentLength = billContents.length - 1; // 最後一個字的位置
        for (int i = 0; i < billContentsLength; ++i) {
            int num = Integer.parseInt(billContents[i]);
            sb.append(gameContents[num]);
            if (lastBillContentLength != i) { // 最後一個字後面不加COMMA
                sb.append(COMMA);
            }
        }
        return sb.toString();
    }

    /**
     * 用在會有用;符號分隔的billContent資料, 列如 (0,1,2,3;11,12,13) 轉成 (大,小,单,双;蓝波,绿波,豹子)
     * 將billContent內儲存的數字轉換為gameContent的文字(多語系)
     *
     * @param billContent Bill資料表存的content資料(數字)
     * @param gameContent gameContent資料表存的content資料(文字)
     * @return String
     */
    private String convertBillContentWithSemicolon(String billContent, String gameContent) {
        // 因應pk10冠亞和wap有送content, app沒送
        if (billContent.equals(gameContent)){
            return billContent;
        }
        String[] digits = StringUtils.split(billContent, SEMICOLON);
        int count = 0;
        StringBuilder content = new StringBuilder();
        for (String digit : digits) {
            content.append(convertBillContent(digit, gameContent));
            count++;
            if (count < digits.length) {
                content.append(SEMICOLON);
            }
        }
        return content.toString();
    }


    /**
     * 取得遊戲內容描述, 若查找的語系不存在, 預設返回簡體中文描述
     *
     * @param gameCategoryID 遊戲ID
     * @param language 語系
     * @return 遊戲內容描述
     */
    public String getGameContentDescription(Integer gameCategoryID, String language) {

        Map<Integer, String> gameTypeMap = gameContentMap.get(language);

        return Objects.nonNull(gameTypeMap)
                ? gameTypeMap.get(gameCategoryID)
                : gameContentMap.get(CN).get(gameCategoryID);
    }

    public String contentCombineOdd(String content, List<BigDecimal> odds, int gameCategoryID) {
        String odd=listToString(odds,',');
        // 3D二字定位&三字定位特殊處理
        if (gameCategoryID == 6051 || gameCategoryID == 6101) {
            content = content.replace(COMMA, "@" + odd + SEMICOLON)+ "@" + odd;
            return content;
        } else {
            return content + "@" + odd;
        }
    }

    public static String listToString(List list, char separator) {
        return StringUtils.join(list.toArray(), separator);
    }

}
```