ชื่อร้าน / Merchant
ใช้ชื่อสื่อถึงผู้รับเงิน ลูกค้าจะได้ทราบว่าชำระให้ระบบใด และใช้แยกรายการใน Console
ศูนย์รวมเอกสารการใช้งาน ตั้งแต่ตั้งค่าร้าน สร้างระบบรับชำระเงิน ไปจนถึงเชื่อมต่อ Webhook และดูแลระบบอย่างครบถ้วน
เรียนรู้ระบบ Payment CytechTeam Dev ตั้งแต่ขั้นตอนพื้นฐานไปจนถึงการเชื่อมต่อ API พร้อมตัวอย่างโค้ดและแนวทางการใช้งานจริง
เลือกหัวข้อที่ต้องการศึกษาเพื่อเริ่มต้นใช้งานและเชื่อมระบบได้รวดเร็ว
ศึกษาการใช้งาน Live API และทดสอบเงื่อนไขชำระเงินจริงก่อนเริ่มให้บริการ
ระบบสร้าง QR PromptPay และจับคู่รายการรับเงินอัตโนมัติจาก LINE Bank Alerts สำหรับร้านค้า เว็บไซต์ หรือระบบสมาชิกที่ต้องการเชื่อมต่อด้วย API
Payment CytechTeam Dev API สร้าง PromptPay checkout และคืนสถานะจาก LINE bank notification. ระบบภายนอกใช้ Merchant API Key ผ่าน Authorization: Bearer; หน้า Merchant Console ใช้ Session Cookie แบบ HttpOnly. การเข้าถึงข้อมูลร้านถูกจำกัดตามสิทธิ์ของ credential
ทำตามลำดับนี้ได้แม้ไม่เคยใช้ Payment API มาก่อน ถ้าต้องการเริ่มแบบสั้น ๆ อ่าน หน้า Quickstart เพิ่มเติมได้
เปิด Console สร้างบัญชีผู้ใช้งานหรือเข้าสู่ระบบ เลือก Workspace และสร้างร้านค้าของคุณ
กรอกชื่อร้าน หมายเลข PromptPay ธนาคารที่รับเงิน และเลขบัญชีผู้รับ 4 หลักท้ายให้ตรงกับบัญชีจริง ตรวจข้อมูลก่อนยืนยันการตั้งค่า
ไปที่เมนู API & Checkout สร้าง Key สำหรับฝั่งเซิร์ฟเวอร์ คัดลอกเก็บทันที เพราะค่าจริงจะแสดงเพียงครั้งเดียว
เรียก POST /v1/checkouts นำ payment_path จากผลลัพธ์ไปเปิดเป็นหน้าจ่ายเงินให้ลูกค้า ระบบจะสร้าง QR จาก PromptPay ที่ตั้งไว้
ตั้ง Webhook เพื่อรับ payment.confirmed หรือเรียก API อ่านสถานะ ให้ส่งสินค้า/เติมเครดิตเฉพาะเมื่อสถานะเป็น PAID เท่านั้น
ระบบนี้เป็น Live environment ไม่ใช่ Sandbox การเรียกสร้าง Checkout จะสร้างบิลจริง แนะนำทดสอบกับยอดเล็กและตรวจสอบปลายทางรับเงินจริง
บัญชีร้านค้าคือพื้นที่จัดการ API keys, Projects, Checkout และ Webhook คุณต้องตั้งค่าฝั่ง Merchant ก่อน จึงจะสร้าง QR ที่ลูกค้าชำระเงินจริงได้
ใช้ชื่อสื่อถึงผู้รับเงิน ลูกค้าจะได้ทราบว่าชำระให้ระบบใด และใช้แยกรายการใน Console
ใส่หมายเลข PromptPay ที่รับเงินจริง เลือกธนาคารผู้รับ และเลขบัญชี 4 หลักท้ายให้ถูกต้องสำหรับการจับคู่แจ้งเตือน
ข้อมูลบัญชีรับเงินหลักถูกล็อกหลังตั้งค่า เพื่อลดการเปลี่ยนปลายทางโดยไม่ตั้งใจ โปรดตรวจสอบก่อนบันทึก
เลือก HTTPS endpoint แบบสาธารณะที่รับ POST แล้วตอบ 2xx ได้ ตรวจ signature ก่อนนำข้อมูลไปดำเนินการ
หากใช้หลายเว็บ: แยกเป็น Projects ภายใน Merchant เดียวกันเพื่อแยก API Key และรายงานได้ ดูหัวข้อ Projects & Workspaces
Server-to-server: เข้าสู่ Merchant Console เพื่อสร้างบัญชี ตั้งค่าร้านและ PromptPay แล้วสร้าง Merchant API Key ในหน้า API & Checkout. ส่ง Key ด้วย Authorization: Bearer cyt_next_... เท่านั้น อย่านำ Key ไปฝังใน JavaScript ที่ส่งให้เบราว์เซอร์
Authorization: Bearer cyt_next_...Merchant Console: เข้าสู่ระบบด้วย POST /v1/auth/login จากนั้นเบราว์เซอร์ใช้ __Host-cytech_console ซึ่งเป็น HttpOnly; Secure; SameSite=Strict Cookie อายุ 30 วัน จึงใช้งานข้ามแท็บหรือเปิดเบราว์เซอร์ใหม่ได้หากยังไม่หมดอายุ. ตรวจสถานะด้วย GET /v1/auth/session และออกจากระบบด้วย POST /v1/auth/logout. ไม่ต้องอ่านหรือเก็บ Session Token ใน JavaScript
Cookie: __Host-cytech_console (browser-managed)ระบบยังรองรับ Authorization: Bearer cyt_sess_... สำหรับ API client ที่ต้องการใช้ Console Session โดยตรง. Session ใหม่มีอายุ 30 วัน และ Session เก่าที่ยังใช้ได้สามารถย้ายไปใช้ Cookie ได้. API Key และ Session ถูกเก็บเป็น Hash ฝั่ง Server; API Key ใหม่แสดงค่าเต็มเพียงครั้งเดียวตอนสร้าง
curl -sS -X POST "https://payment.cytechteam.dev/v1/checkouts" \ -H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"merchant_ref":"order-1001","amount":"20.00","verification_method":"decimal"}'
แทน cyt_next_REPLACE_WITH_YOUR_KEY ด้วย Merchant API Key จริงของร้าน และเปลี่ยน merchant_ref ให้ไม่ซ้ำในแต่ละออเดอร์. ตัวอย่างใช้ URL เต็ม ไม่ต้องตั้งตัวแปร BASE หรือ KEY ใน Terminal. คำสั่งนี้สร้างบิลจริงบน Live environment
ตัวอย่างด้านล่างใช้ URL เต็มของ Live API และใช้ cyt_next_REPLACE_WITH_YOUR_KEY แทนคีย์จริง โดยไม่ต้องตั้งตัวแปร Shell. ดู Quickstart แบบทีละขั้น เพื่อสร้างบิล เปิดหน้าจ่ายเงิน แล้วรับ webhook หรือ polling สถานะ. merchant_ref ต้องไม่ซ้ำสำหรับแต่ละออเดอร์
curl -X POST "https://payment.cytechteam.dev/v1/checkouts" \ -H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"merchant_ref":"order-1001","amount":"20.00","verification_method":"decimal"}'
Response มี payment_path เช่น /pay/pay_... ให้เปิดหรือส่งให้ลูกค้า
ฟัง payment.confirmed ที่ webhook หรือ poll GET /v1/checkouts/<id> จนสถานะเป็น PAID
ความเข้าใจวงจรของบิลสำคัญมาก โดยเฉพาะระบบเติมเครดิตหรือส่งสินค้าอัตโนมัติ
ครบ 15 นาทีแล้วยังไม่ยืนยัน ไม่ควรใช้ QR เดิม ให้สร้างออเดอร์ใหม่ด้วย merchant_ref ใหม่
ไม่พบออเดอร์ที่ตรง หรือพบหลายออเดอร์ ระบบจะไม่สุ่มเลือกบิล ต้องตรวจเหตุการณ์และยอดรับเงินจริง
การที่ลูกค้าเห็น QR หรืออัปโหลดรูปสลิป ไม่ได้แปลว่าบิลสำเร็จ ห้ามเปลี่ยนสถานะออเดอร์เองจนกว่าจะได้รับหลักฐานสถานะ PAID จาก Server
เลือกให้เหมาะกับข้อมูลที่คุณรู้เกี่ยวกับผู้โอน และตรวจให้แน่ใจว่าเงื่อนไขตรงตามธุรกรรมจริง
หนึ่งบิลเลือกได้หนึ่งวิธี และจะถูกจับคู่กับ LINE bank notification ที่ผ่านการตรวจผู้ส่งและเงื่อนไขอื่นของรายการ (บัญชีผู้รับ ยอดเงิน และเวลา) เท่านั้น
| Method | Value | Match rule |
|---|---|---|
| decimal | ไม่ต้องส่ง | เศษสตางค์ของยอดจริงตรงกับ expected cents |
| name_exact | 2–150 chars | ชื่อผู้โอน normalize แล้วตรง exact |
| account_last4 | 4 digits | เลขบัญชีผู้โอน 4 หลักท้ายตรง |
| bank | Bank code/name | ธนาคารผู้โอนตรง |
decimal: เหมาะกับบิลทั่วไปเมื่อยืนยันด้วยเศษสตางค์ แต่ถ้าเปิดบิลยอดเดียวกันหลายใบพร้อมกัน แนะนำ amount_mode: "unique_cents" เพื่อให้ระบบจัดสรรเศษสตางค์ไม่ซ้ำ
name_exact: ใช้เมื่อทราบชื่อบัญชีผู้โอนที่จะแสดงในแจ้งเตือนอย่างแน่นอน · account_last4: ใช้เลขบัญชี ผู้โอน 4 หลักท้าย ไม่ใช่เลขบัญชีรับเงิน · bank: เหมาะเมื่อทราบธนาคารของผู้โอน
curl -sS -X POST "https://payment.cytechteam.dev/v1/checkouts" \
-H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"merchant_ref":"order-1003","amount":"20.00","verification_method":"decimal","amount_mode":"unique_cents"}'เช่น ร้านขอ 20.00 บาท แต่ระบบอาจคืน 20.01 บาท เพื่อแยกบิล อย่าแสดงยอด 20.00 แทนค่าที่ตอบกลับ โหมดนี้ใช้ได้เฉพาะจำนวนเต็มบาทและวิธี decimal เท่านั้น
ไม่ว่าลูกค้าจะกดซื้อจากหน้าเว็บ Discord bot หรือเกม ขั้นตอนหลักเหมือนกัน: Server ของคุณสร้างออเดอร์ → ขอ Checkout → ส่งลิงก์ให้ลูกค้า → รอ PAID → ส่งมอบบริการ
สร้างบิลเมื่อกดสั่งซื้อ บันทึก merchant_ref คู่กับหมายเลขออเดอร์ ส่งลิงก์จ่ายเงินไปหน้า checkout จากนั้นรอ Webhook
ให้ Backend ของบอทเรียก API แล้วส่ง payment link ไปให้ผู้ใช้ รอ PAID ก่อนเพิ่มเครดิต และบันทึก event ID กันเติมซ้ำ
สร้าง Checkout ใหม่ทุกงวดหรือทุกคำสั่งซื้อ ใช้เลขอ้างอิงใหม่ ไม่ถือว่าจ่ายแล้วเพียงเพราะเปิดลิงก์ชำระเงิน
const response = await fetch('https://payment.cytechteam.dev/v1/checkouts', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.CYTECH_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
merchant_ref: 'order-' + crypto.randomUUID(),
amount: '20.00',
verification_method: 'decimal'
})
});
if (!response.ok) throw new Error('Checkout failed: ' + response.status);
const { checkout } = await response.json();
const paymentUrl = 'https://payment.cytechteam.dev' + checkout.payment_path;
console.log(paymentUrl, checkout.id);import os
import requests
resp = requests.post(
"https://payment.cytechteam.dev/v1/checkouts",
headers={"Authorization": "Bearer " + os.environ["CYTECH_API_KEY"]},
json={"merchant_ref": "order-1002", "amount": "20.00",
"verification_method": "decimal"},
timeout=15,
)
resp.raise_for_status()
checkout = resp.json()["checkout"]
print("https://payment.cytechteam.dev" + checkout["payment_path"])curl -sS "https://payment.cytechteam.dev/v1/checkouts/pay_REPLACE_WITH_REAL_ID" \ -H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY"
Polling ควรทำจาก Backend ตามช่วงเวลาที่เหมาะสมและหยุดเมื่อ PAID, EXPIRED หรือ CANCELLED หากต้องการส่งมอบอัตโนมัติแนะนำใช้ Webhook เป็นช่องทางหลักและใช้ GET ตรวจสอบซ้ำได้
Docs MCP สำหรับอ่านคู่มือสาธารณะผ่าน https://payment.cytechteam.dev/v1/mcp/docs โดยไม่ต้องล็อกอิน ใช้ค้นหัวข้อและอ่าน Markdown เท่านั้น
Manager MCP สำหรับอ่านข้อมูล Workspace โปรเจกต์ และ Checkout ผ่าน https://payment.cytechteam.dev/v1/mcp/manager รองรับ OAuth 2.1 ให้ล็อกอิน Payment แล้วเลือก Workspace และสิทธิ์บนหน้าขออนุญาต ไม่ต้องวาง Merchant API Key ลงในแอป AI
รองรับ OAuth Discovery, Dynamic Client Registration, Authorization Code + PKCE (S256), Access/Refresh Token และเพิกถอนแอป AI ใน Console ได้ สิทธิ์อ่าน mcp:read แยกจากสิทธิ์สร้าง Checkout mcp:write โปรดตั้งค่า AI ให้ขออนุมัติก่อนสร้างบิลรับเงินจริง ผู้ใช้ที่ต้องการตั้งค่าแบบเก่าด้วย Merchant API Key ยังใช้ได้ใน MCP Client ที่รองรับ Bearer Header
ดูตัวอย่างการเชื่อมต่อหลายแพลตฟอร์มได้ที่ ตั้งค่าบัญชี → MCP Integration
Workspace ใช้จัดการพื้นที่ร้านและสิทธิ์การเข้าถึงใน Merchant Console ส่วน Project ใช้แบ่งระบบย่อยของร้าน เช่น เว็บ A, Discord Bot, ร้านค้า B โดยใช้รหัส prj_... ของ Project ใน Checkout หรือ API Key ที่ผูก Project
API Key ที่ไม่ได้ผูก Project สามารถทำงานตามสิทธิ์ Merchant และเลือก Project ที่ต้องการสำหรับ Checkout ได้
สิทธิ์จะถูกจำกัดใน Project เดียว ช่วยแยกข้อมูลและลดการเข้าถึง Checkout ของโปรเจกต์อื่นโดยไม่ตั้งใจ
/v1/projectsเรียกรายการโปรเจกต์ที่สิทธิ์ปัจจุบันเข้าถึงได้ เลือก Project จาก Console เมื่อใช้งานหลายระบบ และใช้ project ใน request เฉพาะเมื่อทราบ ID จริงแล้ว
Payment CytechTeam Dev ไม่ได้เชื่อมกับบัญชีธนาคารโดยตรง แต่รับข้อความแจ้งเตือนผ่าน LINE Worker แล้วตรวจสอบเงื่อนไข เช่น ผู้ส่งที่รู้จัก ยอดโอน ผู้รับ เวลาของรายการ และวิธีจับคู่ที่เลือกไว้
Worker ตรวจชื่อ Official Account ตามนโยบายที่กำหนดในระบบ บันทึกข้อมูลเพื่อ audit โดยไม่ได้เท่ากับการรับประกันการยืนยันตัวจากธนาคารโดยตรง
ระบบไม่เดาว่าเงินเข้าบิลไหน เมื่อพบหลายบิลตรงกันจะทำเครื่องหมาย AMBIGUOUS และยังไม่ยืนยันบิลใด
/v1/merchant/bank-events?limit=50ใช้สำหรับตรวจรายการแจ้งเตือนจากธนาคารที่ระบบรับไว้ภายใต้สิทธิ์ของร้าน ควรเปรียบเทียบกับข้อมูลในธนาคารจริงเมื่อเกิดความคลาดเคลื่อน
ดูธนาคารที่ระบบระบุไว้ใน Bank coverage หรือเรียก GET /v1/banks เพื่อดูรายการที่ API ประกาศรองรับ
รับซองอั่งเป้า TrueMoney เข้า wallet ของร้าน ยิงครั้งเดียวต่อซอง ไม่ retry อัตโนมัติ ผ่านได้เฉพาะ POST เท่านั้น
curl -X POST "https://payment.cytechteam.dev/v1/merchant/truemoney/redeem" \ -H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"mobile":"0812345678","voucher":"https://gift.truemoney.com/campaign/?v=..."}'
SUCCESS — เงินเข้าแล้ว ยอดส่งผ่านตรง ไม่ปัดเป็น 0.00FAILED — upstream ตอบปฏิเสธชัดเจน (code จาก TrueMoney)PENDING_RECONCILIATION — timeout/network ขาด ซองอาจถูกใช้ไปแล้ว ห้ามยิงซ้ำเอง ต้อง reconcileซองเดิมเรียกซ้ำคืนแถวเดิม (200 + duplicate:true) ซองเดิมแต่คนละเบอร์ได้ 409. ส่ง "retry":true เพื่อยิงซ้ำเฉพาะเคสชั่วคราว (edge block, rate limit, timeout, 5xx) — SUCCESS กับ code สุดท้ายปฏิเสธด้วย 409 ยกเว้น "retry":"force" ที่ยิงซ้ำได้ครั้งเดียวสำหรับแถวเก่าที่บันทึกด้วยโค้ดรุ่นก่อน (ไม่ใช่ SUCCESS). ประวัติรายการรับซองอยู่ที่ GET /v1/merchant/truemoney/redemptions และดูร่วมกับ Checkout ได้ที่หน้า ธุรกรรม หรือ GET /v1/merchant/transactions?channel=truemoney โดยกรอง channel=all|checkout|truemoney, status, limit, offset ได้ ประวัติรวมแสดงรหัสซองและเบอร์ผู้รับแบบปิดบางส่วน ไม่แสดงลิงก์ซองเต็ม
รายการ TrueMoney ที่สำเร็จแสดงจำนวนเงินจริงตามผลตอบกลับ หากยังยืนยันยอดไม่ได้ ระบบจะแสดงว่าไม่ทราบยอดแทน 0.00 และจะไม่รวมยอดซองเข้ากับสถิติ Checkout โดยอัตโนมัติ เพื่อไม่ให้นับยอดซ้ำ
curl-cffi สำหรับ Chrome-compatible TLS แล้ว แต่ยังต้องตรวจผลตอบกลับของแต่ละซองจริง และไม่ควร retry ซองที่สถานะกำกวมก่อน reconcile/v1/checkouts| Field | Type | Required | Notes |
|---|---|---|---|
| merchant_ref | string | yes | 1–128 chars: A-Z a-z 0-9 _ . : / - |
| amount | string | yes | Positive, up to 2 decimals, max 9,999,999.99 |
| verification_method | string | no | decimal (default), name_exact, account_last4, bank |
| verification_value | string | conditional | Required except decimal |
| amount_mode | string | no | exact (default) or unique_cents (decimal, whole baht) |
| receiver_promptpay | string | no | Per-bill PromptPay; needs receiver_bank + receiver_last4 together, else merchant default |
| receiver_bank | string | no | Per-bill receiver bank code |
| receiver_last4 | string | no | Per-bill receiver last4 |
{
"checkout": {
"id": "pay_...",
"merchant_ref": "order-1001",
"amount": "20.00",
"status": "PENDING",
"verification_method": "decimal",
"payment_path": "/pay/pay_...",
"expires_at": "2026-10-25T12:15:00+00:00"
}
}/v1/checkouts/<pay_id>คืน checkout ของ merchant นี้เท่านั้น ถ้าไม่พบจะได้ 404 แบบไม่เปิดเผยว่ามีอยู่จริงหรือไม่
/v1/checkouts/<pay_id>/cancelยกเลิกบิลที่ยังเป็น PENDING โดยส่ง JSON {} พร้อม Merchant API Key หรือ Console session บนเซิร์ฟเวอร์ จะได้ผลลัพธ์ {"checkout":{"status":"CANCELLED",...}} การกดซ้ำปลอดภัย (idempotent) แต่บิล PAID หรือหมดเวลาแล้วตอบ 409, คนละ Workspace/Project ได้ 404 ไม่ใช่การคืนเงิน และควรใช้ merchant_ref ใหม่เมื่อสร้างบิลทดแทน
curl -X POST "https://payment.cytechteam.dev/v1/checkouts/pay_REPLACE_WITH_REAL_ID/cancel" \
-H "Authorization: Bearer cyt_next_REPLACE_WITH_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'หน้าจ่ายเงินมีปุ่มยกเลิกสำหรับผู้ถือ URL ของบิลผ่าน POST /pay/<pay_id>/cancel (ต้องเป็น JSON และเรียกจาก Origin เดียวกัน) ระบบหยุดแสดง QR และแจ้ง payment.cancelled ผ่าน Webhook และ Custom Notification ที่สมัคร event นี้ไว้ หากโอนไปแล้วหรือโอนผิดหลังยกเลิก ต้องตรวจยอดและจัดการคืนเงินแยกต่างหาก
/v1/merchant/checkouts?status=PAID&limit=50&offset=0รายการล่าสุดสำหรับ Console และรายงาน. รองรับ status PENDING, PAID, EXPIRED, CANCELLED
/v1/merchant/profileดูข้อมูล Merchant ปัจจุบัน ใช้ตรวจสอบ Key และรายละเอียดบัญชีรับเงินที่ตั้งค่าไว้
/v1/merchant/overviewอ่านภาพรวมธุรกรรมของร้าน หรือระบุ Project เมื่อมีหลายโปรเจกต์
/pay/<pay_id>/pay/<pay_id>/status/pay/<pay_id>/qr.pngหน้า public ไม่ต้องใช้ API key. QR จะถูกปฏิเสธเมื่อบิลไม่ใช่ PENDING. Status endpoint คืนเฉพาะสถานะที่ปลอดภัย
หน้า /pay/... และ /pay/.../status ออกแบบให้ลูกค้าเข้าถึงได้โดยไม่เปิดเผย Key ส่วน endpoint /v1/checkouts/... ต้องเรียกจาก Backend ที่ยืนยันตัวแล้ว
ตั้งค่าจุดรับในเมนู API & Checkout หรือใช้ POST /v1/merchant/webhook จากบัญชีที่มีสิทธิ์ Merchant และระบุ HTTPS URL ที่เข้าถึงจากอินเทอร์เน็ตได้
เมื่อยืนยันสำเร็จ ระบบเขียน outbox ใน transaction เดียวกับ PAID แล้วส่ง POST ไปยัง URL ของร้าน
https://merchant.example/payments| Header | Meaning |
|---|---|
| X-Cytech-Event-ID | ID สำหรับ deduplication |
| X-Cytech-Timestamp | Unix seconds |
| X-Cytech-Signature | HMAC-SHA256(secret, timestamp + "." + raw_body) |
{
"type": "payment.confirmed",
"data": {
"id": "pay_...",
"merchant_ref": "order-1001",
"amount": "20.00",
"status": "PAID",
"confirmation_basis": "BANK_LINE_NOTIFICATION",
"funds_received": true
}
}Retries: bounded exponential backoff, สูงสุด 7 ครั้ง. ฝั่งร้านควรตอบ 2xx และ dedup ด้วย event ID
บน Backend ติดตั้ง npm install express ใช้ Node.js แบบ ES Modules และกำหนด CYTECH_WEBHOOK_SECRET จาก Console (Secret แสดงครั้งเดียวเมื่อสร้างหรือ Rotate)
คำนวณ HMAC จาก timestamp + "." + raw_body ด้วย Webhook Secret ที่ตั้งค่าของร้าน ตรวจ timestamp เพื่อป้องกัน replay และใช้การเปรียบเทียบแบบ timing-safe ก่อนอ่านข้อมูลคำสั่งซื้อ
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
const app = express();
app.post('/payments', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Cytech-Timestamp') || '';
const sent = req.get('X-Cytech-Signature') || '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now()/1000 - Number(ts)) > 300)
return res.sendStatus(401);
const expected = createHmac('sha256', process.env.CYTECH_WEBHOOK_SECRET)
.update(ts + '.').update(req.body).digest('hex');
const received = sent.replace(/^sha256=/i, '');
if (!/^[a-f0-9]{64}$/i.test(received) ||
!timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(received, 'hex')))
return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
const eventId = req.get('X-Cytech-Event-ID');
// Queue a transaction: persist eventId uniquely, then fulfill PAID order once.
// Acknowledge 2xx after durable processing or queuing.
return res.sendStatus(204);
});
app.listen(Number(process.env.PORT || 3000));คุณยังต้องทำส่วนบันทึก event ID และอัปเดตออเดอร์ในฐานข้อมูลจริง เพื่อไม่ให้ callback ที่ส่งซ้ำทำให้เติมเงินหรือส่งสินค้าซ้ำ
(merchant_id, merchant_ref) ไม่ซ้ำ. การเรียก create ซ้ำด้วย ref และข้อมูลเดิมจะคืน checkout เดิม. ถ้า ref เดิมแต่ยอด/วิธีตรวจสอบต่าง จะได้ 409
LINE message ID ซ้ำจะไม่สร้าง event ใหม่และไม่จ่ายบิลซ้ำ
| Status | Meaning | Example |
|---|---|---|
| 401 | Missing/invalid credential | unauthorized |
| 404 | Not found or not owned | not_found |
| 409 | Conflict / duplicate ref mismatch | merchant_ref already belongs... |
| 422 | Validation error | amount must have 0 to 2 decimals |
| 500 | Internal error (no detail exposed) | internal_error |
ตรวจจากขั้นตอนที่เกี่ยวข้องก่อนแก้ไขในระบบร้าน อย่าสร้าง Checkout หรือส่งมอบสินค้าซ้ำโดยไม่ได้ตรวจสถานะเดิม
ตรวจว่า Header เป็น Authorization: Bearer cyt_next_... และ Key ไม่ถูกเพิกถอนหรือหมดสิทธิ์ ตรวจว่าใช้ Key ของร้าน/Project ที่ถูกต้อง ไม่ใช่ Session Cookie ของหน้าเว็บ
ตรวจ merchant_ref ความยาว 1–128 ตัวอักษรและอักขระที่อนุญาต ยอดต้องเป็นสตริงเลขบวกทศนิยมไม่เกิน 2 ตำแหน่ง กรณีเลือก name_exact, bank หรือ account_last4 ต้องระบุ verification_value
หมายเลขอ้างอิงนี้เคยถูกสร้างด้วยข้อมูลไม่เหมือนเดิม ห้ามใช้ ref เดิมกับยอดใหม่ ให้สร้าง order ID ใหม่สำหรับคำสั่งซื้อใหม่ และเก็บ ref เดิมไว้สำหรับการ retry request เดิม
ตรวจรายการเงินจริงกับธนาคารก่อน จากนั้นตรวจเวลาบิล (15 นาที), ยอดที่ลูกค้าโอน, ธนาคารและบัญชีผู้รับ 4 หลักท้าย, LINE Worker/Bank alerts, วิธีตรวจที่ระบุ และเหตุการณ์ UNMATCHED/AMBIGUOUS ห้ามปรับ PAID โดยเดา
ตรวจ ID ว่าอยู่ในรูป pay_... และบิลยัง PENDING หากบิลหมดอายุหรือ PAID แล้ว API QR จะไม่ให้ใช้งาน ให้สร้างบิลใหม่หากยังต้องชำระ
ตรวจ URL ต้องเป็น HTTPS แบบ Public, DNS/TLS ถูกต้อง และ endpoint ตอบ 2xx ได้ ตรวจ Firewall, Log ฝั่งรับ, Event ID และสถานะระบบที่ Status ระบบมี retry สูงสุด 7 ครั้ง จึงควรทำตัวรับให้ idempotent และมีทางเช็กด้วย GET
ปลายทางตอบไม่แน่ชัด (เช่น timeout) ซองอาจถูกใช้แล้ว อย่าเรียก redeem ซ้ำทันที ตรวจประวัติและยอดใน Wallet ก่อน แล้วค่อยจัดการ reconciliation
แยก Checkout ออกจากคำสั่งซื้อบันทึก pay_id, merchant_ref, ยอด และสถานะในฐานข้อมูลของคุณเสมอ
ตรวจ Webhook signature จาก raw bodyอย่าเอา JSON ที่ parse แล้วไป serialize ใหม่ก่อน HMAC
ส่งมอบครั้งเดียวเท่านั้นใช้ event ID หรือ order ID เป็น unique key และบันทึกว่าเคยเติมเครดิต/ส่งสินค้าแล้ว
เก็บ API Key ใน Serverใช้ environment variables หรือ secret manager ไม่วางใน frontend, แอปที่แจก หรือ GitHub
แยกโปรเจกต์ตามงานใช้ Project-scoped API Key เมื่อมีเว็บ บอท หรือผู้ดูแลหลายชุด
ตรวจความพร้อมและทำแผนสำรองเฝ้าดู /status, มีวิธีตรวจยอดด้วยคน และรองรับความล่าช้าของ LINE notification
ดูข้อมูลร้านและสร้างบิลผ่าน Merchant Console ได้ ส่วนการสร้างบิลจากเว็บ บอท หรือเติมเครดิตอัตโนมัติควรมี Backend สำหรับเก็บ API Key และรับ Webhook
ใช้รูปแบบ PromptPay ที่หน้าตั้งค่าร้านรองรับและตรวจให้แน่ใจว่าปลายทางที่สร้าง QR ตรงกับบัญชีรับเงินจริง หากไม่มั่นใจให้ทดสอบกับธนาคารของคุณก่อนเปิดขายจริง
ระบบนี้ใช้ LINE bank notification เป็นหลักฐานการรับเงิน ไม่ใช้ภาพสลิปเป็นตัวตัดสิน เพื่อหลีกเลี่ยงการยืนยันจากไฟล์ที่แก้ไขหรือใช้ซ้ำได้
ตัวอย่างทั้งหมดในหน้านี้ใช้ Live API ของ payment.cytechteam.dev อย่าทดสอบด้วยยอดจำนวนมาก และอย่าสมมติว่ามี Sandbox แยกหากยังไม่ได้รับข้อมูลยืนยันจากผู้ดูแลระบบ
ตรวจรายการเงินจริงกับธนาคารและสถานะบิลก่อน อย่าจ่ายเครดิตหรือสร้างรายการ PAID เองโดยไม่พิสูจน์ ตรวจหาธุรกรรมที่ไม่ถูกจับคู่และดำเนินการตรวจสอบด้วยคน
ไม่ใช่บริการชำระเงินของธนาคารหรือ Settlement API โดยตรง ระบบช่วยสร้างบิลและตรวจสอบข้อมูลการรับเงินจากการแจ้งเตือน LINE ธนาคาร ซึ่งมีข้อจำกัดขึ้นกับแหล่งข้อมูลภายนอก
CYTECH_LINE_SENDER_POLICY=name ตรวจชื่อ LINE Official Account ที่ normalize แล้วแบบตรงตัวกับชื่อธนาคารที่รู้จัก โดยบันทึก MID เพื่อ audit แต่ไม่ได้ยืนยัน MID allowlist หรือ Verified/Premium badge ในโหมดนี้. โค้ดยังรองรับโหมด mid, badge_name และ mid_or_badge_name เมื่อกำหนดค่าต่างออกไปBANK_LINE_NOTIFICATION ไม่ใช่ Bank Settlement โดยตรง