← ภาพรวมบัญชี · ฝาก–ถอนให้ลูกค้า · เชื่อม LINE OA

Merchant Integration Guide

คู่มือเชื่อมต่อระบบสำหรับร้านค้า

อ่านจบหน้าเดียว เชื่อมต่อได้ด้วยตัวเอง — ระบบเดิมของร้านยังทำงานตามปกติทุกอย่าง

เริ่มใช้งานผ่านหน้าเว็บ

  1. เจ้าของ: เปิด เอเยนต์และเครือข่าย → สร้างบัญชีเอเยนต์ กำหนดรหัสเองหรือเว้นว่างให้ระบบสร้าง แล้วคัดลอกชื่อผู้ใช้และรหัสผ่านจากหน้าต่างยืนยันก่อนปิด
  2. เอเยนต์: เปิด ร้านค้าของฉัน → เพิ่มร้านค้าในสาย ใส่ชื่อร้านและข้อมูลการเชื่อมต่อ ใช้ปุ่ม สร้างรหัสผ่านให้ หรือกำหนดรหัส 32–128 ตัวอักษร รหัสนี้เป็นทั้งรหัสเข้าเว็บและ API Key ของร้าน คัดลอกข้อมูลหลังสร้างสำเร็จ
  3. ร้านค้า: เข้าด้วยรหัสร้านค้าและรหัสผ่าน เปิด ฝาก–ถอน / อนุมัติคำขอ ตั้งบัญชีรับเงินของร้านก่อน แล้วระบุลูกค้าและจำนวนเงินเพื่อสร้างรายการ
  4. ฝากเงิน: เปิด ดูรายละเอียด → เปิดหน้าชำระเงิน / แนบสลิป หรือคัดลอกลิงก์ให้ลูกค้า เมื่อลูกค้าโอนแล้วให้แนบสลิปของรายการนั้น ระบบจะแสดงยืนยันฝากเมื่อมีผลตรวจผ่าน
  5. ถอนเงิน: ตรวจบัญชีและอนุมัติคำขอจากลูกค้าก่อนเข้าคิว เมื่อครบเวลาจับคู่ ถ้ามีส่วนที่ร้านต้องจ่าย รายละเอียดจะแสดงจำนวนและบัญชีให้ตรวจ ร้านโอนส่วนนี้เองแล้วแนบสลิป ระบบตรวจหลักฐานก่อนยืนยัน
  6. ถ้าการตรวจช้าหรือคำตอบขาดหาย: ใช้ ตรวจสถานะหลังส่งสลิป หรือ ตรวจผลสลิปเดิม อย่าสร้างรายการใหม่หรือโอนซ้ำ สามารถเปิดหลักฐานที่ยืนยันแล้วจากรายละเอียดรายการ

ร้านใหม่ต้องตั้งค่าบัญชีรับเงินและหลักประกันให้พร้อมก่อนรับรายการ การใช้ผ่าน LINE ต้องตั้งค่าและเปิด LINE OA ของร้านแยกต่างหาก ส่วนคู่มือ API สำหรับเชื่อมระบบอยู่ด้านล่าง

1. ระบบนี้ทำอะไรให้ร้านค้า

ระบบจับคู่ "ลูกค้าที่กำลังฝาก" กับ "ลูกค้าที่กำลังถอน" ให้โอนเงินถึงกันโดยตรง (peer-to-peer) พร้อม escrow และตรวจสลิปอัตโนมัติ ผลคือ:

เลือกรูปแบบการเชื่อมต่อได้ 2 แบบ (หรือผสมทั้งสอง)

