Skip to content

แดชบอร์ด ​

http
GET /v1/bo/dashboard

คืนตัวเลขรวมของบัญชีคุณ — ยอดรวมที่ทำรายการได้ อัตราจับคู่สำเร็จ และอัตราสำเร็จโดยรวม

ตัวเลขบนแดชบอร์ดอ่านจากฐานข้อมูลสำรอง ห้ามใช้ตัดสินใจเรื่องเงิน

แดชบอร์ดถูกออกแบบให้ เร็ว ไม่ใช่ให้ เป๊ะวินาทีนั้น ตัวเลขที่ผูกพันทางการเงินมี 2 ที่เท่านั้น: GET /v1/pay/balance และชุดกระทบยอด 4 ตัวใน รายงานและการกระทบยอด

ตัวเลขที่ควรอยู่บนหน้าจอแรก ​

จัดเป็น 3 กลุ่ม — กลุ่มแรกคือเงิน กลุ่มที่สองคือคุณภาพของ integration กลุ่มที่สามคือของค้าง

กลุ่ม 1 — เงิน ​

ตัวเลขคำนวณจาก
ยอดพร้อมใช้available_minor
ยอดรอพ้นหน้าต่างข้อพิพาทpending_clearing_minor
ยอดล็อกไว้ให้รายการถอนreserved_minor
ยอดฝากที่รับเข้าสำเร็จวันนี้ผลรวมของ amount_received_minor ของใบที่ settled
ยอด settlement evidence ที่ธนาคารยืนยันวันนี้ผลรวมของ transferred_amount_minor · สูงกว่ายอดรับเข้าได้เมื่อ same-slip overpay; transfer เพิ่มหลัง settle อยู่ใน overpayment.amount_minor ไม่ถูกรวมซ้ำ
มูลค่าเคสยอดเกินที่ยังไม่จบผลรวม Claim.amount_minor ของ overpayment claim สถานะ open, under_review, refund_pending และ return_pending_payout · เป็น operational exposure ไม่ใช่ยอด liability จาก ledger
ค่าธรรมเนียมวันนี้ผลรวมของ fee_total_minor
ยอดเข้ากระเป๋าวันนี้ผลรวมของ credited_amount_minor
ยอดจ่ายออกวันนี้ผลรวมของ filled_minor ของรายการถอน
ยอดคืนกลับวันนี้ผลรวมของ refunded_minor

ยอดโอนจริง ยอดรับเข้า fee และยอดเข้ากระเป๋าต้องแสดงแยกกัน

amount_received_minor = min(transferred_amount_minor, ordered_amount_minor) และ amount_received_minor − fee_total_minor = credited_amount_minor ทุกใบ แดชบอร์ดที่แสดงแค่ตัวเดียวคือแดชบอร์ดที่ฝ่ายบัญชีเอาไปกระทบยอดไม่ได้ และเป็นต้นเหตุของการหยิบตัวเลขผิดไปเครดิตผู้ใช้

กลุ่ม 2 — คุณภาพของ integration ​

ตัวเลขชุดนี้บอกว่า UI ฝั่งคุณ มีปัญหาหรือไม่ ไม่ได้บอกว่าระบบเรามีปัญหา

ตัวเลขสูตรเกณฑ์
อัตราเข้าคิวตรวจสอบใบที่เคยผ่าน under_review ÷ ใบทั้งหมดควร ต่ำกว่า 5% · เกิน 10% = ฟอร์มเลือกบัญชีผู้โอนมีปัญหา
สัดส่วนที่ใช้บัญชีที่เคยยืนยันแล้วใบที่ส่ง sender_id ÷ ใบทั้งหมดยิ่งสูงยิ่งดี · ใบที่ใช้ sender_id เข้าคิวเกือบเป็นศูนย์
อัตรารายการฝากสำเร็จใบที่ settled ÷ ใบที่สร้างทั้งหมดดูคู่กับอัตราหมดอายุ
อัตราหมดอายุใบที่ expired ÷ ใบทั้งหมดสูงผิดปกติ = countdown ในหน้าเว็บไม่ตรง หรือ hardcode อยู่
อัตรารับสลิปย้อนหลังใบที่มี slip.is_late เป็น trueสูง = ผู้ใช้ปิดเบราว์เซอร์ก่อนอัปสลิปเยอะ
อัตราจับคู่สำเร็จของรายการถอนใบที่ settled ÷ ใบที่เข้าสู่การจับคู่ใบที่ expired โดย filled_minor มากกว่า 0 นับเป็นได้บางส่วน
อัตรา webhook ตรวจลายเซ็นไม่ผ่านนับจากฝั่งคุณเอง🔴 ต้องเป็น 0 ถ้าไม่ใช่ 0 คือมีคนยิงปลอมหรือคุณเก็บกุญแจไม่ทัน
อัตรา handler ตอบไม่ใช่ 2xxนับจากฝั่งคุณเองเกิน 1% ใน 5 นาที ⇒ alert

