python promptpay
หากเราสแกน QR ที่ใช้จ่ายเงินตอนนี้ด้วยแอปพลิเคชั่นสแกน QR โดยเฉพาะ ไม่ใช่แอปของธนาคาร จะเห็นตัวเลขและข้อความยาวๆ เป็นชุดเดียว เช่นตัวอย่าง "00020101021129370016A000000677010111011300660000000005802TH530376463048956" (ตัวอย่างนี้ใช้ข้อมูลหลอก ด้วยหมายเลขโทรศัพท์ 000-000-0000)
แนวทางการอ่านข้อมูลของมาตรฐาน EMVco คือ หมายเลขประจำฟิลด์ข้อมูล (เป็น 00-99), ความยาวของข้อมูลในฟิลด์นั้น (เป็น 01-99), และตัวข้อมูลจริงๆ การอ่านข้อมูลตัวอย่างจะทำเป็นขั้นได้ดังนี้
หมายเลขเวอร์ชั่น ฟิลด์ 00 ความยาว 02 ข้อมูลตอนนี้คือ 01 เสมอ ดังนั้นข้อมูลชุดแรกคือ "000201"
ประเภทของ QR ฟิลด์ 01 ความยาว 02 ข้อมูล "11" แปลว่า QR นี้สร้างขึ้นเพื่อใช้สำหรับการขายหลายครั้ง เช่นระบุผู้ขาย หรือระบุราคาสินค้า ถ้าเป็น 12 คือ QR สร้างขึ้นเพื่อใช้ครั้งเดียว เช่น เป็นการจ่ายสำหรับใบเสร็จใบเดียว
ข้อมูลผู้ขาย (merchant account information) ฟิลด์ 29 ความยาว 37 ข้อมูล "0016A00000067701011101130066000000000" โดยข้อมูลนี้แบ่งออกเป็นสองฟิลด์ย่อย
หมายเลขแอปพลิเคชั่น (application ID - AID) เป็นหมายเลขที่ปกติแล้วใช้อ้างอิงประเภทบัตรสมาร์ตการ์ดแบบต่างๆ ตั้งแต่บัตรประชาชนไปจนถึงบัตรเครดิตทั้งหลาย ในกรณีนี้มีการนำหมายเลขนี้มาใช้ใน QR เพื่อระบุว่า QR นี้เป็น PromptPay หมายเลขฟิลด์ย่อย 00 ความยาว 16 ข้อมูล "A000000677010111"
หมายเลขบัญชี เป็นหมายเลข PromptPay โดยตรง ตอนนี้มีหมายเลขฟิลด์ เช่น
01 หมายเลขโทรศัพท์ ความยาว 13 นำหน้าด้วย 00 แล้วตามด้วยรหัสประเทศ 66 แล้วจึงเป็นหมายเลขโทรศัพท์ตัดศูนย์นำหน้าออก 00-000-0000
02 หมายเลขบัตรประชาชนไม่มีขีดคั่น
ประเทศ ฟิลด์ 58 ความยาว 02 ข้อมูล "TH" หมายถึงประเทศไทย
สกุลเงินที่ใช้งาน ฟิลด์ 53 ความยาว 03 ข้อมูล "764" โดยหมายเลข 764 เป็นหมายเลขประจำค่าเงินบาทตาม ISO 4217
ค่า check sum ฟิลด์ 63 ความยาว 04 ข้อมูล "8956" ข้อมูลนี้ต้องอยู่ท้ายสุดเสมอ โดยค่า check sum เป็นการคำนวณจากข้อมูลทั้งหมด รวมถึงหมายเลขฟิลด์ของ check sum และความยาวของฟิลด์ check sum เอง กระบวนการหาค่า check sum ใช้ CRC-16 และ ค่าคงที่ polynomial 0x1021 (XMODEM) พร้อมกับค่าเริ่มต้น 0xFFFF (อันนี้ต้องระวังเพราะไลบรารีส่วนมากมักใส่ค่าเริ่มต้นเป็น 0x0000) ตัวไลบรารีสามารถใช้ pycrc16 ได้โดยตรง
ตัวอย่างการหาค่า check sum ด้วย crc16pure.py เช่น
In [7]: hex(crc16pure.crc16xmodem("00020101021129370016A000000677010111011300660000000005802TH53037646304",0xffff)) Out[7]: '0x8956'
สังเกตว่าหมายเลขฟิลด์กระโดดจาก 01 ซึ่งเป็นฟิลด์บังคับ ไปยัง 29 ทันที เพราะหมายเลข 02-25 นั้นสงวนไว้สำหรับผู้ให้บริการบัตรเครดิตโดยเฉพาะ เช่น 02-03 สำหรับ VISA, 04-05 สำหรับ Mastercard, 06-08, 11-12 สำหรับ Amex, 13-14 สำหรับ JCB, และ 15-16 สำหรับ UnionPay ที่เหลือทาง EMVco ก็ให้เว้นไว้สำหรับการใช้งานในอนาคต
เราได้ข้อมูลหลายอย่างจากการดูว่ามีข้อมูลอยู่ใน QR เช่น
ตัว QR ยังคงแสดงข้อมูลหมายเลข PromptPay เช่นเดิม ซึ่งอาจจะหมายถึงหมายเลขโทรศัพท์หรือหมายเลขบัตรประชาชน หากใครมีความไม่สะดวกใจที่จะให้หมายเลขทั้งสองกับผู้ซื้อก็อาจจะต้องระวัง
QR เหล่านี้ไม่มีกระบวนการยืนยันว่าสร้างโดยใคร ที่จริงแล้วตอนนี้ทาง Digio ก็เปิดเว็บสร้าง QR PromptPay แล้ว เท่าที่ทดสอบสามารถใช้ได้กับ PromptPay ทุกธนาคาร ความเสี่ยงมีอยู่บ้างคือธนาคารเองก็ไม่สามารถตรวจสอบได้ว่ามีการสร้าง QR ไปแปะทับเพื่อขโมยเงินกันหรือไม่ ดังนั้นการจ่ายเงินควรตรวจสอบชื่อบัญชีของผู้รับเสมอว่าตรงกับคนที่เราต้องการจ่ายไป
มีความเป็นไปได้ที่จะสร้าง QR เหล่านี้โดยอัตโนมัติ ในเร็วๆ นี้เราคงได้เห็นไลบรารีโอเพนซอร์ส หรือบริการต่างๆ เพื่อสร้าง QR ทั้งระบุและไม่ระบุจำนวนเงิน ความเป็นไปได้อื่นๆ ของบริการนี้คงมีอีกมาก เช่นร้านค้าออนไลน์อาจจะเริ่มรับเงินผ่าน PromptPay กันมากขึ้นเพราะสามารถส่ง QR ระบุจำนวนเงินไปในแชตหรือเว็บอีคอมเมิร์ชได้โดยตรง
หลังจากที่ทางธนาคารแห่งประเทศไทยได้เปิดตัว PromptPay QR ออกมาแล้ว และยังได้เปิดเอกสารมาตรฐาน "EMVCo QR Code Specification for Payment Systems: Merchant-Presented Mode" ออกมาด้วย ผมจึงนำข้อมูลดังกล่าวและจาก https://www.blognone.com/node/95133 มาสร้างเป็นโค้ดสำหรับสร้าง QR code ของ PromptPay กับ Python ครับ
อ่านคำอธิบายสเปค PromptPay QR ได้จาก https://www.blognone.com/node/95133
อธิบายเพิ่มเติมที่เนื้อหาใน Blognone ไม่มี
สำหรับการกำหนดจำนวนเงินที่ต้องการจ่าย
จำนวนเงินที่ต้องการจ่าย ฟิลด์ 54 ความยาวข้อมูล 01-09 ข้อมูล "จำนวนเงินที่มีทศนิยม 2 ตำแหน่ง"
เช่น 50 บาท ได้เป็น 540450.00
การเช็คค่า check sum ต้องเอาไว้ข้างหลังท้ายข้อมูลเท่านั้น
ในการเช็คค่า check sum ผมแนะนำให้ใช้ libscrc เป็นโมดูลสำหรับคำนวณ CRC8/CRC16/CRC32/CRC64 ใช้ MIT License และรองรับทั้ง Python 2 , Python 3
ติดตั้งได้ด้วยคำสั่ง pip install libscrc
มาลงมือโค้ดกัน
import libscrc
def text_qr(account,one_time=True,country="TH",money="",currency="THB"):
"""
text_qr(account,one_time=True,country="TH",money="",currency="THB")
account is phone number or identification number.
one_time : if you use once than it's True.
country : TH
money : money (if have)
currency : THB
"""
Version = "0002"+"01" # เวชั่นของ PromptPay
if one_time==True: # one_time คือ ต้องการให้โค้ดนี้ครั้งเดียวหรือไม่
one_time="010212" # 12 ใช้ครั้งเดียว
else:
one_time="010211" # 11 ใช้ได้้หลายครั้ง
merchant_account_information="2937" # ข้อมูลผู้ขาย
merchant_account_information+="0016"+"A000000677010111" # หมายเลขแอปพลิเคชั่น PromptPay
if len(account)!=13: # ใช้บัญชีใช้เป็นเบอร์มือถือหรือไม่ ถ้าใช่ จำนวนจะไม่เท่ากับ 13
account=list(account)
merchant_account_information+="011300" # 01 หมายเลขโทรศัพท์ ความยาว 13 ขึ้นต้น 00
if country=="TH":
merchant_account_information+="66" # รหัสประเทศ 66 คือประเทศไทย
del account[0] # ตัดเลข 0 หน้าเบอร์ออก
merchant_account_information+=''.join(account)
else:
merchant_account_information+="02"+account.replace('-','') # กรณีที่ไม่รับมือถือ แสดงว่าเป็นเลขบัตรประชาชน
country="5802"+country # ประเทศ
if currency=="THB":
currency="5303"+"764" # "764" คือเงินบาทไทย ตาม https://en.wikipedia.org/wiki/ISO_4217
if money!="": # กรณีกำหนดเงิน
check_money=money.split('.') # แยกจาก .
if len(check_money)==1 or len(check_money[1])==1: # กรณีที่ไม่มี . หรือ มีทศนิยมแค่หลักเดียว
money="54"+"0"+str(len(str(float(money)))+1)+str(float(money))+"0"
else:
money="54"+"0"+str(len(str(float(money))))+str(float(money)) # กรณีที่มีทศนิยมครบ
check_sum=Version+one_time+merchant_account_information+country+currency+money+"6304" # เช็คค่า check sum
check_sum1=hex(libscrc.ccitt(check_sum.encode("ascii"),0xffff)).replace('0x','')
if len(check_sum1)<4: # # แก้ไขข้อมูล check_sum ไม่ครบ 4 หลัก
check_sum1=("0"*(4-len(check_sum1)))+check_sum1
check_sum+=check_sum1
return check_sum.upper() # upper ใช้คืนค่าสตริงเป็นตัวพิมพ์ใหญ่
pypromptpay.py hosted with ❤ by GitHub เมื่อลองเรียกใช้ ใส่เบอร์ 0000000000 ลงไป กำหนด 50.56 บาท ใช้ได้ครั้งเดียว
data=text_qr("0000000000",one_time=True,money="50.56")
print(data)
ผลลัพธ์
00020101021229370016A000000677010111011300660000000005802TH5303764540550.566304013E
สามารถนำไปสร้าง QR code ได้โดยใช้โมดูล qrcode อ่านได้จาก https://python3.wannaphong.com/2015/04/สร้าง-qr-code-บน-python.html/
ทำการสร้าง QR code
import qrcode
img = qrcode.make(data)
imgload = open('img-data.png','wb') #สร้างไฟล์ไบต์ใหม่ขึ้นมา กำหนดสิทธิ์เขียนไฟล์ได้
img.save(imgload, 'PNG') #บันทึกค่า QR Code เข้าไปยังไฟล์
imgload.close() #ปิดไฟล์
หากผู้อ่านไม่ต้องการลงมือโค้ดหรือ set ค่าอะไรให้ยุ่งวาย ผมได้ทำโมดูล pypromptpay ไว้แล้ว สามารถใช้ได้ง่าย ๆ โดยติดตั้งด้วยคำสั่ง pip install pypromptpay
การใช้งาน
from pypromptpay import qr_code
qr_code("0000000000",one_time=True,money="50.56",path_qr_code="b.png")
อ่านเอกสารการใช้งาน pypromptpay ได้ที่ https://github.com/wannaphong/pypromptpay