Mode B — เต็มรูปแบบMode A — เสริมระบบเดิม (overlay)
เหมาะกับร้านที่อยากให้เราดูแลทุกรายการร้านที่มีระบบฝาก–ถอนออโต้อยู่แล้ว
วิธีทำงานส่งทุกรายการเข้าระบบเรา เราจับคู่และดูแลจนจบถามเราก่อนว่า "ตอนนี้มีคู่ไหม?" มีคู่ = ยกให้เรา ไม่มี = ใช้ระบบร้านตามเดิม
ไม่มีคู่ร้านรับ/จ่ายแทนอัตโนมัติ (ข้อ 8)ไม่มีอะไรเกิดขึ้นในระบบเราเลย
Endpoint หลักPOST /api/deposit, POST /api/withdrawPOST /api/deposit/try, POST /api/withdraw/try

2. สิ่งที่จะได้รับจากผู้ดูแลระบบ

สิ่งที่ได้รับใช้ทำอะไร
BASE_URLที่อยู่ API — สำหรับระบบนี้คือ (โหลดอัตโนมัติ)
ชื่อร้าน (merchant name)ใช้เป็น username ตอน login จัดการตั้งค่า
API_KEYใส่ header X-API-Key ทุกครั้งที่ยิง API (ความลับสูงสุด)
WEBHOOK_SECRETใช้ตรวจลายเซ็น webhook ที่เราส่งไปหาร้าน
เก็บ API key ใน secret manager ของร้าน ห้าม hardcode ในโค้ดหรือ commit ลง git

3. เริ่มใช้ใน 5 นาที (โหมด sandbox — ไม่แตะเงินจริง)

ทุก endpoint รองรับ header x-sandbox: true สำหรับทดสอบ ไม่กระทบยอดเงินใด ๆ

# 1) สร้างรายการฝากทดสอบ
curl -X POST "{{BASE}}/api/deposit" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "x-sandbox: true" \
  -d '{
    "merchantUserId": "customer-001",
    "amount": "100.0000",
    "idempotencyKey": "test-dep-001"
  }'
# ตอบกลับ: { "id": 123, "uuid": "…", "status": "WAITING_PAYMENT", … }

# 2) เปิดหน้ารายการ (ลูกค้าเห็นบัญชีปลายทาง + ปุ่มแนบสลิป)
curl "{{BASE}}/api/checkout/<uuid>"

# 3) จำลองว่าลูกค้าโอนแล้ว (ต้องแนบ uuid เป็นหลักฐานการครอบครองรายการ)
curl -X POST "{{BASE}}/api/transaction/<id>/transferred" \
  -H "Content-Type: application/json" \
  -d '{ "uuid": "<uuid>" }'

# 4) เช็คสถานะ
curl "{{BASE}}/api/transaction/<id>"
ถ้าทั้ง 4 ขั้นตอบตามคาด แปลว่า key ใช้ได้และร้านพร้อมเชื่อมของจริง

4. เข้าใจ Gas และ Credit (สำคัญ — อ่านก่อนใช้เงินจริง)

ผู้ดูแลระบบจะเติม gas/credit เริ่มต้นให้ตามที่ตกลง — ถ้าเจอ Insufficient Gas Balance / Insufficient Credit Balance แปลว่าต้องเติมวงเงินก่อน

ลิมิตต่อผู้ใช้: ระบบจัดเกรดลูกค้าอัตโนมัติ (NEW → C → B → A) ลูกค้าใหม่เริ่มที่ 1,000/รายการ ขยับขึ้นตามประวัติที่ดี สูงสุดเกรด A ที่ 50,000/รายการ — ป้องกันบัญชีม้าโดยร้านไม่ต้องทำอะไรเพิ่ม

5. จัดการตั้งค่าเอง (รวมเปลี่ยนบัญชีธนาคารของร้าน)

5.1 login รับ session token

curl -X POST "{{BASE}}/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{ "username": "<ชื่อร้าน>", "password": "<API_KEY>" }'
# ตอบกลับ: { "token": "…", "role": "merchant", … }   (อายุ 24 ชม.)

5.2 แก้ไขตั้งค่า / เปลี่ยนบัญชีธนาคาร

ต้องส่งครบ 3 อย่าง: Bearer token (จาก 5.1) + X-API-Key + confirmApiKey (ยืนยัน key ซ้ำใน body — กันการแก้ค่าการเงินโดยไม่ตั้งใจ):