อัตราเข้าคิวตรวจสอบคือ KPI ที่สำคัญที่สุดของ integration

มันไม่ใช่ตัวเลข "คุณภาพของระบบเรา" แต่เป็นตัวเลข "ผู้ใช้กรอกบัญชีผู้โอนถูกหรือไม่" วิธีลดที่ได้ผลที่สุดคือให้ผู้ใช้ เลือก จาก GET /v1/pay/senders แทนการให้ พิมพ์ ระหว่างย้ายระบบ ถ้าอัตรานี้เกิน 10% ⇒ 🔴 อย่าเพิ่มสัดส่วน traffic จนกว่าจะแก้ฟอร์มก่อน

กลุ่ม 3 — ของค้างที่ต้องมีคนดู ​

ตัวเลขทำไมต้องมี
รายการถอนที่ค้าง pending_approvalเกิน 24 ชั่วโมงถูกยกเลิกอัตโนมัติ ⇒ alert ตั้งแต่ 4 ชั่วโมง
รายการฝากที่ค้าง under_review เกิน 4 ชั่วโมงดู review.escalated ประกอบ
ข้อพิพาทที่ยังไม่ตอบเทียบกับ dispute.respond_by — ไม่ตอบ = ยอมรับผล
เรื่องร้องเรียนที่ยังเปิดอยู่เทียบกับ expected_response_at
ช่องว่างของ sequence ที่ค้างเกิน 4 วันช่องว่างสด ๆ ปกติ · ค้างเกิน 4 วัน = event หายจริง
delivery ที่ failed ใน 24 ชั่วโมงล่าสุดถ้าไม่มี success เลยและล้มเกิน 50 ใบ ระบบจะปิด endpoint ให้อัตโนมัติ

คำนวณเองให้ตรงกับ ledger ​

ตัวเลขบนแดชบอร์ดของคุณควรคำนวณจากชุดเดียวกับที่ใช้กระทบยอด ไม่ใช่จาก event ที่สะสมไว้เฉย ๆ

js
// ยอดรวมรายวัน — คำนวณจากชุดกระทบยอด ไม่ใช่จากตาราง event ที่อาจมาไม่ครบ
async function dailyTotals(dayIso) {
  const from = `${dayIso}T00:00:00.000Z`
  const to = new Date(Date.parse(from) + 86_400_000).toISOString().replace(/\.\d{3}Z$/, '.000Z')

  const totals = {
    deposit_count: 0,
    transferred_amount_minor: 0,
    amount_received_minor: 0,
    fee_total_minor: 0,
    credited_amount_minor: 0,
    under_review_count: 0
  }

  let cursor = null
  for (;;) {
    const params = { limit: 200, status: 'settled', updated_after: from }
    if (cursor) params.cursor = cursor
    const body = await call('/v1/pay/deposits', params)
    for (const d of body.data) {
      if (d.settled_at < from || d.settled_at >= to) continue
      const amounts = [d.ordered_amount_minor, d.transferred_amount_minor,
        d.amount_received_minor, d.fee_total_minor, d.credited_amount_minor]
      if (!amounts.every(Number.isSafeInteger) || d.ordered_amount_minor <= 0 ||
          d.transferred_amount_minor <= 0 || d.amount_received_minor <= 0 ||
          d.amount_received_minor !== Math.min(d.transferred_amount_minor, d.ordered_amount_minor) ||
          d.fee_total_minor < 0 || d.fee_total_minor > d.amount_received_minor) {
        throw new Error(`ยอดของ ${d.id} ผิด invariant X/V/A/fee`)
      }
      if (d.amount_received_minor - d.fee_total_minor !== d.credited_amount_minor) {
        throw new Error(`ยอดของ ${d.id} ไม่เป็น A − fee = credited`)
      }
      totals.deposit_count += 1
      totals.transferred_amount_minor += d.transferred_amount_minor
      totals.amount_received_minor += d.amount_received_minor
      totals.fee_total_minor += d.fee_total_minor
      totals.credited_amount_minor += d.credited_amount_minor
      if (d.settled_via === 'review') totals.under_review_count += 1
    }
    if (!body.has_more) break
    cursor = body.next_cursor
  }

  // 🔴 ตรวจยอดรวมซ้ำหลังจากตรวจ A − fee = credited ทีละใบแล้ว
  if (totals.amount_received_minor - totals.fee_total_minor !== totals.credited_amount_minor) {
    throw new Error('ยอดรวมไม่ลงตัว — ดึงข้อมูลมาไม่ครบหรือมีใบที่ผิดปกติ')
  }
  if (totals.transferred_amount_minor < totals.amount_received_minor) throw new Error('ยอดโอนจริงต่ำกว่ายอดรับเข้า')
  return totals
}

