ระบบพร้อมเพย์ (PromptPay) ของประเทศไทยพัฒนาขึ้นบนมาตรฐานสากล EMVCo QR Code Specification for Payment Systems (Merchant-Presented Mode: MPM) ซึ่งเป็นมาตรฐานเดียวกับที่ใช้ในสิงคโปร์ (PayNow) และมาเลเซีย (DuitNow)
ในบทความนี้ เราจะมาแกะเบื้องหลัง Payload สตริงของ QR Code ว่าแต่ละตัวอักษรมีความหมายอย่างไร และวิธีเขียนฟังก์ชันสร้าง QR Code ด้วยตัวคุณเองโดยไม่ต้องพึ่งพาไลบรารีภายนอก
1. เข้าใจโครงสร้างข้อมูลแบบ TLV (Tag-Length-Value)
มาตรฐาน EMVCo บังคับเก็บข้อมูลในรูปแบบเรียงต่อกันเป็นคู่ข้อมูล Tag, Length, และ Value:
- Tag (2 ตัวอักษร): หมายเลขหมวดหมู่ เช่น
00คือเวอร์ชัน,54คือจำนวนเงิน - Length (2 ตัวอักษร): ความยาวของข้อมูล Value ที่ตามมา (นับเป็นจำนวนตัวอักษร)
- Value (ตาม Length): ข้อมูลจริง
ตัวอย่าง:
Tag: "00", Length: "02", Value: "01" => "000201"
Tag: "58", Length: "02", Value: "TH" => "5802TH"
Tag: "54", Length: "06", Value: "150.00" => "5406150.00"2. โครงสร้าง Tag สำคัญของ Thai PromptPay
| Tag ID | ชื่อ Field | รายละเอียด | ค่ามาตรฐานในไทย |
|---|---|---|---|
00 |
Payload Format Indicator | เวอร์ชันของมาตรฐาน EMVCo | 01 |
01 |
Point of Initiation Method | 11 = Static (กรอกเงินเอง), 12 = Dynamic (ระบุยอดเงิน) |
11 หรือ 12 |
29 |
Merchant Account Info | ข้อมูลบัญชีผู้รับเงิน PromptPay | ประกอบด้วย AID + ข้อมูลปลายทาง |
53 |
Transaction Currency | รหัสสกุลเงิน ISO 4217 | 764 (Thai Baht THB) |
54 |
Transaction Amount | ยอดเงินชำระ (ทศนิยม 2 ตำแหน่ง) | เช่น 250.00 |
58 |
Country Code | รหัสประเทศ 2 หลัก ISO 3166-1 | TH |
63 |
Checksum (CRC16) | Checksum ตรวจสอบความถูกต้องของ QR | 4 หลัก Hexadecimal |
3. AID และประเภทรหัสผู้รับเงิน (Tag 29)
สำหรับประเทศไทย Application Identifier (AID) คือ:
$$\text{AID} = \text{A000000677010111}$$
ประเภทรหัสผู้รับเงินใน Sub-tag:
01เบอร์โทรศัพท์มือถือ: แปลงเป็นรหัสสากล 13 หลัก เช่น0812345678$\rightarrow$006681234567802เลขบัตรประชาชน / เลขประจำตัวผู้เสียภาษี: 13 หลักตรงๆ03e-Wallet ID: 15 หลัก
4. โค้ดคำนวณ CRC16-CCITT และสร้าง Payload
function crc16(data: string): string {
let crc = 0xffff;
for (let i = 0; i < data.length; i++) {
let c = data.charCodeAt(i);
crc ^= c << 8;
for (let j = 0; j < 8; j++) {
if ((crc & 0x8000) !== 0) {
crc = ((crc << 1) ^ 0x1021) & 0xffff;
} else {
crc = (crc << 1) & 0xffff;
}
}
}
return (crc & 0xffff).toString(16).toUpperCase().padStart(4, "0");
}
function tlv(tag: string, value: string): string {
const len = value.length.toString().padStart(2, "0");
return `${tag}${len}${value}`;
}
export function generatePromptPayString(
targetPhoneOrId: string,
amount?: number,
): string {
const isMobile = targetPhoneOrId.length === 10;
const formatted = isMobile
? `0066${targetPhoneOrId.slice(1)}`
: targetPhoneOrId;
const subTag = isMobile ? "01" : "02";
const aid = tlv("00", "A000000677010111");
const target = tlv(subTag, formatted);
const tag29 = tlv("29", aid + target);
let raw =
tlv("00", "01") +
tlv("01", amount ? "12" : "11") +
tag29 +
tlv("53", "764") +
(amount ? tlv("54", amount.toFixed(2)) : "") +
tlv("58", "TH") +
"6304";
return raw + crc16(raw);
}นำสตริงผลลัพธ์ที่ได้ไปเรนเดอร์ลงใน Canvas QR Code เพื่อให้ลูกค้าสแกนจ่ายเงินผ่านแอพพลิเคชันธนาคารไทยทุกแห่งได้ทันที!