curl -X PATCH "{{BASE}}/api/merchant/settings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUrl": "https://your-system.example.com/webhooks/gateway",
    "bankName": "SCB",
    "bankAccountNo": "111-2-33333-4",
    "bankAccountName": "Your Shop Co., Ltd.",
    "confirmApiKey": "<API_KEY>"
  }'

ฟิลด์ที่ตั้งเองได้: webhookUrl, bankName, bankAccountNo (ระบบเข้ารหัสเก็บให้), bankAccountName, allowedIps, webhookEncryptionEnabled, autoVerifySlip, notificationEmail, webhookRetryInterval, checkoutLogoUrl, checkoutPrimaryColor

บัญชีธนาคารของร้านถูกใช้เมื่อ (ก) deposit ไม่มีคู่ → ลูกค้าโอนเข้าบัญชีนี้ และ (ข) เป็นบัญชีต้นทางเวลาระบบออโต้ของร้านจ่ายถอนแทน (ข้อ 8)

5.3 หมุน API key (แนะนำทำเป็นระยะ)

curl -X POST "{{BASE}}/api/merchant/rotate-api-key" \
  -H "X-API-Key: $API_KEY" \
  -d '{ "expiresHours": 24 }'
# ได้ key ใหม่ทันที; key เก่าใช้ต่อได้ตามชั่วโมงที่กำหนด (transition period)

6. Webhook — ระบบแจ้งผลกลับหาร้านแบบ real-time

ทุกครั้งที่รายการเปลี่ยนสถานะ เรา POST ไปที่ webhookUrl ของร้าน:

{
  "event": "transaction.completed",
  "data": {
    "id": 123, "type": "DEPOSIT", "amount": "100.0000",
    "status": "COMPLETED", "merchantId": 1, "userId": 45,
    "idempotencyKey": "…", "createdAt": "…"
  }
}

Event หลัก: transaction.waiting_payment, transaction.paid_unconfirmed, transaction.completed, transaction.cancelled, transaction.disputed, transaction.gas_settled, payout.required (ข้อ 8) และ confirmation.reminder — ระบบทักถามอัตโนมัติเมื่อฝั่งผู้ถอนยังไม่กดยืนยันเกิน 10 นาที (ตั้งค่าได้) ร้านใช้ event นี้เด้งถามลูกค้าว่า "ได้รับเงินแล้วหรือยัง?" โดย data.uuid พาลูกค้าไปหน้ารายการได้โดยตรง

6.1 ตรวจลายเซ็น (จำเป็น — ห้ามเชื่อ webhook ที่ไม่ผ่านการตรวจ)

Header X-Webhook-Signature = HMAC-SHA256 ของ raw body ด้วย WEBHOOK_SECRET:

import crypto from 'crypto';
import express from 'express';

const app = express();
app.post('/webhooks/gateway', express.json({
  verify: (req, _res, buf) => { req.rawBody = buf; },
}), (req, res) => {
  const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(req.rawBody).digest('hex');
  const got = req.get('X-Webhook-Signature') || '';
  if (got.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) {
    return res.status(401).end();
  }
  res.status(200).end();          // ตอบ 200 ให้เร็ว แล้วค่อยประมวลผลต่อ
  handleEvent(req.body);          // ทำงานหนักแบบ async
});

7. Mode B — เชื่อมเต็มรูปแบบ

7.1 ฝากเงิน (ลูกค้าเติมเครดิต)

POST /api/deposit   (X-API-Key)
body: { merchantUserId, amount, idempotencyKey, redirectUrl?, slipUrl? }

Flow: สร้างรายการ → ระบบจับคู่กับคนถอน (ปกติภายในวินาที) → ร้านพาลูกค้าไปหน้า GET /api/checkout/<uuid> (มีเลขบัญชีปลายทาง + ปุ่มแนบสลิป) → ลูกค้าโอน + กด "โอนแล้ว" → ระบบตรวจสลิป/รอผู้ถอนยืนยัน → webhook completed → ร้านเติมเครดิตให้ลูกค้าในระบบร้าน

