IzFiles.Com

minimal_vietqr.py

Public Note Code 2025-10-18 08:19
Code
# minimal_vietqr.py

# Yêu cầu: pip install segno

import segno

# helper TLV (id là string 2 ký tự, value là string ASCII)

def tlv(id_str: str, value: str) -> str:

    length = f"{len(value):02d}"

    return f"{id_str}{length}{value}"

# CRC16-CCITT (CRC-CCITT-FALSE): poly 0x1021, init 0xFFFF

def crc16_ccitt_false(data: bytes) -> int:

    crc = 0xFFFF

    for b in data:

        crc ^= (b << 8)

        for _ in range(8):

            if (crc & 0x8000):

                crc = ((crc << 1) & 0xFFFF) ^ 0x1021

            else:

                crc = (crc << 1) & 0xFFFF

    return crc & 0xFFFF

# build payload minimal (ví dụ account transfer dạng VietQR)

def build_minimal_vietqr(bin_id: str, consumer_id: str, amount: str = None, memo: str = None):

    parts = []

    # 00 Payload Format Indicator (01)

    parts.append(tlv("00", "01"))

    # 01 Point of initiation method: "12" dynamic, "11" static. (ví dụ static)

    parts.append(tlv("01", "11"))

    # Merchant Account Information (ID "26" theo EMV) - subfields:

    # sub 00: GUI (NAPAS AID) -> A000000727 (common for NAPAS) + maybe 0124...

    # sub 01/02: BIN ID / consumer info depends on spec. Đây là ví dụ đơn giản:

    mai = ""

    # subtag 00 = GUI

    mai += tlv("00", "A000000727")   # NOTE: kiểm tra AID chính xác theo spec NAPAS

    # subtag 01 = BIN ID (ví dụ)

    mai += tlv("01", bin_id)

    # subtag 02 = consumer id (tài khoản)

    mai += tlv("02", consumer_id)

    parts.append(tlv("26", mai))

    # 52 Merchant Category Code (dummy) - 0000 nếu không có

    parts.append(tlv("52", "0000"))

    # 53 Currency (704 = VND)

    parts.append(tlv("53", "704"))

    # 54 Transaction amount (optional)

    if amount:

        parts.append(tlv("54", amount))

    # 58 Country code

    parts.append(tlv("58", "VN"))

    # 59 Merchant name (tối đa 25) - test

    parts.append(tlv("59", "TEN_MERCHANT"))

    # 60 Merchant city

    parts.append(tlv("60", "HANOI"))

    # 62 Additional data field templates (ví dụ bill number)

    if memo:

        add_data = tlv("01", memo)  # subfield 01 = bill/reference (ví dụ)

        parts.append(tlv("62", add_data))

    # assemble (tính CRC sau khi thêm field 63 with length 04 và placeholder)

    payload_without_crc = "".join(parts)

    # add CRC ID 63 and length 04 and placeholder (value will be computed)

    payload_for_crc = payload_without_crc + "63" + "04"  # CRC value to be appended as 4 hex chars (uppercase)

    # compute CRC on ASCII bytes

    crc = crc16_ccitt_false(payload_for_crc.encode('utf-8'))

    crc_hex = f"{crc:04X}"

    full_payload = payload_for_crc + crc_hex

    return full_payload

if __name__ == "__main__":

    bin_id = "970436"

    consumer_id = "1031933430"

    amount = "15000"   # VND as string (format depends spec)

    memo = "Thanh toan hoa don 123"

    emv_payload = build_minimal_vietqr(bin_id, consumer_id, amount, memo)

    print("EMV payload:", emv_payload)

    # tạo ảnh QR (segno)

    qr = segno.make(emv_payload)

    qr.save("vietqr_minimal.png", scale=4)

    print("Lưu file vietqr_minimal.png")