Skip to main content

bakong-khqr-image

PyPI version Python versions License: MIT

Generate styled Bakong KHQR card images that match the official NBC design — with correct USD and KHR amount formatting.

Built from production experience: the official bakong-khqr library formats USD amounts with a comma (0,01 USD) instead of a period (0.01 USD). This library fixes that and ships a clean, standalone implementation.


Features

  • ✅ EMV-compliant KHQR payload generation per KHQR spec v1.3 (no external bakong dependency)
  • ✅ Correct amount formatting — 1,234.56 USD · 4,000 KHR
  • ✅ Styled 300 × 450 px card (Bakong red header, merchant info, dashed separator, rounded corners)
  • ✅ Bundled assets — works out of the box, no manual font/icon setup
  • ✅ Multiple export formats: PNG · JPEG · WebP · bytes · Base64 · data URI
  • ✅ Static (open-amount) and dynamic QR code support
  • ✅ Individual & merchant account types — correct Tag 15 / Tag 29 structure
  • ✅ Tag 99 transaction timestamps — required for Bakong backend to process payments
  • ✅ from_emv() — render a styled card from any existing raw KHQR string
  • ✅ Typed, documented, tested

Installation

pip install bakong-khqr-image

Dependencies (Pillow and qrcode) are installed automatically.


Quick Start

from bakong_khqr_image import KHQRImage

# Create one instance per merchant — reuse it for every payment
khqr = KHQRImage(
    bakong_account_id="yourname@aclb",
    merchant_name="My Coffee Shop",
    merchant_city="Phnom Penh",   # optional, default "Phnom Penh"
    currency="USD",               # optional, default "USD"
    account_type="individual",    # optional, default "individual" (personal wallet)
                                  # use "merchant" for registered KHQR merchants
)

# Generate a styled QR card image
result = khqr.generate_image(amount=2.50)

# Save as PNG
result.save_png("payment_qr.png")

# Or get bytes / Base64 / data URI for web / API responses
png_bytes = result.to_bytes()
b64       = result.to_base64()
data_uri  = result.to_data_uri()

API Reference

KHQRImage

KHQRImage(
    bakong_account_id: str,
    merchant_name: str,
    merchant_city: str = "Phnom Penh",
    currency: str = "USD",              # "USD" or "KHR"
    account_type: str = "individual",   # "individual" or "merchant"
)

generate_image(amount, *, bill_number, store_label, terminal_label, mobile_number, static, include_additional_data, ttl_seconds) → QRImageResult

Renders the full styled card image.

Parameter Type Default Description
amount float Transaction amount. 0 = open-amount / static QR
bill_number str None Unique reference ID (auto-generated UUID if omitted)
store_label str None Store label shown in the Bakong app
terminal_label str None Terminal identifier
mobile_number str None Merchant phone number
static bool False True → static QR (no amount, no Tag 99)
include_additional_data bool False True → embed Tag 62 (bill/store/terminal) in payload
ttl_seconds int 60 Transaction validity window for Tag 99. 0 = omit

generate_payload(amount, *, ...) → (payload, bill_number)

Returns only the raw EMV KHQR string — useful for custom renderers or the Bakong payment-verification API.

payload, ref_id = khqr.generate_payload(amount=5.00)
md5_hash = KHQRImage.md5(payload)   # pass to Bakong API to check payment status

KHQRImage.from_emv(emv_string) → QRImageResult

Generate a styled card image directly from an existing raw KHQR / EMV string. Merchant name, amount, and currency are extracted automatically from Tags 59, 54, and 53.

emv = "00020101021229240020yourname@aclb...6304ABCD"
result = KHQRImage.from_emv(emv)
result.save_png("from_emv.png")

KHQRImage.md5(payload) → str

Computes the MD5 hash of a payload string required by the Bakong transaction-check API.


QRImageResult

Method Returns Description
save_png(path) str Save PNG, returns absolute path
save_jpeg(path) str Save JPEG, returns absolute path
save_webp(path) str Save lossless WebP, returns absolute path
to_bytes() bytes PNG-encoded bytes
to_base64() str Base64-encoded PNG string
to_data_uri() str data:image/png;base64,… URI
.image PIL.Image Raw PIL Image for custom processing

Examples

KHR currency

from bakong_khqr_image import KHQRImage