7.2 ถอนเงิน (ลูกค้าเอาเงินออก)

POST /api/withdraw   (X-API-Key)
body: { merchantUserId, amount, idempotencyKey,
        bankName, bankAccountNo, bankAccountName }   ← บัญชีของลูกค้าผู้รับเงิน

Flow: ระบบจับคู่กับคนฝาก → คนฝากโอนเข้าบัญชีลูกค้าของร้านโดยตรง → สลิปผ่าน/ผู้ถอนกดยืนยัน/ครบหน้าต่างโต้แย้ง → webhook completed

7.3 การยืนยันรายการ (3 ชั้น อัตโนมัติทั้งหมด)

  1. ผู้ถอนกดยืนยันรับเงิน — จบทันที ไม่ต้องใช้สลิป (POST /api/transaction/:id/release)
  2. สลิปตรวจผ่าน — ระบบปล่อยให้เองไม่ต้องรอใครกด
  3. เงียบจนครบหน้าต่างโต้แย้ง (ค่าเริ่มต้น 30 นาที) — ปล่อยอัตโนมัติ ฝั่งที่เงียบโดนหักคะแนนความน่าเชื่อถือ · มีปัญหาจริง → POST /api/transaction/:id/dispute เครดิตถูกล็อกไว้ให้ตรวจสอบ

8. Auto-payout — ให้ระบบออโต้ของร้านจ่ายแทนเมื่อไม่มีคู่ (opt-in)

สำหรับร้านที่มีระบบโอนเงินอัตโนมัติ: เมื่อ withdraw ไม่มีคู่ (หรือเหลือเศษ) ระบบจะยิง webhook payout.required บอกร้านว่า "โอน X บาท ไปบัญชีนี้":

{
  "event": "payout.required",
  "data": {
    "id": 456, "payoutRef": "b1f0-…", "payoutAmount": "300.0000",
    "bankName": "KBank", "bankAccountNo": "123-4-56789-0",
    "bankAccountName": "Somchai J."
  }
}

ระบบร้านโอนเงินแล้วยืนยันกลับ:

curl -X POST "{{BASE}}/api/transaction/456/payout-confirm" \
  -H "X-API-Key: $API_KEY" \
  -d '{ "payoutRef": "b1f0-…", "bankRef": "<เลขอ้างอิงโอนจากธนาคารร้าน>" }'

9. Mode A — เสริมระบบเดิม (try-match)

ก่อนร้านจะประมวลผลด้วยระบบตัวเอง ถามเราหนึ่งครั้ง:

# ฝั่งถอน: มีคนฝากรออยู่พอดีไหม?
curl -X POST "{{BASE}}/api/withdraw/try" \
  -H "X-API-Key: $API_KEY" \
  -d '{ "merchantUserId": "c1", "amount": "500.0000",
        "idempotencyKey": "try-w-001",
        "bankName": "KBank", "bankAccountNo": "…", "bankAccountName": "…" }'

# matched:true  → เราจัดการ P2P ให้ทั้งรายการ (ติดตามผลทาง webhook)
# matched:false → "ไม่มีอะไรถูกสร้างเลย" ร้านโอนเองด้วยระบบเดิมได้ทันที
# ฝั่งฝาก: มีคนถอนรอรับพอดีไหม?
curl -X POST "{{BASE}}/api/deposit/try" -H "X-API-Key: $API_KEY" \
  -d '{ "merchantUserId": "c2", "amount": "500.0000", "idempotencyKey": "try-d-001" }'
