POST Login
Description
ใช้สำหรับ login เข้าระบบและรับ JWT Token สำหรับใช้งาน API อื่นๆ
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | String | Required | Username สำหรับ login |
| password | String | Required | Password สำหรับ login |
| pin | String | Required | PIN 6 หลัก |
Request Example
{
"username": "bot0860",
"password": "aa112233++",
"pin": "123456"
}
Response Example
{
"jwt": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "bot0860",
"message": "Login สำเร็จสำหรับ bot0860"
}
POST Login Multiple Accounts
Description
Login หลาย accounts พร้อมกันและบันทึก JWT Token ของแต่ละ account
Request Body
ส่งเป็น object ที่มี key เป็นชื่อที่ต้องการ และ value เป็นข้อมูล login
Request Example
{
"a": {
"username": "zds88zzq",
"password": "aa112233++",
"pin": "445566"
},
"b": {
"username": "zds88zzr",
"password": "bb112233++",
"pin": "556677"
}
}
GET JWT List
Description
แสดงรายการ JWT Token ทั้งหมดที่มีในระบบ
Response Example
{
"success": true,
"count": 2,
"jwtTokens": [
{
"username": "bot0860",
"hasToken": true,
"tokenPreview": "***...last20chars",
"timestamp": "2024-06-06T10:30:00.000Z"
}
],
"message": "พบ JWT Token 2 รายการ"
}
POST JWT Refresh
Description
ต่ออายุ access token ทันทีผ่าน OIDC refresh grant โดยไม่ต้องกรอกรหัสผ่านใหม่
ปกติระบบต่ออายุให้อัตโนมัติอยู่แล้วเมื่อ token ใกล้หมด เส้นนี้มีไว้สำหรับกรณีที่ automation ครอบคลุมไม่ได้ — อยากได้เวลาเต็มชั่วโมงก่อนเริ่มงานยาว หรืออยากรู้เดี๋ยวนี้ว่า refresh token ยังใช้ได้ไหม แทนที่จะไปรู้ตอนรายงานล้ม
Request Body
{ "username": "sbobetonline168" }
เว้น body ว่างได้ = ต่ออายุ token ตัวแรกที่เจอในระบบ
Response Example
{
"success": true,
"usedUsername": "sbobetonline168",
"expiresAt": "2026-07-29T12:56:35Z",
"message": "ต่ออายุ token ของ sbobetonline168 เรียบร้อย"
}
ได้ 401 พร้อม refreshable: false
เมื่อ refresh token ใช้ไม่ได้แล้ว — กรณีนั้นต้องล็อกอินใหม่เท่านั้น
DELETE JWT Clear
Description
ลบ JWT Token ออกจากระบบ
Request Body (Optional)
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | String | Optional | ระบุเพื่อลบเฉพาะ JWT ของ username นั้น (ถ้าไม่ระบุจะลบทั้งหมด) |
POST Create Member
Description
สร้าง Member account ใหม่
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| userName | String | Required | Username ที่ต้องการสร้าง |
| password | String | Optional | Password (default: "123123123Zz@@") |
| firstName | String | Optional | ชื่อ (default: "ABC") |
| lastName | String | Optional | นามสกุล (default: "Xenter") |
| useUsername | String | Optional | ระบุ username ที่ต้องการใช้ JWT |
Request Example
{
"userName": "testuser001",
"password": "Test@123456",
"firstName": "John",
"lastName": "Doe",
"useUsername": "bot0860"
}
POST Create Bulk Members
Description
สร้าง Member accounts หลายๆ account พร้อมกัน (สูงสุด 100 accounts)
Request Example
{
"userNames": ["zds88zz001", "zds88zz002", "zds88zz003"],
"password": "123123123Zz@@",
"firstName": "ABC",
"lastName": "Xenter",
"useUsername": "bot0860"
}
POST Change Password
Description
เปลี่ยนรหัสผ่านของ Member
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerId | Number | Required | Customer ID ที่ต้องการเปลี่ยนรหัสผ่าน |
| password | String | Required | รหัสผ่านใหม่ |
| accountId | String | Optional | Account ID (ถ้าไม่ใส่จะใช้ customerId) |
Request Example
{
"customerId": 35688234,
"accountId": "zds88zz960",
"password": "@@123123Zz",
"useUsername": "bot0860"
}
POST Suspend Account
Description
ล็อค/ปลดล็อค account
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerId | Number | Required | Customer ID ที่ต้องการ suspend |
| isSuspended | Boolean | Required | true = ล็อค, false = ปลดล็อค |
Request Example
{
"customerId": 35688231,
"isSuspended": true,
"useUsername": "bot0860"
}
POST Suspend Bulk Accounts
Description
ล็อค/ปลดล็อคหลาย accounts พร้อมกัน (สูงสุด 50 accounts)
Request Example
{
"customers": [35688231, 35688232, 35688233],
"isSuspended": true,
"useUsername": "bot0860"
}
POST Topup Credit
Description
เติมเครดิตให้กับ Member พร้อมตรวจสอบ boardtotal ก่อนเติม
Request Body
รองรับหลายรูปแบบ:
- เติมให้ 1 คน
- เติมให้หลายคนจำนวนเท่ากัน
- เติมให้หลายคนจำนวนต่างกัน
| Parameter | Type | Required | Description |
|---|---|---|---|
| agentCustomerId | Number | Optional | Agent Customer ID สำหรับดึงข้อมูลเครดิตก่อน/หลังเติม |
| boardtotal | Number | Optional | ตรวจสอบ boardtotal ก่อนเติม (ถ้าไม่ตรงจะไม่เติม) |
Request Examples
รูปแบบหลัก: เติมผ่าน customers array
{
"agentCustomerId": 34185327,
"customers": [
// { "customerId": 35576317, "amount": 160 },
{
"customerId": 34837990,
"amount": 100,
"boardtotal": 46887
}
],
"useUsername": "zds88zzq"
}
รูปแบบที่ 1: เติมให้ 1 คน
{
"customerId": 34837990,
"amount": 100,
"boardtotal": 1000,
"accountId": "bot0860",
"agentCustomerId": 34185327,
"useUsername": "zds88zzq"
}
รูปแบบที่ 2: เติมให้หลายคนจำนวนเท่ากัน
{
"customerId": "34837990,34837991,34837992",
"amount": 100,
"agentCustomerId": 34185327
}
รูปแบบที่ 3: เติมให้หลายคนจำนวนต่างกัน
{
"customers": [
{
"customerId": 34837990,
"amount": 130,
"boardtotal": 1000,
"accountId": "bot0860",
"agentCustomerId": 34185327
},
{
"customerId": 34837991,
"amount": 105
}
],
"agentCustomerId": 34185327,
"useUsername": "zds88zzq"
}
Response Example
{
"success": true,
"verify": {
"status": "ok"
},
"message": "เติมเครดิต: สำเร็จ 2 accounts, ล้มเหลว 0 accounts, รวม 235 บาท",
"usedUsername": "zds88zzq",
"summary": {
"totalAccounts": 2,
"successCount": 2,
"failCount": 0,
"skipCount": 0,
"creditNoChangeCount": 0,
"requestedAmount": 235,
"actualUsedAmount": 235
},
"boardtotal": {
"boardtotal_before": 10000,
"boardtotal_after": 9765,
"used": 235,
"change": "10000 → 9765 (ใช้ไป 235 บาท)"
},
"results": [
{
"customerId": 34837990,
"amount": 130,
"success": true,
"creditBefore": {
"givenCredit": 100,
"totalBalance": 50,
"betCredit": 150
},
"creditAfter": {
"givenCredit": 230,
"totalBalance": 50,
"betCredit": 280
},
"creditChange": {
"givenCredit": 130,
"totalBalance": 0,
"betCredit": 130
}
}
]
}
หมายเหตุ
- หากเครดิตปัจจุบันเท่ากับจำนวนที่ต้องการเติมอยู่แล้ว (CreditNoChange) จะนับเป็น skip
- ระบบจะแสดงข้อมูลเครดิตก่อนและหลังเติมเมื่อระบุ agentCustomerId
- boardtotal คือยอดเครดิตคงเหลือของ Agent
-
การเทียบยอดหลังเติม (verify):
ระบบอ่านยอดกระดานก่อนและหลังเติม
แล้วเทียบว่ายอดที่ขยับเท่ากับยอดที่เติมพอดี ผลอยู่ในฟิลด์
verify.status—"ok"ยอดตรง,"unverified"อ่านยอดไม่ครบจึงเทียบไม่ได้,"mismatch"ยอดไม่ตรง -
เมื่อ
verify.status = "mismatch"ระบบตอบsuccess: falseพร้อมexpected,actualและreviewId— รายการถูกบันทึกเป็นรายการค้างตรวจสอบ รอพนักงานตรวจที่GET /reviews -
หากส่ง
boardtotalรายตัวแล้วยอดไม่ตรงก่อนเติม รายการนั้นจะถูกข้าม (ยังไม่มีเงินเคลื่อนไหว) และถูกบันทึกเป็นรายการค้างตรวจสอบพร้อมreviewIdเช่นกัน
POST Get Credit
Description
ดึงข้อมูลเครดิตของลูกค้าทั้งหมดภายใต้ Agent หรือเฉพาะลูกค้าที่ระบุ
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| agentCustomerId | Number | Required | Agent Customer ID |
| customerId | Number | Optional | Customer ID ที่ต้องการดูเฉพาะเจาะจง |
| useUsername | String | Optional | ระบุ username ที่ต้องการใช้ JWT |
Request Example
{
"agentCustomerId": 34185327,
"customerId": 34837990,
"useUsername": "zds88zzq"
}
Response Example
{
"success": true,
"agentCustomerId": 34185327,
"customerId": 34837990,
"usedUsername": "zds88zzq",
"data": {
"customerId": 34837990,
"credit": {
"givenCredit": 1000
},
"balance": {
"totalBalance": 500
},
"betCredit": 1500,
"customerInfo": {
"accountId": "zds88zz001",
"isSuspended": false
}
},
"message": "ดึงข้อมูลเครดิตสำเร็จสำหรับ Customer ID: 34837990"
}
POST Transfer (Withdraw)
Description
ถอนเงินจาก Member โดยระบบจะ:
- อ่านยอดกระดานและ cash balance ของ agent ก่อนถอนทุกครั้ง (ปิดได้ด้วย skipCashBalanceCheck สำหรับกระดานที่ไม่มี Cash Balance)
- ตรวจสอบ bet credit ว่าเพียงพอหรือไม่
- ตัดจาก Credit ก่อน หากไม่พอจะตัดจาก Balance (Transfer)
-
อ่านยอดลูกค้าซ้ำหลังถอน
แล้วเทียบว่าลดลงเท่ายอดที่ถอนพอดี — ผลอยู่ในฟิลด์
verify -
ยอดไม่ตรง (ก่อนหรือหลัง) →
บันทึกเป็นรายการค้างตรวจสอบ ดูได้ที่
GET /reviews
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerId | Number | Required | Customer ID ที่ต้องการถอนเงิน |
| amount | Number | Required | จำนวนเงินที่ต้องการถอน (ส่งเป็นค่าบวก) |
| boardtotal | Number | Optional | ยอดกระดานที่คาดไว้ — ถ้าไม่ตรงกับค่าจริงจะไม่ถอน และบันทึกเป็นรายการค้างตรวจสอบ |
| cashbalance | Number | Optional | cash balance ที่คาดไว้ — ถ้าไม่ตรงกับค่าจริงจะไม่ถอน และบันทึกเป็นรายการค้างตรวจสอบ |
| skipCashBalanceCheck | Boolean | Optional | สำหรับกระดานที่ไม่มี Cash Balance เท่านั้น — ปกติระบบอ่าน cash balance ของ agent ก่อนถอนทุกครั้ง ตั้งเป็น true เพื่อข้ามการอ่านนี้ (default: false) |
| skipBetCreditCheck | Boolean | Optional | ข้ามการเช็ค bet credit (default: false) |
| agentCustomerId | Number | Recommended | Agent Customer ID สำหรับเช็ค bet credit (แนะนำให้ใส่เสมอ) |
| useUsername | String | Optional | ระบุ username ที่ต้องการใช้ JWT |
Request Example
{
"agentCustomerId": 34185327,
"customerId": 34837990,
"amount": 1,
"boardtotal": 46887,
"cashbalance": 174,
"useUsername": "zds88zzq" // optional
}
Response Example
{
"success": true,
"verify": {
"status": "ok"
},
"message": "Transfer สำเร็จสำหรับ Customer ID 35688234",
"customerId": 35688234,
"amount": 10,
"usedUsername": "bot0860",
"breakdown": {
"totalWithdrawn": 10,
"fromCredit": 10,
"fromBalance": 0
},
"balanceBefore": {
"credit": 15,
"totalBalance": 100,
"betCredit": 115,
"boardtotal": 909015.0,
"cashBalance": -19039187.83
},
"balanceAfter": {
"credit": 5,
"totalBalance": 100,
"betCredit": 105,
"boardtotal": 909015.0,
"cashBalance": -19039177.83
}
}
Error Cases
- บัญชีถูกล็อคอยู่: ไม่สามารถทำ transfer ได้
-
boardtotal/cashbalance ไม่ตรง: จะไม่ดำเนินการ
transfer และบันทึกเป็นรายการค้างตรวจสอบ (ตอบ
reviewIdมาด้วย) -
อ่าน cash balance ก่อนถอนไม่สำเร็จ:
จะไม่ดำเนินการ transfer — ถ้ากระดานไม่มี Cash Balance ให้ส่ง
skipCashBalanceCheck: true -
ยอดหลังถอนไม่ตรง: เงินขยับไปแล้ว ระบบตอบ
HTTP 200 แต่
success: falseพร้อมverify.status: "mismatch"และreviewId— รอพนักงานตรวจสอบ อย่า retry ทันที - เครดิตไม่พอ: เมื่อ bet credit น้อยกว่าจำนวนที่ต้องการถอน
- ไม่พบลูกค้า: เมื่อ customerId ไม่อยู่ภายใต้ agent ที่ระบุ
หมายเหตุสำคัญ
- ระบบจะตัดจาก Credit ก่อนเสมอ หากไม่พอจึงจะตัดจาก Balance
- ระบบจะ suspend account อัตโนมัติก่อนทำ transfer และ unsuspend หลังเสร็จ
- หากไม่ระบุ agentCustomerId อาจทำให้เช็ค bet credit ไม่ถูกต้อง
- Bet Credit = Given Credit + Total Balance
- หากต้องการข้ามการเช็ค bet credit ให้ตั้ง skipBetCreditCheck = true
GET Review Queue
Description
รายการฝาก/ถอนที่การเทียบยอดก่อนหรือหลังทำรายการ ไม่ตรง — ระบบบันทึกไว้เป็นรายการค้าง รอพนักงานเข้ามาตรวจสอบและปิดรายการ ตาม ขั้นตอนการเทียบยอดกระดาน/Cash Balance
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | String | Optional |
pending (default) | resolved |
all
|
Response Example
{
"success": true,
"count": 1,
"reviews": [
{
"id": "665f1a2b3c4d5e6f7a8b9c0d",
"at": "2026-08-13T12:00:00Z",
"kind": "withdraw",
"stage": "after",
"field": "credit",
"customerId": 34837990,
"amount": 100,
"expected": 450,
"actual": 460,
"useUsername": "zds88zzq",
"note": "ยอดลูกค้าหลังถอนไม่ตรงกับที่คาด รอพนักงานตรวจสอบ",
"status": "pending"
}
]
}
หมายเหตุ
-
kind:deposit(เติม) |withdraw(ถอน) -
stage:before= ไม่ตรงก่อนทำรายการ (ยังไม่มีเงินเคลื่อนไหว) |after= ไม่ตรงหลังทำรายการ (เงินขยับไปแล้ว ต้องตรวจสอบก่อนทำรายการซ้ำ) -
field: ยอดที่ไม่ตรง —boardtotal|cashbalance|credit - ต้องตั้งค่า MongoDB จึงจะเก็บคิวนี้ได้
POST Resolve Review
Description
ปิดรายการค้างตรวจสอบหลังพนักงานตรวจแล้ว รายการที่ถูกปิดแล้วปิดซ้ำไม่ได้ (ได้ 404) เพื่อไม่ให้สองคนเข้าใจว่าตัวเองเป็นคนจัดการรายการเดียวกัน ต้องใช้สิทธิ์ระดับ operator ขึ้นไป
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | String | Required | id ของรายการค้างตรวจสอบ (จาก GET /reviews) |
| resolution | String | Optional | บันทึกผลการตรวจสอบ |
Request Example
{
"id": "665f1a2b3c4d5e6f7a8b9c0d",
"resolution": "ตรวจแล้ว ยอดตรงหลังรอบตัดยอด"
}
Response Example
{
"success": true,
"id": "665f1a2b3c4d5e6f7a8b9c0d"
}
รหัสข้อผิดพลาด
หน้าตาของคำตอบเมื่อล้มเหลว
{
"success": false,
"code": "FSB-CREDIT-003",
"error": "boardtotal ไม่ตรง: ระบบ 4143777974.86 แต่ใน agent 4143777474.86",
"fix": "อ่านยอดล่าสุดด้วย /getcredit แล้วส่งมาใหม่ ..."
}
- error เขียนให้คนอ่าน มีรายละเอียดของคำขอนั้นๆ และอาจถูกแก้คำ
- code ไม่เปลี่ยน ใช้แยกกรณีในโค้ดและใช้ค้นหาต้นตอ
- fix คือสิ่งที่ควรทำต่อ
คำขอไม่ถูกต้อง
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-INPUT-001 | 400 | body ไม่ใช่ JSON ที่อ่านได้ | ตรวจว่าส่ง Content-Type: application/json และ body เป็น JSON ที่ถูกต้อง |
FSB-INPUT-002 | 400 | ไม่ได้ส่งค่าที่จำเป็น | ดูชื่อฟิลด์ที่ขาดในข้อความ แล้วส่งมาให้ครบ |
FSB-INPUT-003 | 400 | ค่าที่ส่งมาผิดรูปแบบหรืออยู่นอกช่วงที่รับได้ | ดูชนิดและช่วงค่าที่รับได้ในเอกสารของเส้นนั้น |
FSB-INPUT-004 | 400 | ช่วงวันที่ผิดรูปแบบ กว้างเกินไป หรือ from อยู่หลัง to | ใช้รูปแบบ YYYY-MM-DD และให้ from ไม่เกิน to |
การเข้าใช้ระบบนี้
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-AUTH-001 | 401 | ไม่ได้แนบ API key หรือ session ที่ใช้ได้ | แนบ X-API-Key หรือ Authorization: Bearer <access token> |
FSB-AUTH-002 | 401 | ชื่อผู้ใช้หรือรหัสผ่านของระบบนี้ไม่ถูกต้อง | ตรวจชื่อผู้ใช้และรหัสผ่าน |
FSB-AUTH-003 | 401 | session หมดอายุหรือถูกยกเลิกแล้ว | เรียก /auth/refresh ด้วย refresh token หรือเข้าสู่ระบบใหม่ |
FSB-AUTH-004 | 403 | สิทธิ์ไม่พอสำหรับงานนี้ | ใช้ token ที่มีสิทธิ์ระดับที่ระบุในข้อความ หรือขอให้แอดมินเพิ่มสิทธิ์ |
FSB-AUTH-005 | 401 | บัญชีนี้เปิดยืนยันตัวตนสองชั้นไว้ ต้องส่งรหัส 6 หลักมาด้วย | ส่ง code จากแอป authenticator มาในคำขอเดียวกัน |
FSB-AUTH-006 | 401 | รหัสยืนยันสองชั้นไม่ถูกต้อง | ขอรหัสใหม่จากแอป และตรวจว่าเวลาบนเครื่องตรงกับเวลาจริง |
FSB-AUTH-007 | 401 | รหัสถูกต้องแต่หมดอายุไปแล้ว | ใช้รหัสปัจจุบันจากแอป อย่ากรอกทิ้งไว้นานก่อนกดยืนยัน |
บัญชีพอร์ทัลและ Token
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-TOKEN-001 | 400 | ยังไม่มี JWT ของบัญชีพอร์ทัลที่จะใช้ | เรียก POST /login เพื่อล็อกอินบัญชีพอร์ทัลก่อน |
FSB-TOKEN-002 | 401 | JWT ของบัญชีพอร์ทัลหมดอายุและต่ออายุอัตโนมัติไม่ได้ | ล็อกอินบัญชีพอร์ทัลใหม่ หรือเก็บรหัสไว้เพื่อให้ระบบต่อให้เอง |
FSB-TOKEN-003 | 403 | บัญชีพอร์ทัลนี้ถูกปิดใช้งานไว้ในระบบ | เปิดใช้งานที่หน้าสถานะล็อกอิน หรือเลือกบัญชีอื่น |
FSB-TOKEN-004 | 403 | บัญชีที่ล็อกอินอยู่ไม่มีสิทธิ์สลับไปยังบัญชีที่ขอ | เลือกบัญชีจากรายการที่สลับได้ หรือให้ผู้ดูแลพอร์ทัลเปิดสิทธิ์ให้ |
FSB-TOKEN-005 | 400 | บัญชีที่ขอสลับไปไม่ใช่บัญชีเอเย่นต์ | สมาชิกไม่ใช่บัญชีที่สลับเข้าไปได้ ให้ระบุด้วย customerId แทน |
FSB-TOKEN-006 | 409 | พอร์ทัลให้สลับบัญชีได้ครั้งเดียวต่อการล็อกอิน และครั้งนี้ใช้ไปแล้ว | เก็บรหัสพอร์ทัลไว้เพื่อให้ระบบล็อกอินใหม่ให้เอง หรือล็อกอินใหม่เอง |
เครดิต
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-CREDIT-001 | 502 | อ่านเครดิตปัจจุบันไม่สำเร็จ จึงไม่ทำรายการต่อ | ลองใหม่อีกครั้ง — ระบบยกเลิกไว้แทนที่จะเดา เพราะเดาผิดคือเขียนทับเครดิตลูกค้า |
FSB-CREDIT-002 | 400 | ไม่พบลูกค้ารายนี้ในสายงานของ agent ที่ระบุ | ตรวจ agentCustomerId ว่าเป็นต้นสายของลูกค้าคนนี้จริง |
FSB-CREDIT-003 | 409 | boardtotal ที่ส่งมาไม่ตรงกับยอดจริงที่พอร์ทัล | อ่านยอดล่าสุดด้วย /getcredit แล้วส่งมาใหม่ หรือไม่ต้องส่ง boardtotal ถ้าไม่ต้องการให้ตรวจ |
FSB-CREDIT-004 | 409 | cashbalance ที่ส่งมาไม่ตรงกับยอดจริงที่พอร์ทัล | อ่านยอดล่าสุดแล้วส่งมาใหม่ หรือไม่ต้องส่งถ้าไม่ต้องการให้ตรวจ |
FSB-CREDIT-005 | 400 | เครดิตคงเหลือไม่พอสำหรับจำนวนที่ขอดึงออก | ลดจำนวนลง หรือถ้าติดที่เครดิตที่ผูกกับบิลค้าง ให้ส่ง skipBetCreditCheck: true เมื่อยอมรับผลได้ |
FSB-CREDIT-006 | 400 | amount ต้องมากกว่า 0 | เส้นเติมและเส้นดึงออกใช้จำนวนบวกทั้งคู่ ทิศทางกำหนดด้วยเส้นที่เรียก ไม่ใช่ด้วยเครื่องหมาย |
สมาชิก
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-MEMBER-001 | 404 | ไม่พบสมาชิกรายนี้ | ตรวจ customerId — ใช้ /getcredit หรือ /report/directory เพื่อดูรายชื่อที่มีจริง |
FSB-MEMBER-002 | 409 | ชื่อผู้ใช้นี้ถูกใช้ไปแล้ว | เปลี่ยนชื่อผู้ใช้ หรือให้ระบบสุ่มต่อท้ายให้ |
รายงาน
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-REPORT-001 | 400 | ช่วงวันที่กว้างเกินกว่าที่พอร์ทัลตอบได้ | แบ่งเป็นช่วงสั้นลง หรือใช้เส้น /history/* ที่อ่านจากข้อมูลที่เก็บไว้ |
FSB-REPORT-002 | 404 | ยังไม่เคยเก็บข้อมูลของช่วงวันที่นี้ไว้ | ใช้ /history/coverage ดูว่ามีวันไหนบ้าง หรือเปิดดูรายงานวันนั้นหนึ่งครั้งเพื่อให้ระบบเก็บ |
Gateway
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-GW-001 | 400 | path ที่ส่งมาไม่ใช่ path เปล่า — เป็น URL เต็ม ชื่อโฮสต์ หรือมี ".." | ส่งเฉพาะ path เช่น "/report/winloss" |
FSB-GW-002 | 403 | เส้นนี้ไม่อนุญาตให้เรียกผ่าน gateway | ใช้เส้นเฉพาะของระบบที่ทำงานเดียวกัน ดูชื่อเส้นในข้อความ |
FSB-GW-003 | 400 | method ที่ส่งมาไม่รองรับ | ใช้ GET, POST, PUT, PATCH หรือ DELETE |
พอร์ทัลปลายทาง
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-PORTAL-001 | 502 | พอร์ทัลตอบกลับเป็นข้อผิดพลาด | ดู details และ upstreamStatus ในคำตอบ — เป็นข้อความจากพอร์ทัลโดยตรง |
FSB-PORTAL-002 | 504 | พอร์ทัลไม่ตอบภายในเวลาที่กำหนด | ลองใหม่อีกครั้ง ถ้าเกิดต่อเนื่องแปลว่าพอร์ทัลกำลังมีปัญหา |
FSB-PORTAL-003 | 429 | พอร์ทัลปฏิเสธเพราะเรียกถี่เกินไป และลองซ้ำให้แล้วยังไม่ผ่าน | ลดความถี่ในการเรียกลง |
การจำกัดอัตราและ IP
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-LIMIT-001 | 429 | เรียกถี่เกินขีดจำกัดของระบบนี้ | รอสักครู่แล้วลองใหม่ ดู Retry-After ในหัวข้อคำตอบ |
FSB-LIMIT-002 | 403 | ที่อยู่ IP นี้ไม่อยู่ในรายการที่อนุญาต | เรียกจาก IP ที่อยู่ใน allow list หรือให้แอดมินเพิ่ม IP นี้เข้าไป |
FSB-LIMIT-003 | 403 | ที่อยู่ IP นี้ถูกบล็อกไว้ | ติดต่อแอดมินเพื่อปลดบล็อก |
ระบบ
| Code | HTTP | หมายถึง | แก้ยังไง |
|---|---|---|---|
FSB-SYS-001 | 501 | ฟีเจอร์นี้ต้องใช้ MongoDB แต่ยังไม่ได้ตั้งค่าไว้ | ตั้งค่า MONGO_URI แล้วเริ่มเซิร์ฟเวอร์ใหม่ |
FSB-SYS-002 | 500 | ข้อผิดพลาดภายในระบบ | แจ้งรหัสนี้พร้อมเวลาที่เกิดให้ผู้ดูแล — รายละเอียดอยู่ในบันทึกของเซิร์ฟเวอร์ |
POST Win/Loss Report
Description
รายงานแพ้/ชนะ แยกตามดาวน์ไลน์และผลิตภัณฑ์
คืนยอดรวมของทุกคอลัมน์มาให้ในฟิลด์ totals
⚠️ เรื่องวันที่ — สำคัญที่สุด
-
ส่งวันที่เป็น "YYYY-MM-DD" ธรรมดา เช่น
"2026-07-29"เท่านั้น - เซิร์ฟเวอร์แปลงเป็น epoch ให้เอง โดยใช้ เที่ยงคืน UTC-4 ซึ่งเป็นเส้นแบ่งวัน (win/lost date) ของ upstream
- ห้ามคำนวณ epoch เอง — ถ้าใช้ UTC midnight หรือ GMT+7 ยอดจะเหลื่อมไปทั้งวัน
-
เว้น
toไว้ = รายงานวันเดียว, เว้นทั้งคู่ = วันนี้
Request Body
{
"customerId": 2286064,
"requesterId": 2286064,
"from": "2026-07-01",
"to": "2026-07-29",
"filters": [1, 3, 4, 2, 5, 6],
"simple": false,
"useUsername": "optional"
}
-
requesterId— เว้นไว้ได้ ค่าเริ่มต้นเท่ากับcustomerId. เวลา drill-down เข้าดาวน์ไลน์ ให้ เปลี่ยนแค่ customerId แล้วคง requesterId เป็นบัญชีที่ล็อกอิน -
filters— 1 = กีฬา, 2 = คาสิโน, 3 = เกม (4-6 upstream รับแต่ไม่เคยคืนข้อมูล) เว้นไว้ = ครบทุกตัว -
simple: true— ยุบชั้น Agent/MA/SMA เหลือชุดตัวเลขเดียว พร้อมฟิลด์payout
Response Example
{
"success": true,
"customerId": 2286064,
"from": "2026-07-29",
"to": "2026-07-29",
"mode": "detailed",
"count": 27,
"data": [
{
"displayName": "Efootball",
"filter": 1,
"customerId": 11126203,
"accountId": "zds78",
"turnover": 69540,
"playerWinloss": -69540,
"maTotal": 521.55,
"smaTotal": 55214.76,
"grossCommission": 521.55,
"companyWinloss": 13803.69
}
],
"totals": { "turnover": 2851429, "companyWinloss": 18420.55 }
}
POST Dashboard
Description
รวม 9 เส้น dashboard ของ upstream ให้เหลือ request เดียว ยิงขนานฝั่งเซิร์ฟเวอร์ — ออกแบบมาให้เรียกซ้ำเป็นรอบ (realtime)
Request Body
{
"accountId": "zds",
"useUsername": "optional"
}
accountId คือ username ที่ได้จาก
/report/directory (เช่น zds, zla, zum)
Response Example
{
"success": true,
"fetchedAt": "2026-07-29T05:20:11Z",
"data": {
"totalOutstanding": 356175.68,
"remainingCredit": 0,
"usedCredit": 580000000,
"players": { "online": 73, "open": 35152 },
"monthToDate": {
"previous": 6275622.85,
"current": 12957815.21,
"trend": { "value": 1.0648, "display": true }
},
"inactivePlayers": [ { "accountId": "zds8810069", "betCredit": 0.43 } ]
},
"errors": null
}
ถ้ามีบางเส้นล้ม จะไม่ทำให้ทั้ง request พัง — คีย์นั้นจะหายไปจาก
data แล้วไปโผล่ใน errors แทน
ให้ tile ที่เหลือยังแสดงผลได้ (จะได้ 502
ก็ต่อเมื่อล้มทั้งหมด ซึ่งมักแปลว่า token หมดอายุ)
POST Metrics
Description
time series แพ้/ชนะรายวัน แยกตามกลุ่มผลิตภัณฑ์ ใช้วาดกราฟ
Request Body
{
"accountId": "zds",
"filters": [1, 2, 3]
}
คีย์ของ data เป็นชื่อกลุ่มภาษาไทย (กีฬา / คาสิโน /
เกม) แต่ละค่ามี current และ
lastYear เป็น array ของ
{ labels: [epoch, epoch], value }
POST Statement
Description
งบรายวันแยกตามผลิตภัณฑ์ พร้อมรายการโอนเครดิตในช่วงเดียวกัน ดึงมาคู่กันเพราะพอร์ทัลใช้คู่กันเสมอ
Request Body
{
"customerId": 2286064,
"from": "2026-07-22",
"to": "2026-07-29"
}
Response Example
{
"data": {
"statement": [
{
"date": 1784779200000,
"type": 99,
"product": 1,
"betting": 273139.75,
"commission": -18654.55,
"cashBalance": 254485.2,
"totalBalance": -152000.69
}
],
"transfers": [
{
"winLostDate": 1784779200000,
"transDesc": "To Customer:zds88(...)",
"amount": -0.74,
"transDate": 1784810147857
}
]
}
}
แถว type: 100 เป็นแถวยอดยกมา ซึ่ง
product, betting,
commission จะเป็น null
POST Calendar
Description
ข้อมูลประกอบตัวเลือกวันที่ — วันไหนมีบิล และวันไหน upstream ปิดยอดแล้ว คืนมาเป็น "YYYY-MM-DD" ทั้งหมด ไม่ต้องแปลง epoch เอง
Response Example
{
"data": {
"datesWithBet": ["2026-07-28", "2026-07-29"],
"settledDays": {
"2026-07-27": 1,
"2026-07-28": 1,
"2026-07-29": 0
}
}
}
settledDays: 1 = ปิดยอดแล้ว,
0 = ยังไม่ปิด — วันที่ยังไม่ปิด
ตัวเลขในรายงานยังเปลี่ยนได้ ควรเตือนผู้ใช้ก่อนเอายอดไปใช้จริง
POST Settled Bets
Description
บิลรายใบที่เคลียร์แล้ว แยกตามผลิตภัณฑ์ (sports, eSports, games,
liveCasino, raceTote) พร้อมจำนวนบิลต่อผลิตภัณฑ์ในฟิลด์
counts
Request Body
{
"customerId": 25783301,
"from": "2026-07-28",
"language": "1"
}
โครงสร้างบิลลึกและต่างกันตามผลิตภัณฑ์ จึงส่งผ่านตามที่ upstream คืนมาโดยไม่ปรับรูป
POST Breakdown
Description
รวม billing breakdown และ cancelled-campaign breakdown ไว้ในคำขอเดียว
หมายเหตุ
ทั้งสองเส้นคืน array ว่างในทุก response ที่เก็บได้จริง จึงส่งผ่านแบบไม่กำหนดชนิดข้อมูล แทนที่จะเดาโครงสร้างขึ้นมาเอง
POST Outstanding Bets
Description
ความเสี่ยงที่ยังไม่เคลียร์ แยกรายแมตช์ — มีเงินวางอยู่ข้างไหนเท่าไหร่ ทั้งเต็มเวลาและครึ่งแรก เรียงจากยอดค้างมากสุดลงมาให้แล้ว เพราะเป็นหน้าจอความเสี่ยง แมตช์ที่เงินเยอะสุดต้องเห็นก่อน
Request Body
{
"monitored": false,
"language": "1"
}
ทุกฟิลด์เว้นได้หมด — monitored: true
จะกรองเหลือเฉพาะแมตช์ที่ตั้งเฝ้าไว้
Response Example
{
"count": 112,
"liveCount": 78,
"totalExposure": 175286.18,
"data": [
{
"leagueName": "Sydney Super Cup",
"sportName": "Football",
"homeTeamName": "Sydney FC",
"awayTeamName": "Tottenham Hotspur",
"isLive": false,
"liveHomeScore": 0,
"showTime": 1785318300000,
"fullTime": {
"home": 4.8, "away": 0,
"hdpTotal": 4.8, "hdpTotalCount": 1,
"over": 1205, "under": 0,
"ouTotal": 1205, "ouTotalCount": 2
},
"halfTime": { "hdpTotal": 0, "ouTotal": 0 }
}
]
}
totalExposure =
fullTime.hdpTotal + fullTime.ouTotal + halfTime.hdpTotal +
halfTime.ouTotal รวมทุกแมตช์ —
คำนวณมาให้แล้วไม่ต้องบวกเอง
POST Credit Downline
Description
ฐานะเครดิตของตัวเองพร้อมของดาวน์ไลน์ทุกคน รวม 3 เส้นเป็นคำขอเดียว
(creditbalance/self,
creditbalance/children, BalanceInfo)
Request Body
{
"customerId": 2286064,
"accountStatus": 0
}
accountStatus — 0 = ทุกสถานะ,
1 = เฉพาะที่ใช้งานอยู่ ค่าเริ่มต้นคือ 0
(ส่ง 0 มาเองก็ยังเป็น 0 ไม่ถูกแทนที่)
Response Example
{
"data": {
"self": {
"customerId": 2286064,
"customerInfo": { "accountId": "zds", "accountStatus": 1 },
"credit": { "givenCredit": 580000000, "usedCredit": 580000000 },
"balance": { "totalBalance": -441911.43 },
"outstanding": { "totalOutstanding": 299643.86 },
"betCredit": 4723897218.11
},
"children": [ "... 34 รายการ รูปแบบเดียวกับ self" ]
}
}
POST IP Groups
Description
ยอดเดิมพันรวมตาม IP ต้นทาง — ใช้จับกรณีหลายสมาชิกยิงจาก IP เดียวกัน ซึ่งเป็นเหตุผลที่รายงานนี้มีอยู่ เรียงตามจำนวนสมาชิกมากสุดก่อน แล้วค่อยตามยอดเดิมพัน
Request Body
{
"filter": 1
}
filter เป็น ตัวเลขเดี่ยว
(1 = กีฬา, 2 = คาสิโน, 3 = เกม) ไม่ใช่ array แบบเส้นอื่น
เพราะ upstream รับ filters แค่ค่าเดียวที่เส้นนี้
ค่าเริ่มต้นคือ 1
Response Example
{
"count": 421,
"sharedIPs": 1,
"totalStake": 1088714,
"data": [
{
"ip": "223.205.251.58",
"memberCount": 2,
"betCount": 6,
"memberTotal": 7500,
"agentTotal": 0,
"maTotal": 0,
"smaTotal": 6000
}
]
}
sharedIPs = จำนวน IP ที่มี
memberCount > 1 — คือตัวเลขที่ควรดูก่อน
ส่วน IP ที่มีสมาชิกคนเดียวเป็นเรื่องปกติ
POST Directory
Description
รายชื่อบัญชีที่ใช้ทำรายงานได้ — ดาวน์ไลน์ชั้นแรก และบัญชี agent ที่สลับไปได้
Response Example
{
"data": {
"linkedAgents": [
{ "customerId": 2286064, "username": "zds", "isLocked": false }
],
"children": [
{ "customerId": 3014933, "accountId": "zds08", "firstName": "P.BOY" }
]
}
}
เรียกเส้นนี้ก่อนเสมอ — username
ใช้เป็น accountId ของ dashboard/metrics ได้ตรงๆ
และ customerId ใช้เป็น requesterId
ของรายงานได้ตรงๆ จึงไม่ต้องให้ผู้ใช้พิมพ์เอง