ภาพรวม

ยอดรับชำระรายวัน (บาท)

ตามช่องทาง

ตามประเภท

Site ยอดสูงสุด

รายการชำระเงิน

ตั้งค่าช่องทางชำระเงินต่อ Site

site ที่ไม่ได้ตั้ง merchant ของตัวเอง จะใช้ merchant กลางจาก env ของ gateway — secret เป็นแบบเขียนอย่างเดียว (ดูได้แค่ว่าตั้งแล้วหรือยัง)

API สำหรับ client

สำหรับระบบที่ต้องการรับชำระเงินผ่าน gateway (เช่น pssgogo, pss-ee, บริการใหม่ในอนาคต)

การยืนยันตัวตน

ทุก endpoint ใต้ /api/payments/* ต้องส่ง header:

Authorization: Bearer <token>

สร้าง/เพิกถอน token ได้ที่หน้า API Tokens — token แสดงครั้งเดียวตอนสร้าง

1. สร้างรายการชำระเงิน + QR

POST /api/payments/create
fieldชนิดคำอธิบาย
purposestring *PARKING | WALLET | PACKAGE | LIFF_PAYMENT
amountBahtnumber *ยอดเงิน (บาท) ขั้นต่ำ 1
siteIdnumbersite ของ pssgogo — ใช้เลือก merchant ตามที่ตั้งในหน้า "ตั้งค่า Site"
referenceIdnumberอ้างอิงรายการฝั่งระบบหลัก (เช่น parking transaction id)
metaobjectcontext ที่ต้องใช้ตอน finalize (เช่น netFee, viewerLineId)

ตอบกลับ:

{ "success": true, "qrImage": "data:image/png;base64,...", "mchOrderNo": "P2026...", "amountBaht": 40, "provider": "ksher" }

2. เช็คสถานะ

GET /api/payments/{mchOrderNo}/status

ตอบกลับ: { "success": true, "status": "...", "paid": true|false } โดย status =

PENDINGยังไม่จ่าย (ระบบ reconcile กับผู้ให้บริการให้อัตโนมัติ — จ่ายแล้วจะ finalize จาก polling ได้เลย)
PAIDจ่ายแล้ว + finalize สำเร็จ
SUCCESS_PENDING_CONFIRMเงินเข้าแล้วแต่ finalize ฝั่งระบบหลักยังไม่สำเร็จ (จะ retry)
FAILED / NOT_FOUNDสร้าง QR ไม่สำเร็จ / ไม่พบรายการ

3. ดึงรายการไป reconcile (สำหรับ pssgogo)

GET /api/payments/reconcile?state=unsettled

ดึงรายการที่ ธนาคารยืนยันจ่ายแล้ว แต่ยังปิดงานไม่สำเร็จ (เงินเข้าแล้วแต่ค้าง) — pssgogo ดึงไปปิดงานเองได้

paramค่า
stateunsettled (default: จ่ายแล้วแต่ค้าง) · paid · all
siteId, from, toกรอง site / ช่วงวันที่ (YYYY-MM-DD)
limit, offsetแบ่งหน้า (limit สูงสุด 500)

ตอบกลับ:

{ "success": true, "state": "unsettled", "total": N, "count": N, "orders": [
  { "mchOrderNo", "provider", "purpose", "referenceId", "siteId", "amountSatang",
    "status", "bankPaid": true, "bankConfirmedAt", "providerOrderNo", "meta",
    "payment": { "bankRef", "approvalCode", "payerName", "fromBank", "amount" }, "createdAt" } ] }

ใช้ Bearer token เดียวกับ create/status · bankPaid=true คือธนาคารยืนยันจ่ายแล้ว (มี bankRef/approvalCode ให้ตรวจสอบ)

4. Callback แจ้งเงินเข้า (gateway → ระบบหลัก)

เมื่อเงินเข้า gateway จะ POST ไปที่ endpoint ของระบบหลัก (ตอนนี้คือ pssgogo /api/payments/gateway-callback) พร้อมลายเซ็น:

X-Gateway-Timestamp: <epoch ms>   (คลาดได้ไม่เกิน 5 นาที — กัน replay)
X-Gateway-Signature: HMAC-SHA256(GATEWAY_CALLBACK_SECRET, "{timestamp}.{rawBody}") hex

body: { mchOrderNo, provider, purpose, referenceId, siteId, amountSatang, meta, providerOrderNo }

ฝั่งรับควรเชื่อแค่ mchOrderNo แล้วอ่านรายการจริงจาก DB, ตรวจลายเซ็นแบบ timing-safe, และต้อง idempotent — ตอบ { "ok": true } เมื่อ finalize สำเร็จ (gateway จะตอบ SUCCESS ให้ผู้ให้บริการหยุด retry)

ตัวอย่าง (curl)

curl -X POST /api/payments/create \
  -H "Authorization: Bearer gwk_..." \
  -H "Content-Type: application/json" \
  -d '{"purpose":"PARKING","siteId":233,"amountBaht":40,"referenceId":12345,"meta":{"netFee":40}}'

ผู้ให้บริการ QR — ปัจจุบัน 3 เจ้า

gateway เลือกเจ้าตามที่ตั้งค่าแต่ละ site (แท็บ "ตั้งค่า Site") — API สำหรับ client ด้านบนใช้เหมือนกันทุกเจ้า

① Ksher — PromptPay QR

สร้าง QR: gateway เรียก Ksher native_pay (merchant per-site หรือ merchant กลาง)

เงินเข้า (webhook): Ksher POST → /api/ksher/notify · verify ลายเซ็น RSA-MD5 ด้วย public key ของ site · ตอบ {"result":"SUCCESS"}

② BBL (PromptPay) — CAPS ThaiQR

สร้าง QR: gateway POST → https://capsparkingapi.cityparking.asia/portal/qr (header x-api-key/x-req-sig) ได้ QR base64

ขาเข้า — ให้ทีม CAPS/Peaw ยิงเข้าที่ endpoint เหล่านี้ · ยืนยันตัวตนด้วย header x-caps-key: <secret ที่ตกลงกัน> · ตอบกลับ { responseMesg, responseCode } (000 = สำเร็จ)

1. Validation — เช็คก่อนให้ลูกค้าจ่าย

POST /api/bbl/validate
{ "billerId", "amount", "transTime", "transDate", "reference1"(=order), "reference2"(=card code), "reference3" }
→ 000 สำเร็จ · 209 ไม่พบ order · 211 ข้อมูลไม่ถูกต้อง/ยอดไม่ตรง · 215 token ไม่ถูกต้อง

2. Notification — แจ้งเมื่อเงินเข้า (finalize + ปิดตั๋ว)

POST /api/bbl/notify
{ "data": { "billerId", "amount", "reference1"(=order), "reference2"(=card code),
            "fromBank", "approvalCode", "bankRef", "retryFlag", ... }, "type": "ThaiQR" }
→ 000 สำเร็จ (ปิดตั๋ว) · 209 ไม่พบ order · 211 ยังไม่สำเร็จ/ข้อมูลผิด (ส่งซ้ำได้) · 215 token ไม่ถูกต้อง

match order ด้วย reference1 (หมายเลข Order ที่ gateway ส่งไปตอนสร้าง QR) · responseCode: 000 success · 209 order not found · 211 invalid data · 215 invalid token

—
สร้างใหม่แล้ว key เดิมจะใช้ไม่ได้ทันที — ต้องแจ้ง Peaw อัปเดตด้วย

③ Stripe (PromptPay)

สร้าง QR: gateway → Stripe PaymentIntent (payment_method promptpay, amount เป็นสตางค์) → คืน PromptPay QR

เงินเข้า (webhook): ตั้งที่ Stripe Dashboard ให้ยิง event payment_intent.succeeded มาที่ /api/stripe/notify · verify ด้วย header Stripe-Signature (STRIPE_WEBHOOK_SECRET)

ต้องเปิดใช้ PromptPay ใน Stripe account ก่อน · match order ด้วย metadata.mchOrderNo

API Tokens

token สำหรับ client (เช่น pssgogo) ใช้เรียก /api/payments/* — ระบบเก็บเฉพาะ hash ค่าเต็มเห็นครั้งเดียวตอนสร้าง · key หลักจาก env (GATEWAY_API_KEY) ยังใช้ได้เสมอ

เหตุการณ์ (Log)