# matched:true → ได้ bankDetails ของผู้รับ ให้ลูกค้าโอนตรง
# matched:false → ให้ลูกค้าโอนเข้าบัญชีร้านตามระบบเดิม
รับประกัน: matched:false = ไม่มีร่องรอยค้างในระบบ ใช้ idempotencyKey เดิมลองใหม่ได้เสมอ
ร้านเปิดใหม่: ช่วงแรกระบบจะจำกัดวงเงิน "จับคู่ข้ามร้าน" อัตโนมัติ (เพดานเพิ่มขึ้นตามอายุและประวัติของร้าน) — ช่วง warm-up จึงอาจได้ matched:false บ่อยกว่าปกติ ซึ่งไม่ใช่ความผิดพลาด: ร้านใช้ระบบเดิมของตัวเองไปตามปกติ ส่วนการจับคู่ภายในร้านเดียวกันใช้ได้เต็มที่ตั้งแต่วันแรก

10. ตัวเลือกความปลอดภัยเพิ่มเติม (แนะนำเปิดเมื่อขึ้นจริง)

ตัวเลือกวิธีเปิดผล
IP allowlistallowedIps: "1.2.3.4, 10.0.0.0/24" ใน settingsยิง API ได้เฉพาะจาก IP ร้าน
Webhook encryptionwebhookEncryptionEnabled: truepayload เข้ารหัส AES-256-GCM
Key rotationPOST /api/merchant/rotate-api-keyเปลี่ยน key โดยไม่มี downtime

11. Checklist ก่อนเปิดใช้จริง

12. อ้างอิงย่อ

สถานะรายการ (lifecycle)

PENDING_MATCH → WAITING_PAYMENT → PAID_UNCONFIRMED → COMPLETED │ │ └→ DISPUTED → (COMPLETED | CANCELLED) │ └→ CANCELLED └→ GAS_SETTLED (ปิดผ่านบัญชีร้าน/สลิป instant)

Endpoint ทั้งหมดที่ร้านใช้

MethodPathAuthใช้ทำอะไร
POST/api/depositX-API-Keyสร้างรายการฝาก
POST/api/withdrawX-API-Keyสร้างรายการถอน
POST/api/deposit/tryX-API-Keyถามจับคู่ฝากแบบทันที (Mode A)
POST/api/withdraw/tryX-API-Keyถามจับคู่ถอนแบบทันที (Mode A)
GET/api/checkout/:idOrUuidหน้ารายการสำหรับลูกค้า
POST/api/transaction/:id/transferreduuid หรือ X-API-Keyแจ้ง "โอนแล้ว" (+สลิป)
POST/api/transaction/:id/releaseX-API-Keyยืนยันรับเงิน/ปล่อยเครดิต
POST/api/transaction/:id/disputeX-API-Keyเปิดข้อโต้แย้ง
POST/api/transaction/:id/payout-confirmX-API-Keyยืนยันจ่ายแทนแล้ว (auto-payout)
GET/api/transaction/:idเช็คสถานะรายการ
POST/api/auth/loginname+keyรับ session token จัดการตั้งค่า
PATCH/api/merchant/settingsBearer + X-API-Key + confirmApiKeyตั้งค่า/เปลี่ยนบัญชีธนาคาร
POST/api/merchant/rotate-api-keyX-API-Keyหมุน API key
GET/api/merchant/webhook-logsX-API-Keyประวัติส่ง webhook
GET/api/merchant/reconciliation-reportX-API-Keyรายงานกระทบยอด (CSV)

Error ที่พบบ่อย

HTTPความหมายทางแก้
401key/token ผิดหรือหมดอายุตรวจ X-API-Key / login ใหม่
403IP ไม่อยู่ใน allowlist, uuid ไม่ตรง, หรือเกินลิมิตเกรดผู้ใช้ตรวจ allowedIps / uuid / วงเงินเกรด
409idempotencyKey ซ้ำแต่ยอดไม่ตรงใช้ key ใหม่ต่อหนึ่งรายการเสมอ
429ยิงถี่เกิน (60 req/นาที/IP)เว้นจังหวะ / กระจายเวลา
Insufficient Gas/Credit Balanceวงเงินร้านไม่พอติดต่อผู้ดูแลเติมวงเงิน