// backlog ปัจจุบันต้องอ่าน Claim; Deposit.overpayment เป็น immutable snapshot ตอนเปิด flag
async function unresolvedOverpaymentMinor() {
  let total = 0
  for (const status of ['open', 'under_review', 'refund_pending', 'return_pending_payout']) {
    let cursor = null
    for (;;) {
      const params = { kind: 'overpayment', status }
      if (cursor) params.cursor = cursor
      const body = await call('/v1/bo/claims', params)
      for (const claim of body.data) {
        if (!Number.isSafeInteger(claim.amount_minor) || claim.amount_minor <= 0) {
          throw new Error(`ยอด claim ${claim.claim_id} ไม่ถูกต้อง`)
        }
        total += claim.amount_minor
      }
      if (!body.has_more) break
      cursor = body.next_cursor
    }
  }
  return total
}

ห้ามรวม Deposit.overpayment.amount_minor เพื่อหายอดค้างปัจจุบัน: flag นี้ไม่เปลี่ยนตามผลของ Claim และ deposit เดียวอาจมี claim จาก transfer เพิ่มได้มากกว่าหนึ่งใบ. ใช้ Claim เป็น source of truth เสมอ ตัวเลขนี้เป็นมูลค่าเคส ไม่ใช่ยอด OVERPAYMENT_REFUND_PAYABLE: ระหว่าง return_pending_payout original-transfer return อาจดับ liability แล้ว แต่ยังมี payout/recovery race ที่ต้องติดตาม

แสดงเวลาที่คำนวณเสมอ

ทุกตัวเลขบนจอควรมีบรรทัด "ข้อมูล ณ …" ที่มาจาก as_of ของ GET /v1/pay/balance หรือเวลาที่ job ของคุณรันเสร็จ แดชบอร์ดที่ไม่บอกเวลา คือแดชบอร์ดที่คนจะเถียงกันว่าใครถูกตอนตัวเลขไม่ตรง

ตัวเลขที่คุณจะไม่ได้ — และเหตุผล ​

อยากเห็นได้ไหมเพราะ
ปริมาณสภาพคล่องในระบบ / จำนวนผู้ถอนที่รออยู่❌เป็นข้อมูลการแข่งขันของผู้ให้บริการชั้นบน · สิ่งที่คุณเห็นได้คือ liquidity.estimated_wait_ms เฉพาะออร์เดอร์ของคุณเอง
เวลาที่เจ้าหน้าที่ต้องตัดสินให้เสร็จ❌คิว support เป็นของชั้นบน · ส่งให้คุณคือ review.estimated_resolution_bucket
ค่าธรรมเนียมแยกตามว่าใครได้ส่วนแบ่ง❌fee_breakdown[] แยกตาม component (ประเภทค่าธรรมเนียม) เท่านั้น
ตัวตนของช่องทางสำรองที่ใช้❌คำเดียวที่คุณเห็นคือ liquidity_source ที่มีค่าได้แค่ internal หรือ external_gateway
ข้อมูลของคู่กรณีอีกฝั่ง❌ระบบไม่เปิดเผยข้อมูลข้ามบัญชีหรือข้ามผู้ให้บริการ · id ของ fill ที่คุณเห็น (leg_ref) ทั้งสองฝั่งได้ค่าคนละตัวและ correlate ข้ามกันไม่ได้

liquidity_source ห้ามเอาไปตัดสินใจเรื่องเงิน

มันมีไว้ทำสถิติและตอบ support เท่านั้น เงื่อนไขการเครดิตมีข้อเดียวคือ status = 'settled'

เกณฑ์ alert ที่ควรมีตั้งแต่วันแรก ​

  • อัตราเข้าคิวตรวจสอบเกินเกณฑ์ที่คุณตั้งไว้ (สัญญาณว่าฟอร์มผู้โอนมีปัญหา)
  • ตรวจลายเซ็น webhook ไม่ผ่าน แม้แต่ครั้งเดียว
  • handler ตอบไม่ใช่ 2xx เกิน 1% ใน 5 นาที
  • job กระทบยอดรายวันออก exit code ไม่เป็น 0
  • ช่องว่างของ sequence ค้างเกิน 4 วัน
  • รายการถอนค้าง pending_approval เกิน 4 ชั่วโมง
  • ข้อพิพาทที่เหลือเวลาไม่ถึงครึ่งของหน้าต่างและยังไม่ตอบ
  • ได้ deposit.reversed หรือ balance.debited (ควรแจ้งคนทันที ไม่ใช่แค่บันทึก)

ขั้นต่อไป ​

เอกสารชุดนี้อิงสัญญา API เวอร์ชัน 2026-08-01