Skip to content

รายชื่อธนาคาร ​

GET /v1/pay/banks คือแหล่งเดียวของรายชื่อธนาคารที่ใช้ในฟอร์มบัญชีผู้โอน

อย่า hardcode รายชื่อธนาคาร

ธนาคารถูกเปิด/ปิดรับรายการใหม่ได้ตลอดเวลา (ปิดปรับปรุงระบบ · ปัญหาชั่วคราว · นโยบายความเสี่ยง) · ถ้าคุณ hardcode ลิสต์ไว้ ผู้ใช้จะเลือกธนาคารที่ปิดอยู่ กรอกเลขบัญชีจนครบ แล้วไปเจอ 409 sender_bank_unavailable ที่ปุ่มยืนยัน

นั่นคือจุดที่คนทิ้งรายการมากที่สุดในหน้าเติมเงิน — เพราะเขาเสียเวลาไปแล้วทั้งหมด


Request ​

bash
curl https://api.example-wl.com/v1/pay/banks \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "PG-Timestamp: 1755749400" \
  -H "PG-Nonce: 01JQA5TG4T6V8X0Z2B4D6F8H0K" \
  -H "PG-Version: 2026-08-01" \
  -H "PG-Signature: v1=0d5e...71fa"

Response ​

json
{
  "data": [
    { "bank_code": "002", "name_th": "ธนาคารกรุงเทพ", "name_en": "Bangkok Bank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "004", "name_th": "ธนาคารกสิกรไทย", "name_en": "Kasikornbank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "006", "name_th": "ธนาคารกรุงไทย", "name_en": "Krungthai Bank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "011", "name_th": "ธนาคารทหารไทยธนชาต", "name_en": "TMBThanachart Bank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "014", "name_th": "ธนาคารไทยพาณิชย์", "name_en": "Siam Commercial Bank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "025", "name_th": "ธนาคารกรุงศรีอยุธยา", "name_en": "Bank of Ayudhya", "available": false, "message_th": "ธนาคารปิดปรับปรุงระบบชั่วคราว กรุณาเลือกธนาคารอื่น", "side": ["sender", "receiver"] },
    { "bank_code": "030", "name_th": "ธนาคารออมสิน", "name_en": "Government Savings Bank", "available": true, "side": ["sender"] },
    { "bank_code": "034", "name_th": "ธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร", "name_en": "Bank for Agriculture and Agricultural Cooperatives", "available": true, "side": ["sender"] },
    { "bank_code": "069", "name_th": "ธนาคารเกียรตินาคินภัทร", "name_en": "Kiatnakin Phatra Bank", "available": true, "side": ["sender", "receiver"] },
    { "bank_code": "022", "name_th": "ธนาคารซีไอเอ็มบี ไทย", "name_en": "CIMB Thai Bank", "available": false, "message_th": "ธนาคารนี้ปิดรับรายการใหม่ชั่วคราว", "side": ["sender"] }
  ]
}

(รายการด้านบนเป็นตัวอย่างรูปแบบ ค่าจริงต้องดึงจาก endpoint เสมอ)

fieldชนิดมีเสมอความหมาย
bank_codestring✅รหัส 3 หลัก · ค่านี้คือค่าที่ส่งเข้า sender.bank_code ตอนสร้างรายการ
name_thstring✅ชื่อไทยสำหรับแสดงผล
name_enstring✅ชื่ออังกฤษสำหรับแสดงผล
availableboolean✅🔴 false = ปิดรับรายการใหม่ ต้องเทาปุ่ม
message_thstring❌เหตุผลที่แสดงให้ผู้ใช้เห็นได้ · optional แปลว่าอาจไม่มี key นี้เลย ไม่ใช่ null — โค้ดต้องรองรับกรณีไม่มี key
sidestring[]✅ธนาคารนี้ใช้ได้ในฝั่งไหน — "sender" = ใช้เป็นบัญชีผู้โอนได้ · "receiver" = เป็นบัญชีปลายทางได้

message_th เป็น optional จริง ๆ

js
// ❌ พังเมื่อไม่มี key
const reason = bank.message_th.trim()

// ✅
const reason = bank.message_th ?? 'ธนาคารนี้ปิดรับรายการใหม่ชั่วคราว'

เอาไปใช้ยังไง ​

1. กรองด้วย side ก่อน ​

ฟอร์มบัญชีผู้โอนต้องแสดงเฉพาะธนาคารที่มี "sender" ใน side · ธนาคารที่ไม่มีจะใช้เป็นบัญชีผู้โอนไม่ได้ตั้งแต่ต้น อย่าเอามาแสดงเลย

2. เทาปุ่มด้วย available — อย่าซ่อน ​

เทา ไม่ใช่ซ่อน

ถ้าซ่อนธนาคารที่ปิดออกจากลิสต์ ผู้ใช้ที่มีบัญชีอยู่แค่ธนาคารนั้นจะหาไม่เจอแล้วคิดว่าระบบพัง แล้วโทรหา support

ถ้าเทาไว้พร้อมข้อความ เขาจะรู้ทันทีว่าเกิดอะไรขึ้นและเลือกทางอื่นเอง

คุณจะโอนจากบัญชีไหน

  ( ) ธนาคารกรุงเทพ
  ( ) ธนาคารกสิกรไทย
  ( ) ธนาคารกรุงไทย
  ( ) ธนาคารไทยพาณิชย์
  (·) ธนาคารกรุงศรีอยุธยา        ← เทา กดไม่ได้
      ธนาคารปิดปรับปรุงระบบชั่วคราว กรุณาเลือกธนาคารอื่น

3. cache ได้ แต่ต้อง refresh ​

รายชื่อนี้ cache ได้ และควร cache — refresh ทุก ~5 นาที ก็เพียงพอ · bucket ของ endpoint นี้กว้างมาก (3,000 ครั้ง/นาที ต่อ key) แต่ไม่มีเหตุผลที่จะยิงทุก request

js
// Node — cache 5 นาที พร้อม fallback เป็นค่าเดิมถ้า refresh พลาด
let cache = { at: 0, banks: [] }
const TTL_MS = 5 * 60 * 1000

async function getBanks() {
  if (Date.now() - cache.at < TTL_MS && cache.banks.length) return cache.banks

  try {
    const res = await fetch('https://api.example-wl.com/v1/pay/banks', {
      headers: sign('GET', '/v1/pay/banks', '', '', null),
    })
    const { data } = await res.json()
    cache = { at: Date.now(), banks: data }
  } catch (err) {
    // 🔴 refresh พลาดแล้วยังมีของเก่า ให้ใช้ของเก่าต่อ ดีกว่าโชว์ฟอร์มเปล่า
    if (!cache.banks.length) throw err
  }
  return cache.banks
}

async function senderBankOptions() {
  const banks = await getBanks()
  return banks
    .filter((b) => b.side.includes('sender'))
    .map((b) => ({
      value: b.bank_code,
      label: b.name_th,
      disabled: b.available !== true,
      hint: b.available ? undefined : (b.message_th ?? 'ธนาคารนี้ปิดรับรายการใหม่ชั่วคราว'),
    }))
}
php
<?php
// PHP — cache ลงไฟล์/APCu แล้วกรองเฉพาะฝั่งผู้โอน
function senderBankOptions(array $headers): array
{
    $banks = apcu_fetch('pg_banks');
    if ($banks === false) {
        $res   = httpGet('https://api.example-wl.com/v1/pay/banks', $headers);
        $banks = $res['data'];
        apcu_store('pg_banks', $banks, 300);   // 5 นาที
    }

    $out = [];
    foreach ($banks as $b) {
        if (!in_array('sender', $b['side'], true)) {
            continue;
        }
        $out[] = [
            'value'    => $b['bank_code'],
            'label'    => $b['name_th'],
            'disabled' => ($b['available'] !== true),
            'hint'     => $b['available']
                ? null
                : ($b['message_th'] ?? 'ธนาคารนี้ปิดรับรายการใหม่ชั่วคราว'),
        ];
    }
    return $out;
}

ธนาคารถูกปิดแล้วเกิดอะไรขึ้นบ้าง ​

การปิดธนาคารกระทบแค่ 2 อย่าง

  1. POST /v1/pay/deposits ที่ใช้ธนาคารนั้นเป็นบัญชีผู้โอน → 409 sender_bank_unavailable
  2. ค่า available ใน GET /v1/pay/banks เปลี่ยนเป็น false

🔴 รายการที่เปิดอยู่แล้วไม่ขยับแม้แต่ใบเดียว — ไม่มี event ไม่มี version ใหม่ ไม่มีการยกเลิกอัตโนมัติ · และ สลิปของรายการเดิมยังส่งได้เสมอ ต่อให้ธนาคารต้นทางเพิ่งถูกปิดไปเมื่อกี้

⇒ อย่าเขียน job ที่ไล่ยกเลิกรายการค้างเมื่อเห็นธนาคารถูกปิด นั่นคือการปิดทางผู้ใช้ที่กำลังจะโอน

รับมือกับ 409 sender_bank_unavailable ​

json
{
  "error": {
    "code": "sender_bank_unavailable",
    "type": "invalid_request_error",
    "message": "The sending bank is not accepting new deposits right now.",
    "message_th": "ธนาคารต้นทางปิดรับรายการใหม่ชั่วคราว",
    "retryable": true,
    "retry_after_seconds": null,
    "request_id": "req_01JQA5TH6U8W0Y2A4C6E8G0J2L",
    "details": {
      "bank_code": "025",
      "retry_after_hint_seconds": 1800,
      "public_message_th": "ธนาคารกรุงศรีอยุธยาปิดปรับปรุงระบบชั่วคราว กรุณาเลือกธนาคารอื่น",
      "alternative_banks": ["002", "004", "014"]
    }
  }
}
detailsเอาไปทำอะไร
public_message_thแสดงให้ผู้ใช้เห็นตรง ๆ ได้เลย ไม่ต้องเขียนเอง
alternative_banks[]เลื่อนธนาคารเหล่านี้ขึ้นบนสุดของลิสต์ให้ผู้ใช้เลือกต่อทันที
retry_after_hint_secondsคาดว่าอีกนานเท่าไหร่จะกลับมา — ใช้ตัดสินใจว่าจะบอกให้ "ลองใหม่ภายหลัง" หรือ "เลือกธนาคารอื่น"
js
// Node — พาผู้ใช้ไปต่อแทนการโชว์ error เปล่า
catch (err) {
  if (err.code === 'sender_bank_unavailable') {
    await refreshBankCache()                    // ค่าใน cache เก่าไปแล้ว
    return ui.pickAnotherBank({
      message: err.details.public_message_th,
      highlight: err.details.alternative_banks, // เลื่อนขึ้นบนสุด
    })
  }
  throw err
}

available: false กับ BANK_DISABLED คนละเรื่องกัน ​

สองอย่างนี้ชื่อคล้ายกันจนสับสน แต่เกิดคนละจุดและต้องรับมือคนละแบบ

available: falsereasons: ['BANK_DISABLED']
เห็นที่ไหนGET /v1/pay/banksdeposit.under_review หลังอัปสลิป
เกิดตอนไหนก่อนสร้างรายการหลังผู้ใช้โอนและอัปสลิปแล้ว
ผลกับรายการสร้างไม่ได้ → 409รายการเข้าคิวเจ้าหน้าที่ แล้วเจ้าหน้าที่ตรวจด้วยตนเอง
เงินเป็นยังไงยังไม่มีเงินเกี่ยวข้องโอนไปแล้ว 🔴 ไม่หาย และไม่ถูกปฏิเสธอัตโนมัติ
คุณต้องทำเทาปุ่มในฟอร์มแสดง review.reasons[0].display_th · ห้ามมีปุ่ม "โอนใหม่"

⇒ ธนาคารที่ available: true ก็ยังทำให้รายการเข้าคิว BANK_DISABLED ได้ เพราะการตรวจสลิปอัตโนมัติของธนาคารนั้นอาจถูกปิดชั่วคราวแยกจากการเปิดรับรายการใหม่ · นี่ไม่ใช่ error และไม่ใช่การปฏิเสธเงิน


เช็กก่อนขึ้น production ​

  • [ ] ฟอร์มบัญชีผู้โอนดึงลิสต์จาก GET /v1/pay/banks ไม่ใช่จากค่าคงที่ในโค้ด
  • [ ] กรองด้วย side ให้เหลือเฉพาะธนาคารที่ใช้เป็นบัญชีผู้โอนได้
  • [ ] available: false → เทา ไม่ใช่ซ่อน · แสดง message_th ถ้ามี
  • [ ] โค้ดรองรับกรณีที่ ไม่มี key message_th (ไม่ใช่แค่กรณี null)
  • [ ] cache 5 นาที และ fallback เป็นค่าเดิมเมื่อ refresh พลาด
  • [ ] 409 sender_bank_unavailable พาผู้ใช้ไปเลือกจาก alternative_banks[] ไม่ใช่ขึ้น error เปล่า
  • [ ] ไม่มี job ที่ยกเลิกรายการค้างเมื่อธนาคารถูกปิด

ขั้นต่อไป ​

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