ทุก endpoint (ยกเว้น register/login/health) ต้องส่ง auth ผ่าน header ตัวใดตัวหนึ่ง:
Authorization: Bearer <USER_TOKEN> หรือ X-API-Key: <API_KEY>
หรือใส่ใน body ก็ได้: { "token": "..." } / { "apiKey": "..." }
caller.id ให้อัตโนมัติ
aham, ส่ง username:"555" → final = aham555
parentId)customer เสมอ (ไม่รับ parameter role) — สร้างลูกสายใช้หน้าหลังบ้านusername = suffix อะไรก็ได้ ระบบ sanitize เป็น a-z 0-9 _ + ประกบ caller.id ข้างหน้าcaller.id อยู่แล้ว → ใช้ตามนั้น (กัน prefix ซ้ำ)// ── Example 1 — caller=aham (master) ส่งแค่ suffix
// Request
{
"username": "555", // suffix อะไรก็ได้
"password": "1234",
"displayName": "ลูกค้าหนึ่ง", // (optional — default = final username)
"refCode": "A8K2X9MP" // ★ optional — รหัสแนะนำ (ต้องอยู่สายเดียวกัน — ข้ามสายไม่ได้)
}
// Response 200
{
"ok": true,
"user": {
"id": "aham555",
"role": "customer",
"parentId": "aham",
"refCode": "X3Y9Z2P1", // ★ รหัสแนะนำของลูกค้าใหม่ (auto-gen)
"affWallet": 0,
"referrerId": "some_user", // ★ ผู้แนะนำ (null ถ้าไม่ได้ใส่ refCode)
...
},
"token": "abc123...", // login token ของลูกค้าใหม่
"finalUsername": "aham555", // ★ username สุดท้ายที่ระบบสร้างให้
"referralBound": true, // ★ ผูก refCode สำเร็จไหม (false = code ไม่ถูก/ตัวเอง)
"playUrl": "https://play.pokstack.com/?autotoken=abc123...",
"referralCode": "X3Y9Z2P1", // ★ รหัสของลูกค้าใหม่ (เอาไปแสดงให้เขา)
"referralUrl": "https://play.pokstack.com/?ref=X3Y9Z2P1" // ★ ลิงค์ชวนเพื่อน
}
// ── Example 2 — caller=aham, ส่งชื่อเต็ม (เริ่มด้วย "aham" อยู่แล้ว)
// Request
{ "username": "aham_vip01", "password": "1234" }
// Response 200 — ใช้ "aham_vip01" ตามที่ส่ง (ไม่ประกบซ้ำ)
{ "ok": true, "user": { "id": "aham_vip01", ... }, "finalUsername": "aham_vip01" }
// ── Error 403 — ผู้แนะนำอยู่คนละสาย (HARD BLOCK — ข้ามสายไม่ได้)
// HTTP 403 Forbidden
{
"ok": false,
"error": "❌ ผู้แนะนำ \"นาย A\" อยู่คนละสาย — แนะนำได้เฉพาะสมาชิกในสายของตัวเองเท่านั้น",
"crossBranch": true,
"referrerName": "นาย A",
"refCode": "A8K2X9MP"
}
// → ต้องเปลี่ยน refCode เป็นของคนในสายเดียวกัน หรือเอา refCode ออก (สมัครโดยไม่ผูกผู้แนะนำ)
// ── Error 400 — ไม่พบรหัสแนะนำ
{ "ok": false, "error": "❌ ไม่พบรหัสแนะนำ \"A8K2X9MP\"" }
// ── Error 403 — caller ไม่มีสิทธิ์สร้างลูกค้า
{ "ok": false, "error": "role \"...\" ไม่สามารถสร้างลูกค้าผ่าน API ได้ — ต้องเป็น super_senior/senior/master/agent" }
// ── Error 400 — final username ยาวเกิน 16
{ "ok": false, "error": "username สุดท้าย \"aham_long_suffix\" ยาว 17 ตัว — เกิน 16 (suffix สั้นกว่านี้)" }
playUrl สำหรับ redirect เข้าเล่น
"returnUrl": "https://agent-site.com/lobby" → ลูกค้ากด logout → เด้งกลับเว็บ agent ทันที
http:// หรือ https:// — ความยาว ≤ 2000 ตัว
// Request
{
"username": "user01",
"password": "1234",
"returnUrl": "https://agent-site.com/lobby" // ★ optional — URL กลับเมื่อ logout
}
// Response 200
{
"ok": true,
"user": {
"id":"user01", "displayName":"...", "role":"customer", "points":0,
"refCode": "X3Y9Z2P1", // ★ รหัสแนะนำของผู้ login
"affWallet": 35.50, // ★ ยอดคอม Aff รอถอน
"referrerId": null, // ★ ผู้แนะนำ (ถ้ามี)
...
},
"token": "abc123...",
"playUrl": "https://play.pokstack.com/?autotoken=abc123...",
"returnUrl": "https://agent-site.com/lobby",
"referralCode": "X3Y9Z2P1", // ★
"referralUrl": "https://play.pokstack.com/?ref=X3Y9Z2P1" // ★ ลิงค์ชวนเพื่อน
}
refCode, affWallet, referrerId{
"ok": true,
"user": {
"id": "user01", "displayName": "...", "role": "customer",
"points": 1500, "bonusPoints": 0, "holdPct": 0,
"refCode": "X3Y9Z2P1", // ★ รหัสแนะนำเพื่อน (auto-gen ลำดับแรกที่เรียก)
"affWallet": 35.50, // ★ ยอดคอม Aff รอถอน
"referrerId": null, // ★ id ผู้แนะนำ (null = ไม่มี)
...
},
"authVia": "token" // "token" | "apiKey"
}
/api/downline/regen-api-key แทน// Request
{
"customerId": "user01",
"ttlMinutes": 15,
"returnUrl": "https://agent-site.com/lobby" // ★ optional
}
// Response 200
{
"ok": true,
"url": "https://play.pokstack.com/?autotoken=XXXX...",
"expiresAt": 1762291800000,
"ttlMinutes": 15,
"customerId": "user01",
"returnUrl": "https://agent-site.com/lobby" // ★ echo (null ถ้าไม่ส่ง)
}
// ลูกค้าคลิก url → frontend exchange autotoken → real token → entered game
// ★ returnUrl จะถูกบันทึกในเซสชันลูกค้า — กดออก/logout → redirect กลับไป returnUrl
// Request
{ "userId": "user01" } // optional — ถ้าไม่ใส่ = ของตัวเอง
// Response 200
{ "ok": true, "userId": "user01", "points": 1500, "bonus": 0 }/wallet/topup เช็คก่อนว่ามี credit พอเติมหรือเปล่า — และดูภาพรวมเงินสะพัดที่จ่ายให้ลูกค้าตัวเองทั้งหมด
agent_creditcanTopupCustomer:false (ไม่มีสิทธิ์เติม)customers.totalPoints = ผลรวม points ของลูกค้าตรงทุกคน — เฉพาะลูกค้าตัวเอง ไม่รวมลูกค้าใต้สายลึก// Request (no body needed — ใช้ token/apiKey identify caller)
{}
// Response 200 — caller=agent01 (master)
{
"ok": true,
"userId": "agent01",
"role": "master",
"agentCredit": 12500, // ★ เครดิตคงเหลือสำหรับเติมลูกค้า
"canTopupCustomer": true,
"customers": { // ★ ยอดรวมในลูกค้าตรงของตัวเอง
"count": 8, // จำนวนลูกค้าตรงทั้งหมด
"totalPoints": 24500, // รวมเครดิตเล่นในมือลูกค้า
"totalBonus": 1200, // รวมโบนัส
"totalCombined": 25700 // points + bonus
}
}
// Response 200 — caller เป็น customer
{
"ok": true,
"userId": "user01",
"role": "customer",
"agentCredit": 0,
"canTopupCustomer": false,
"customers": { "count": 0, "totalPoints": 0, "totalBonus": 0, "totalCombined": 0 }
}// Request (caller = agent01)
{ "userId": "agent01_user1", "amount": 1000, "reason": "เติมประจำวัน" }
// Response 200
{
"ok": true,
"userId": "agent01_user1",
"before": 500, "after": 1500, "delta": 1000, "mode": "topup",
"fromCredit": { "id": "agent01", "before": 5000, "after": 4000, "unlimited": false }
}
// Error 403 — target เป็น downline
{ "ok": false, "error": "target เป็น senior (downline) — เติมเครดิตให้ดาวไลน์ผ่าน API ไม่ได้ ใช้หน้า /downline.html เท่านั้น" }
// Error 403 — ไม่ใช่ลูกค้าตรง
{ "ok": false, "error": "เติมได้เฉพาะลูกค้าตรงของคุณเท่านั้น (target.parent_id ต้อง = caller.id)" }
// Request
{ "userId": "agent01_user1", "amount": 200, "reason": "ขอคืน" }
// Response — สำเร็จ
{
"ok": true, "userId": "agent01_user1",
"before": 1500, "after": 1300, "delta": -200, "mode": "withdraw",
"fromCredit": { "id": "agent01", "before": 4000, "after": 4200, "unlimited": false }
}
// ★ ลูกค้ากำลังเล่นเกมอยู่ — ถอนไม่ได้
{
"ok": false,
"error": "⚠️ ผู้เล่น \"agent01_user1\" อยู่ในระหว่างเล่นเกม — ไม่สามารถถอนเครดิตได้ กรุณารอจบเกม/รอบก่อน",
"reason": "in_active_game"
}
// → รอลูกค้าเล่นเกมจบรอบ หรือออกจากห้องก่อน แล้วลองใหม่
// Request
{ "userId": "user01", "limit": 30, "offset": 0, "fromTs": 1762000000000, "toTs": 1762999999999 }
// Response 200
{ "ok": true, "userId": "user01", "transactions": [...], "total": 142 }// Request
{
"userId": "user01", // optional (default = ตัวเอง)
"limit": 30, "offset": 0,
"fromTs": ..., "toTs": ...,
"gameType": "dummy" // optional: 'dummy' | 'pokdeng'
}
// Response 200
{
"ok": true, "userId": "user01", "total": 87,
"rooms": [
{ "id": 41, "ts": 1762293000000, "roomId": "AB1234",
"gameType": "dummy", "rateName": "5/10/15",
"playerIds": ["user01","user02","..."],
"winnerId": "user01", "winType": "darkKnock", "rounds": 3 }
]
}
// Request
{ "roomId": "AB1234", "userId": "user01" }
// Response 200
{
"ok": true, "roomId": "AB1234", "total": 24,
"payments": [
{ "id": 1234, "ts": 1762293010000,
"eventKind": "ตีเต็ม", "reason": "ตีเต็ม (ตอง 5)",
"payerId": "user01", "receiverId": null, "amount": 30 },
{ "id": 1235, "ts": 1762293020000,
"eventKind": "เกิดหัว", "reason": "เกิดหัว Q♠",
"payerId": "user01", "receiverId": "user02", "amount": 10 }
]
}
// Response 200
{ "ok": true, "roomId": "AB1234", "summary": [
{ "userId": "user01", "displayName": "ลูกค้า1", "received": 850, "paid": 90, "net": 760 },
{ "userId": "user02", "displayName": "ลูกค้า2", "received": 120, "paid": 460, "net": -340 }
]}API ที่ตรงกับฟีเจอร์ในหน้า Downline (👤 สมาชิก / 🌐 ลูกสาย / 📊 รายงาน / ⚡ ทะลุบอท)
// Response 200
{
"ok": true,
"list": [
{
"id": "ahyxhmyixitr",
"displayName": "นาย A",
"agentRole": null, // null = customer; agent/master/senior/super_senior = ลูกสาย
"holdPct": 0,
"points": 1500, // เครดิตเล่น (เฉพาะ customer)
"agentCredit": 0, // เครดิตเติม (เฉพาะลูกสาย)
"createdAt": 1762293000000,
"isSuspended": false,
"online": true,
"botsOverride": false, // ⚡ ทะลุบอท (per-customer)
"affCommissionPct": 5, // 🎁 % คอม Aff ของ customer (null = default 5%)
"affWithdrawSchedule": "anytime"
}
]
}// Response 200
{ "ok": true, "total": 5, "list": [ /* customers only */ ] }{
"ok": true,
"counts": {
"totalSubtree": 47, // ทั้งสาย (ไม่นับตัวเอง)
"customers": 38, // ลูกค้าทั้งสาย
"agents": 9, // ลูกสายทั้งหมด
"directCustomers": 8, // ลูกค้าตรง
"directAgents": 2 // ลูกสายตรง
}
}// Request
{ "fromTs": 1762000000000, "toTs": 1762999999999 } // optional
// Response 200
{
"ok": true,
"period": { "fromTs": 1762000000000, "toTs": 1762999999999 },
"income": 12500.75, // ค่าต๋ง + ซื้อสติกเกอร์/ตัวละคร/ค่าห้อง (gross)
"cashback": 350.20, // คืนยอดเสีย
"eventPayout": 500, // รางวัล event (ลบจากรายได้สุทธิ)
"netIncome": 11650.55, // income − cashback − eventPayout
"creditTopup": 25000, // เติมเครดิตให้ลูกตรงรวม
"creditWithdraw": 8000, // ลดเครดิตจากลูกตรงรวม
"creditNet": 17000, // เติม − ลด
"customerCount": 38,
"directCount": 10
}kind// Request
{
"fromTs": 1762000000000,
"toTs": 1762999999999,
"kind": "all" // all | rake | sticker | character | room_fee
}
// Response 200
{ "ok": true, "kind": "rake", "total": 8420.50, "count": 1234,
"period": { "fromTs": ..., "toTs": ... } }// Request
{
"fromTs": ..., "toTs": ...,
"gameType": "dummy", // optional: dummy | pokdeng | makhos
"playerId": "user01", // optional
"winnerId": "user02", // optional
"roomId": "AB12", // optional (partial match)
"limit": 30, "offset": 0
}
// Response 200
{
"ok": true, "total": 142,
"games": [
{
"id": 4711, "ts": 1762293010000, "roomId": "AB1234",
"gameType": "dummy", "gameMode": "fast", "rateName": "5/10/15",
"playerIds": ["user01","user02","user03"],
"winnerId": "user01", "winType": "ตีเต็ม", "rounds": 4
}
]
}source: all (default) / game / wallet
// Request
{
"source": "all", // all | game | wallet
"fromTs": ..., "toTs": ...,
"eventKind": "ตีเต็ม", // หรือ "topup" / "withdraw" สำหรับเครดิต
"gameType": "dummy", // optional
"roomId": "AB12", // optional
"payerId": "...",
"receiverId": "...",
"limit": 30, "offset": 0
}
// Response 200 — แต่ละ row มี kind: 'game' หรือ 'wallet'
{
"ok": true, "total": 89,
"logs": [
{
"kind": "game", "id": "g:1234",
"ts": 1762293010000, "eventKind": "เกิดหัว",
"reason": "เกิดหัว Q♠",
"roomId": "AB1234", "gameType": "dummy",
"payerId": "user02", "receiverId": "user01",
"amount": 10
},
{
"kind": "wallet", "id": "w:5678",
"ts": 1762293000000, "eventKind": "topup",
"reason": "ฝากผ่านสลิป #240",
"targetUserId": "user01", "byUser": "ahz",
"amount": 500
}
]
}// Request
{ "targetId": "user01", "enabled": true }
// Response 200
{ "ok": true, "targetId": "user01", "botsOverride": true }
// Error 400 — target เป็นลูกสาย ไม่ใช่ลูกค้า
{ "ok": false, "error": "ทะลุบอทใช้ได้กับลูกค้า (customer) เท่านั้น" }
// Error 403 — ไม่ใช่ลูกตรง
{ "ok": false, "error": "ตั้งค่าได้เฉพาะลูกตรง" }// Request
{ "targetId": "agent_a", "reason": "ผิดข้อตกลง" }
// Response 200
{ "ok": true, "affected": 8 }// Request
{ "targetId": "agent_a" }
// Response 200
{ "ok": true, "unsuspended": true }// Response 200 — ปกติ
{ "ok": true, "suspended": false }
// Response 200 — ถูกระงับ
{ "ok": true, "suspended": true, "by": "agent_a", "reason": "...", "viaAncestor": true }
Flow โดยรวม: ลูกค้า A ได้ refCode auto-gen → ส่งให้เพื่อน B → เว็บผู้ค้าจับ ?ref=XXX → สมัคร auth/register พร้อม refCode → B เล่นเสียค่าต๋ง → ระบบหัก agent_credit ของ Agent (parent ของ A) ตามเปอร์เซ็นต์ → +aff_wallet ของ A → A เรียก /aff/withdraw → +points
★ ค่าคอม Aff ไม่นับ Tier (ไม่เข้า topups) — ป้องกันโกง tier
★ agent_credit ไม่พอ → mark pending → เติม credit เมื่อไหร่ ระบบจ่ายอัตโนมัติ
// Response 200
{
"ok": true,
"stats": {
"refCode": "X3Y9Z2P1",
"affWallet": 35.50, // ยอดรอถอน
"earnedPaid": 120.00, // คอมที่จ่ายแล้วทั้งหมด
"earnedPending": 25.00, // คอมรอจ่าย (agent_credit ไม่พอ)
"referredCount": 5, // ชวนสำเร็จกี่คน
"settings": {
"commissionPct": 5, // % ค่าคอม
"withdrawSchedule": "anytime" // ตารางถอน
},
"canWithdraw": { "ok": true } // ถอนได้ตอนนี้ไหม (ตามตาราง)
},
"referralCode": "X3Y9Z2P1",
"referralUrl": "https://play.pokstack.com/?ref=X3Y9Z2P1"
}
refCode ก่อนสมัคร — บอกว่าเป็น cross-agent ไหม (preflight ก่อนเรียก register)// Request
{ "refCode": "A8K2X9MP" }
// Response 200 — เจอ + ในสายเดียวกัน
{
"ok": true, "found": true,
"referrerId": "agent_a",
"referrerName": "นาย A",
"crossAgent": false,
"hint": "ในสายเดียวกัน — สร้างได้ปกติ"
}
// Response 200 — เจอแต่ข้ามสาย ★
{
"ok": true, "found": true,
"referrerId": "agent_a",
"referrerName": "นาย A",
"crossAgent": true,
"hint": "ผู้แนะนำอยู่ใต้สาย agent อื่น — ถ้าสมัครใต้คุณ คุณจะจ่ายคอมจาก agent_credit ตัวเอง"
}
// Response 200 — ไม่เจอ
{ "ok": true, "found": false }
aff_wallet → points (ตรวจ schedule, atomic)// Request
{ "amount": 30 }
// Response 200
{
"ok": true,
"amount": 30,
"newAffWallet": 5.50,
"newPoints": 1030,
"withdrawalId": 123
}
// Error — ไม่ถึงวันที่ถอน
{ "ok": false, "error": "วันนี้ยังถอนไม่ได้ (ตารางถอน: monthly — วันที่ 1)" }
// Error — ยอดไม่พอ
{ "ok": false, "error": "aff_wallet ไม่พอ (มี 35.50)" }
// Request
{ "limit": 50 }
// Response
{
"ok": true,
"commissions": [
{ "id": 1, "ts": ..., "amount": 5.00, "baseAmount": 100, "pct": 5,
"referredId": "some_b", "referredName": "นาย B",
"paid": true, "paidTs": ... }
],
"withdrawals": [
{ "id": 1, "ts": ..., "amount": 30 }
]
}
refCode ของเรา (พร้อมยอดคอมที่ได้จากแต่ละคน)// Request
{ "limit": 50 }
// Response
{
"ok": true,
"list": [
{ "id": "agent_b", "displayName": "นาย B",
"createdAt": 1762000000000,
"earned": 25.50 }
]
}
// Response 200
{
"ok": true,
"summary": {
"activeReferrers": 3, // จำนวนลูกค้าที่ชวนเพื่อนสำเร็จ
"totalPaid": 250.00, // คอมที่จ่ายไปแล้วรวม
"totalPending": 50.00, // คอมรอจ่ายรวม (เครดิตไม่พอ)
"customers": [
{
"id": "agent_a", "displayName": "นาย A",
"refCode": "X3Y9Z2P1",
"referredCount": 2,
"earnedPaid": 100, "earnedPending": 25,
"affWallet": 35.50,
"commissionPct": 5,
"withdrawSchedule": "anytime"
}
]
}
}
// Request — fromTs/toTs เป็น Unix ms (optional, default = 30 วันย้อนหลัง)
{
"fromTs": 1762000000000, // optional
"toTs": 1762999999999 // optional
}
// Response 200
{
"ok": true,
"role": "user",
"period": { "from": 1762000000000, "to": 1762999999999 },
"summary": {
"totalAmount": 145.50, // คอมรวม (จ่ายแล้ว+รอจ่าย)
"paidAmount": 120.00,
"pendingAmount": 25.50,
"totalBase": 2910, // ฐานค่าต๋งรวม
"eventCount": 28, // จำนวน event คอม
"uniqueReferred": 5 // ลูกค้าที่ทำคอมให้ในช่วงนี้
},
"daily": [ // breakdown รายวัน (Bangkok TZ, สูงสุด 90 วัน)
{ "date": "2026-05-09", "events": 5, "amount": 25.50, "paid": 20, "pending": 5.50 },
{ "date": "2026-05-08", "events": 3, "amount": 15.00, "paid": 15, "pending": 0 }
],
"perReferred": [ // top 20 ลูกค้าที่ทำเงินสูงสุดในช่วงนี้
{
"id": "agent_b", "name": "นาย B",
"events": 12, "amount": 60.00, "paid": 50, "pending": 10
}
],
"withdrawals": { "total": 100, "count": 3 } // ยอดถอนรวมในช่วง
}
// Request — fromTs/toTs เป็น Unix ms (optional)
{ "fromTs": 1762000000000, "toTs": 1762999999999 }
// Response 200 — โครงสร้างเหมือน /aff/report แต่ role="agent"
{
"ok": true,
"role": "agent",
"period": { "from": ..., "to": ... },
"summary": {
"totalAmount": 850.50, // คอมที่ตัวเองรับผิดชอบจ่าย (ทั้งหมด)
"paidAmount": 700,
"pendingAmount": 150.50, // ยอดที่ยัง "รอจ่าย" — เครดิตไม่พอ
"totalBase": 17010,
"eventCount": 142,
"uniqueReferred": 8
},
"daily": [...],
"perReferred": [ // top 20 ลูกค้าที่ตัวเองต้องจ่ายคอมสูงสุด
{ "id": "agent_b", "name": "นาย B", "events": 30, "amount": 200, "paid": 180, "pending": 20 }
],
"withdrawals": null // ★ agent role ไม่มีตัวเลขถอน (= null)
}
commissionPct + withdrawSchedule per ลูกค้า// Request
{
"userId": "agent_a",
"commissionPct": 7, // 0-100 (default 5%)
"withdrawSchedule": "monthly" // ตารางถอน (ดูข้างล่าง)
}
// withdrawSchedule values:
// "anytime" — ถอนได้ทุกเวลา (default)
// "daily" — ถอนได้ทุกวัน
// "monthly" — ถอนได้เฉพาะวันที่ 1 ของเดือน
// "days:1,15" — ถอนได้เฉพาะวันที่ 1 และ 15 ของเดือน
// "days:1,10,20" — กำหนดเอง (1-31, คั่น ,)
// Response 200
{ "ok": true, "settings": { "commissionPct": 7, "withdrawSchedule": "monthly" } }
// Error 403 — userId ไม่ใช่ลูกใต้สาย
{ "ok": false, "error": "userId ไม่ได้อยู่ใต้สายของคุณ" }
★ API สร้าง user ได้เฉพาะ "customer" เท่านั้น — สร้างลูกสาย (super_senior/senior/master/agent) ต้องใช้หน้าหลังบ้านเท่านั้น
★ ทุก action ผ่าน API จำกัด เฉพาะลูกค้าตรง ของ caller — เติม/ถอนให้ลูกสายต้องใช้หน้าหลังบ้าน
| บทบาท | สร้าง user ผ่าน API | เติม/ถอนเครดิต ผ่าน API | เช็คยอด/ดูข้อมูล | play-link ลูกค้า |
|---|---|---|---|---|
| super_senior | ✗ (สร้างลูกสายใช้หน้าหลังบ้าน) | ✗ ไม่มีลูกค้าตรง | ✓ ใต้สายตัวเอง | ✓ ใต้สายตัวเอง |
| senior | ✗ (สร้างลูกสายใช้หน้าหลังบ้าน) | ✗ ไม่มีลูกค้าตรง | ✓ ใต้สายตัวเอง | ✓ ใต้สายตัวเอง |
| master | ✗ (สร้างลูกสายใช้หน้าหลังบ้าน) | ✗ ไม่มีลูกค้าตรง | ✓ ใต้สายตัวเอง | ✓ ใต้สายตัวเอง |
| agent | ✓ เฉพาะ customer ของตัวเอง | ✓ เฉพาะลูกค้าตรงตัวเอง | ✓ ลูกค้าตัวเอง | ✓ ลูกค้าตัวเอง |
| customer | ✗ | ✗ | เฉพาะตัวเอง | ✗ |
หมายเหตุ 1: สร้างลูกสาย (super_senior/senior/master/agent) ทำผ่าน API ไม่ได้เลย — ต้องใช้หน้าหลังบ้าน ➕ สร้างลูกสาย
หมายเหตุ 2: เติมเครดิตให้ ลูกสาย ผ่าน API ไม่ได้ — ใช้หน้าหลังบ้าน → ปุ่มเติม
หมายเหตุ 3: API ส่วนใหญ่ที่เกี่ยวกับ "agent" หมายถึงเฉพาะ agent role (ระดับล่างสุด ที่มีลูกค้าตรง)
ตัวอย่างด้านล่างใช้ $BASE = URL ของระบบ — ดูได้จากที่อยู่เว็บที่คุณ login เข้ามา (เช่น https://your-domain.com)
# 1) Login
curl -X POST "$BASE/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"agent01","password":"xxx"}'
# → { ok:true, token:"AGENT_TOKEN", playUrl:"...", user:{...} }
# 2a) เช็คเครดิตของตัวเอง (เผื่อพอเติมหรือไม่)
curl -X POST "$BASE/api/v1/wallet/agent-credit" \
-H "Authorization: Bearer AGENT_TOKEN" \
-H "Content-Type: application/json" -d '{}'
# → { ok:true, agentCredit:12500, unlimited:false, canTopupCustomer:true, ... }
# 2b) เติมให้ลูกค้า user01
curl -X POST "$BASE/api/v1/wallet/topup" \
-H "Authorization: Bearer AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"userId":"user01","amount":500,"reason":"เติมรายวัน"}'
# 3) สร้าง play-link ส่งให้ลูกค้า
curl -X POST "$BASE/api/v1/auth/play-link" \
-H "Authorization: Bearer AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId":"user01","ttlMinutes":15}'
# → { ok:true, url:"...autotoken=XXX", ... }
# ส่ง url ให้ลูกค้าคลิก = เข้าเล่นได้เลย ไม่ต้องกรอก password
# รายการห้องที่ user01 เคยเล่น
curl -X POST "$BASE/api/v1/rooms/my" \
-H "Authorization: Bearer AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"userId":"user01","limit":10}'
# รายละเอียดการหักเงินในห้อง AB1234
curl -X POST "$BASE/api/v1/rooms/payments" \
-H "Authorization: Bearer AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"roomId":"AB1234","userId":"user01"}'
# Agent ทุกคนมี API key อัตโนมัติตอนสร้างบัญชี
# ดู key ของตัวเองได้ที่หน้าหลังบ้าน → ปุ่ม "🔑 API Key" (ต้องใส่รหัสผ่าน)
# ใช้ key แทน user token
curl -X POST "$BASE/api/v1/wallet/balance" \
-H "X-API-Key: abc123def456..." \
-d '{"userId":"user01"}'
# 1) ตรวจ refCode ก่อนสมัคร (preflight) — เช็คว่าผู้แนะนำอยู่สายเดียวกันไหม
curl -X POST "$BASE/api/v1/aff/check-code" \
-H "X-API-Key: AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{"refCode":"A8K2X9MP"}'
# → { found:true, crossAgent:false/true, referrerName:"...", ... }
# 2) สมัครลูกค้าใหม่พร้อมผูก refCode
curl -X POST "$BASE/api/v1/auth/register" \
-H "X-API-Key: AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"username":"555",
"password":"1234",
"displayName":"เพื่อนของ A",
"refCode":"A8K2X9MP"
}'
# → 200 + { user, token, referralCode, referralUrl, playUrl, referralBound:true }
# → 403 ถ้าผู้แนะนำอยู่คนละสาย — ต้องเปลี่ยน refCode หรือสมัครโดยไม่ใส่
# 3) ลูกค้าดูยอด aff + ลิงค์ชวนเพื่อน
curl -X POST "$BASE/api/v1/aff/me" \
-H "Authorization: Bearer USER_TOKEN" \
-H "Content-Type: application/json" -d '{}'
# → { stats:{ refCode, affWallet, earnedPaid, earnedPending, ... },
# referralCode, referralUrl }
# 4) ลูกค้าถอน aff_wallet → points
curl -X POST "$BASE/api/v1/aff/withdraw" \
-H "Authorization: Bearer USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"amount":30}'
# 5) Agent ดูสรุปคอม Aff ของลูกค้าตัวเอง + ตั้ง %
curl -X POST "$BASE/api/v1/aff/agent/summary" \
-H "X-API-Key: AGENT_KEY" -d '{}'
curl -X POST "$BASE/api/v1/aff/agent/set-user" \
-H "X-API-Key: AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{"userId":"agent_a","commissionPct":7,"withdrawSchedule":"monthly"}'
# 6) ★ รายงานช่วงเวลา — ลูกค้า (สรุป + daily + top referred)
curl -X POST "$BASE/api/v1/aff/report" \
-H "Authorization: Bearer USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fromTs":1762000000000,"toTs":1762999999999}'
# default = 30 วันย้อนหลัง (ส่ง {} ก็ได้)
# 7) ★ รายงานสำหรับ agent — ยอดที่ตัวเองจ่ายคอม Aff (รายวัน + per-ลูกค้า)
curl -X POST "$BASE/api/v1/aff/agent/report" \
-H "X-API-Key: AGENT_KEY" \
-H "Content-Type: application/json" -d '{}'
<?php
// === register.php ===
$AGENT_KEY = 'YOUR_AGENT_API_KEY';
$BASE = 'YOUR_BASE_URL'; // ★ ใส่ที่อยู่ของระบบที่คุณใช้
// 1) จับ ?ref=XXX จาก URL ตอนเพื่อนคลิกลิงค์
$refCode = $_GET['ref'] ?? null;
// 2) ฟอร์มสมัคร — เก็บ refCode ไว้ใน hidden input
?>
<form method="POST" action="do_register.php">
<input name="username" placeholder="username">
<input name="password" type="password">
<input type="hidden" name="refCode" value="<?=htmlspecialchars($refCode)?>">
<button>สมัคร</button>
</form>
<?php
// === do_register.php ===
$BASE = 'YOUR_BASE_URL';
function api($path, $data, $key) {
global $BASE;
$ch = curl_init("$BASE$path");
curl_setopt_array($ch, [
CURLOPT_POST=>true,
CURLOPT_POSTFIELDS=>json_encode($data),
CURLOPT_HTTPHEADER=>["Content-Type: application/json","X-API-Key: $key"],
CURLOPT_RETURNTRANSFER=>true,
]);
return json_decode(curl_exec($ch), true);
}
$res = api('/api/v1/auth/register', [
'username' => $_POST['username'],
'password' => $_POST['password'],
'refCode' => $_POST['refCode'] ?: null,
], $AGENT_KEY);
if (!empty($res['ok'])) {
// สำเร็จ — เก็บ token, แสดงลิงค์ชวนเพื่อนของตัวเองให้ user คัดลอก
$_SESSION['token'] = $res['token'];
echo "<p>ลิงค์ชวนเพื่อนของคุณ: <a href='{$res['referralUrl']}'>{$res['referralUrl']}</a></p>";
header('Location: '.$res['playUrl']); exit;
} elseif (!empty($res['crossBranch'])) {
echo "<p>ผู้แนะนำ \"{$res['referrerName']}\" อยู่คนละสาย — แนะนำได้เฉพาะสมาชิกในสายเดียวกัน</p>";
}
?>
// Request (body)
{} // ไม่ต้องส่ง params — ดึงตาม subtree ของ caller อัตโนมัติ
// Response 200 — ลิสต์ห้องของสาย
{
"ok": true,
"count": 3,
"rooms": [
{
"roomId": "ABC12XYZ",
"gameType": "dummy",
"roomType": "public",
"rate": { "id": "r3", "name": "เรท 5", "entryFee": 5 },
"gameMode": "fast",
"isPractice": false,
"isTournament": false,
"maxPlayers": 4,
"hostId": "ahalice1",
"dealerId": null,
"createdAt": 1779300000000,
"players": [
{
"id": "ahalice1",
"displayName": "Alice",
"online": true,
"handSize": 7,
"seatIndex": null,
"isMine": true // ★ ลูกค้าใต้สายคุณ
},
{
"id": "fk1a2b3c4d",
"displayName": "บอท1",
"online": true,
"handSize": 8,
"seatIndex": null,
"isMine": false // ★ บอท / นอกสาย
}
],
"gameState": {
"phase": "play",
"currentPlayerId": "ahalice1",
"deckSize": 22,
"discardPileTop": { "id": "K-H" },
"publicMelds": [
{ "ownerId": "ahalice1", "type": "tong", "hasHead": false,
"cards": [{ "rank":"7","suit":"S" }, { "rank":"7","suit":"H" }, { "rank":"7","suit":"D" }] }
],
"headClaimed": false,
"winnerId": null,
"roundCount": 1
}
}
]
}
// ── Error 403 — ไม่มีสิทธิ์ (Super Senior ใน chain ยังไม่เปิด/Admin ยังไม่มอบ)
HTTP 403
{ "ok": false, "error": "ไม่มีสิทธิ์ใช้ Special API — ติดต่อ super senior เปิดสิทธิ์ให้" }
roomDissolved + เด้งกลับ lobby อัตโนมัติ
// Request
{
"roomId": "ABC12XYZ",
"reason": "ลูกค้าร้องเรียน — รีเซ็ตเกม" // optional, max 100 chars
}
// Response 200
{
"ok": true,
"roomId": "ABC12XYZ",
"reason": "ลูกค้าร้องเรียน — รีเซ็ตเกม",
"playersAffected": 3
}
// ── Error 403 — ห้องไม่มีลูกค้าของคุณ
{ "ok": false, "error": "ห้องนี้ไม่มีลูกค้าของคุณ — ยุบไม่ได้" }
// ── Error: ห้องไม่พบ
{ "ok": false, "error": "ไม่พบห้องนี้" }