qrid — Python
Decode and encode MergeID electronic invoice QR codes.
Python port of qrid/codec (PHP). All three implementations (PHP, Node.js, Python) share the same payload format and function signatures, so QR codes generated by any one of them scan correctly in the others.
Typical usage flow
sequenceDiagram
actor Staff as Billing Staff
participant ERP as Billing / ERP System
participant Lib as qrid
participant QR as Invoice QR Code
participant App as MergeID App
Staff->>ERP: create invoice
ERP->>Lib: encode_qr_id(id, company, email, address, activity_code)
Lib-->>ERP: SVG QR code
ERP->>QR: print / embed on invoice
Note over App,QR: later, at point of scan
App->>QR: scan with camera
QR-->>App: base64 payload string
App->>Lib: decode_qr_id(encoded)
Lib-->>App: { v, id, company, email, address, activity_code }
App-->>Staff: display verified invoice identity
Installation
# Decode only (no extra dependencies)
pip install qrid
# Decode + encode SVG
pip install 'qrid[encode]'
Usage
Decode
from qrid import decode_qr_id
# `encoded` is the raw string value scanned from a MergeID QR code
payload = decode_qr_id(encoded)
print(payload["v"]) # Payload schema version (int, currently 1)
print(payload["id"]) # Tax or company ID (e.g. "3101679980")
print(payload["company"]) # Company legal name
print(payload["email"]) # Billing e-mail address
print(payload["address"]) # Physical address
print(payload["activity_code"]) # Installation / activity code (e.g. "ACT-001"), or "" if blank
decode_qr_id strips surrounding whitespace before decoding, so strings
copied with accidental padding are handled transparently.
Exceptions raised:
| Exception | Cause |
|---|---|
ValueError |
Input is not valid base64 |
json.JSONDecodeError |
Decoded bytes are not valid JSON |
import json
from qrid import decode_qr_id
try:
payload = decode_qr_id(raw)
except ValueError:
# QR data was not base64
...
except json.JSONDecodeError:
# QR data decoded but was not the expected JSON structure
...
Encode (requires qrid[encode])
from qrid import encode_qr_id
svg = encode_qr_id(
id="3101679980",
company="Acme Corp S.A.",
email="billing@acme.example",
address="123 Main St, San José, Costa Rica",
activity_code="ACT-001",
)
# Write to a file
with open("invoice_qr.svg", "w") as f:
f.write(svg)
# Or serve directly
# Content-Type: image/svg+xml
activity_code is optional and defaults to "" (blank). A blank activity code signals a
consuming system to generate an electronic ticket instead of using an activity code.
Exceptions raised:
| Exception | Cause |
|---|---|
ImportError |
segno is not installed (pip install 'qrid[encode]') |
Payload format
The QR code data is a UTF-8 JSON object encoded as standard base64 (no line-breaks):
{
"v": 1,
"id": "3101679980",
"company": "Acme Corp S.A.",
"email": "billing@acme.example",
"address": "123 Main St, San José, Costa Rica",
"activity_code": "ACT-001"
}
| Field | Type | Description |
|---|---|---|
v |
int |
Payload schema version. Currently always 1. |
id |
str |
Tax / company registration ID. |
company |
str |
Legal company name (UTF-8, including accented characters). |
email |
str |
Primary billing or contact e-mail address. |
address |
str |
Physical address of the company. |
activity_code |
str |
Installation or activity code that links the QR to an internal record. May be blank (""), which signals a consuming system to generate an electronic ticket instead. |
Requirements
| Dependency | Version | Required for |
|---|---|---|
| Python | >= 3.9 |
Always |
segno |
>= 1.6 |
encode_qr_id() only |
Running tests
pip install 'qrid[dev]'
pytest
Publishing
Releases to PyPI are fully automated — there is no manual twine upload step:
- Bump
versioninpyproject.tomland merge tomain. release.ymlruns on every push tomain. It reads the version frompyproject.toml; if novX.Y.Ztag already exists for it, it runs the test suite and creates that tag plus a GitHub Release.- In that same run,
release.ymldispatchespublish.ymlvia the GitHub API (gh workflow run publish.yml), which re-runs the tests, builds the sdist/wheel, and uploads to PyPI using Trusted Publishing (OIDC) — no stored API token.
publish.yml is dispatched as an independent, top-level run via the Actions API rather than invoked as a reusable workflow (workflow_call) or relying on the release: published event. Both alternatives were tried and both are broken for this use case:
release: publishednever fires for releases created with the Actions-internalGITHUB_TOKEN(GitHub's anti-recursion guard).workflow_callchaining runspublish.ymlas a job withinrelease.yml's own run, so the OIDC certificate's build-config claim identifies the caller (release.yml) instead ofpublish.yml— PyPI's Trusted Publisher verification then rejects the upload outright (does not match expected Trusted Publisher). PyPI's own docs confirm reusable workflows are not supported for Trusted Publishing.
A workflow_dispatch API call made with GITHUB_TOKEN is explicitly exempt from the anti-recursion guard and starts a genuine new top-level run of publish.yml, so its OIDC certificate correctly identifies publish.yml as the source. publish.yml also still accepts release: published (for a release cut by hand) and workflow_dispatch (manual re-run) as a fallback.
Pushes to main that don't change the version are a no-op for release.yml (the tag already exists), so unrelated commits (docs, CI tweaks) don't trigger a release.
One-time PyPI setup (already done for this project, documented here for reference): add a pending trusted publisher at pypi.org/manage/account/publishing with owner Quality-XP-Development-SESSA, repository qrid-python, workflow filename publish.yml, and environment name pypi; the same pypi environment must exist under the repo's GitHub Settings → Environments.
License
MIT
Metadata
Release files for qrid 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qrid-1.1.0.tar.gz | 6.3 kB | With provenance | Details for qrid-1.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qrid-1.1.0-py3-none-any.whl | Python 3 | none | any | Details for qrid-1.1.0-py3-none-any.whl |
Total release size: 11.5 kB
Release files / qrid-1.1.0.tar.gz
| Download URL | qrid-1.1.0.tar.gz |
|---|---|
| Size | 6.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1472b2580db0098c81a2377b3f01e03c466df621f7b1786f865462b9b52823e6
|
|
BLAKE2b-256 checksum How to use checksums |
f1d4e5003da527d4866e4847605cc4f440a97612c3dcf1106e8cae64ee363d5c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 10, 2026.
Transparency logRelease files / qrid-1.1.0-py3-none-any.whl
| Download URL | qrid-1.1.0-py3-none-any.whl |
|---|---|
| Size | 5.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0ed79c01130037025226cd23e5aa569320a682404b805e2e1741c8f50526c4ba
|
|
BLAKE2b-256 checksum How to use checksums |
de94ab7e2d24c24bd7f5eff59dddbd26c5f479ae1060efb1bd61790337519ea8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 10, 2026.
Transparency logSecurity for qrid 1.1.0
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.
Attestation for qrid-1.1.0.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 Jul 10, 2026.
Transparency logA 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.