khqr = KHQRImage(
    bakong_account_id="yourname@aclb",
    merchant_name="ហាងកាហ្វេ",
    currency="KHR",
)
result = khqr.generate_image(amount=4_000)
result.save_png("payment_khr.png")

Static (open-amount) QR

# Customer enters the amount inside the Bakong app — useful for printed menus
result = khqr.generate_image(amount=0, static=True)
result.save_png("static_qr.png")

Individual vs. merchant account type

# Default — personal Bakong wallet (Tag 29 only, no Tag 15)
khqr_personal = KHQRImage(
    bakong_account_id="yourname@aclb",
    merchant_name="My Shop",
    account_type="individual",   # default, can be omitted
)

# Registered KHQR merchant (adds Tag 15 with AID per KHQR spec v1.3)
khqr_merchant = KHQRImage(
    bakong_account_id="merchant@wbkh",
    merchant_name="Virackbot Mart",
    account_type="merchant",
)

Render a card from an existing KHQR string

# No need to re-generate — just pass the raw EMV string
emv = "00020101021229240020yourname@aclb...6304ABCD"
result = KHQRImage.from_emv(emv)
result.save_png("from_emv.png")

Web / API response

result = khqr.generate_image(amount=1.50)

# Flask / Django — return as JSON
response = {"qr_image": result.to_data_uri()}

# Or stream PNG bytes directly
return Response(result.to_bytes(), mimetype="image/png")

Check payment status with the Bakong API

import requests

payload, ref_id = khqr.generate_payload(amount=5.00)
md5 = KHQRImage.md5(payload)

resp = requests.post(
    "https://api-bakong.nbc.org.kh/v1/check_transaction_by_md5",
    headers={"Authorization": "Bearer <YOUR_TOKEN>"},
    json={"md5": md5},
)
if resp.json().get("responseCode") == 0:
    print("Payment received!")

Access the raw PIL image

result = khqr.generate_image(amount=2.50)
pil_image = result.image          # PIL.Image.Image
pil_image.show()                  # open in system viewer

📁 See the examples/ folder for full runnable scripts:


Card Layout

┌─────────────────────────────┐  ◥ red fold
│  [KHQR / Bakong logo]       │  ← red header (60 px)
│                             │
│  Merchant Name              │
│  2.50 USD                   │
│ - - - - - - - - - - - - - - │  ← dashed separator
│                             │
│    ┌───────────────────┐    │
│    │                   │    │
│    │    QR Code        │    │
│    │      [💲]         │    │  ← currency icon overlay
│    │                   │    │
│    └───────────────────┘    │
│                             │
└─────────────────────────────┘
         300 × 450 px

Sample image

USD QR sample KHR QR sample

Running Tests

pip install -e ".[dev]"
pytest

Publishing to PyPI

pip install build twine
python -m build
twine upload dist/*

License

MIT © 2026 Virackbot

Metadata

Release files for bakong-khqr-image 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bakong-khqr-image 0.1.4
File Size Uploaded Provenance Actions
bakong_khqr_image-0.1.4.tar.gz 210.1 kB With provenance Details for bakong_khqr_image-0.1.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for bakong-khqr-image 0.1.4
File Interpreter ABI Platform Actions
bakong_khqr_image-0.1.4-py3-none-any.whl Python 3 none any Details for bakong_khqr_image-0.1.4-py3-none-any.whl

Total release size: 414.8 kB

Release files / bakong_khqr_image-0.1.4.tar.gz

Download URL bakong_khqr_image-0.1.4.tar.gz
Size 210.1 kB
Tags Source
SHA-256 checksum
How to use checksums
bf67d7056a4503c3805805e3fed2df654a5bc3fc4bdcf657003848cc3e1f6c1c
BLAKE2b-256 checksum
How to use checksums
418e5bd5cd7202779f17f732e20215c8399dd9d905cb261178060f161afb58ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 5, 2026.

Transparency log

Release files / bakong_khqr_image-0.1.4-py3-none-any.whl

Download URL bakong_khqr_image-0.1.4-py3-none-any.whl
Size 204.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e3d088800f012aae9a5475c8bc399d0d0cea3a6ad9105cebbbf2f31aeb38e1f4
BLAKE2b-256 checksum
How to use checksums
d2319345b52ff03cb66ebae248b4b5253f1c25c9b20dd479f284871d93606fff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 5, 2026.

Transparency log

Security for bakong-khqr-image 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for bakong_khqr_image-0.1.4.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page