Usage
Usage¶
napas-qr-python encodes and decodes VietQR payloads for the NAPAS247 fast
transfer service (EMVCo Merchant-Presented Mode QR).
Encode¶
Build a payload from fields and read it off the .code property:
from qr_pay import QRPay
qr_pay = QRPay(
bin_id="970436", # beneficiary bank BIN
consumer_id="1031933430", # account (or card) number
service_code="ACCOUNT", # ACCOUNT | CARD | PAYMENT | CASH_WITHDRAWL
transaction_amount=50000, # optional; omit for a reusable static QR
purpose_of_transaction="Thanh toan don hang",
)
print(qr_pay.code)
Static vs dynamic¶
point_of_initiation_method follows spec §5.2.2: "STATIC" (emitted as 11,
reusable / multi-use) or "DYNAMIC" (emitted as 12, single transaction,
typically with a fixed amount). Default is "DYNAMIC".
QRPay("970436", "1031933430", point_of_initiation_method="STATIC")
All fields¶
Every root and template object from the spec is supported — MCC (52), tip/convenience indicator and fees (55–57), merchant name/city/postal (59–61), additional data (62.01–09), and the alternate-language template (64). See the parameter table on the Home page.
qr_pay = QRPay(
bin_id="970403",
consumer_id="0011012345678",
transaction_amount=189000,
merchant_category_code="5812",
merchant_name="CUA HANG ABC",
merchant_city="HA NOI",
bill_number="HD2026001",
reference_label="REF99",
language_preference="vi",
merchant_name_alt="Cửa hàng ABC",
)
Generate a QR image¶
Rendering uses segno; styles are passed
straight through to segno's save():
qr_pay.generate_qr_pay(dist="qr.png", styles={"scale": 8, "border": 3, "dark": "darkblue"})
Decode¶
Parse a raw payload back into fields. The CRC is verified by default and a
qr_pay.decode.QRDecodeError is raised on a mismatch or malformed input.
from qr_pay import QRPay, decode
code = "00020101021238570010A00000072701270006970403011300110123456780208QRIBFTTA530370454061800005802VN62340107NPS68690819thanh toan don hang63042E2E"
data = decode(code) # dict of fields
qr_pay = QRPay.decode(code) # re-encodable QRPay instance
assert QRPay.decode(code).code == code # round-trips
data = decode(code, verify_crc=False) # skip CRC check
root = QRPay.parse(code) # nested EMVCo TLV objects
root["38"].children["01"].get("00") # -> '970403' (BIN)
Verify a checksum¶
from qr_pay import verify_checksum, calculate_checksum
verify_checksum(code) # True / False over a full payload
calculate_checksum(code[:-4]) # recompute the 4-char CRC (CRC-CCITT / 0x1021)
Command line¶
$ napas_qr encode --bin 970436 --consumer 1031933430 --amount 50000 --purpose "Thanh toan"
$ napas_qr encode --bin 970436 --consumer 1031933430 --qr qr.png
$ napas_qr decode "<payload>"
$ napas_qr decode "<payload>" --no-verify