qrid 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qrid-1.0.0/.gitignore +7 -0
- qrid-1.0.0/PKG-INFO +151 -0
- qrid-1.0.0/README.md +126 -0
- qrid-1.0.0/pyproject.toml +33 -0
- qrid-1.0.0/qrid/__init__.py +3 -0
- qrid-1.0.0/qrid/codec.py +90 -0
- qrid-1.0.0/tests/__init__.py +0 -0
- qrid-1.0.0/tests/test_codec.py +92 -0
qrid-1.0.0/.gitignore
ADDED
qrid-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qrid
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Decode and encode MergeID electronic invoice QR codes
|
|
5
|
+
Project-URL: Homepage, https://github.com/Quality-XP-Development-SESSA/qrid-python
|
|
6
|
+
Project-URL: Repository, https://github.com/Quality-XP-Development-SESSA/qrid-python
|
|
7
|
+
Author-email: Valentin Secades Mendez <vsecades@qxdev.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: codec,decoder,invoice,mergeid,qr,qrcode
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: segno>=1.6; extra == 'dev'
|
|
22
|
+
Provides-Extra: encode
|
|
23
|
+
Requires-Dist: segno>=1.6; extra == 'encode'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# qrid — Python
|
|
27
|
+
|
|
28
|
+
Decode and encode **MergeID electronic invoice QR codes**.
|
|
29
|
+
|
|
30
|
+
Python port of [`qrid/codec`](https://packagist.org/packages/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.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Decode only (no extra dependencies)
|
|
36
|
+
pip install qrid
|
|
37
|
+
|
|
38
|
+
# Decode + encode SVG
|
|
39
|
+
pip install 'qrid[encode]'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Decode
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from qrid import decode_qr_id
|
|
48
|
+
|
|
49
|
+
# `encoded` is the raw string value scanned from a MergeID QR code
|
|
50
|
+
payload = decode_qr_id(encoded)
|
|
51
|
+
|
|
52
|
+
print(payload["v"]) # Payload schema version (int, currently 1)
|
|
53
|
+
print(payload["code"]) # Installation / activity code (e.g. "ACT-001")
|
|
54
|
+
print(payload["id"]) # Tax or company ID (e.g. "3101679980")
|
|
55
|
+
print(payload["company"]) # Company legal name
|
|
56
|
+
print(payload["email"]) # Billing e-mail address
|
|
57
|
+
print(payload["address"]) # Physical address
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`decode_qr_id` strips surrounding whitespace before decoding, so strings
|
|
61
|
+
copied with accidental padding are handled transparently.
|
|
62
|
+
|
|
63
|
+
**Exceptions raised:**
|
|
64
|
+
|
|
65
|
+
| Exception | Cause |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `ValueError` | Input is not valid base64 |
|
|
68
|
+
| `json.JSONDecodeError` | Decoded bytes are not valid JSON |
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import json
|
|
72
|
+
from qrid import decode_qr_id
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
payload = decode_qr_id(raw)
|
|
76
|
+
except ValueError:
|
|
77
|
+
# QR data was not base64
|
|
78
|
+
...
|
|
79
|
+
except json.JSONDecodeError:
|
|
80
|
+
# QR data decoded but was not the expected JSON structure
|
|
81
|
+
...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Encode (requires `qrid[encode]`)
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from qrid import encode_qr_id
|
|
88
|
+
|
|
89
|
+
svg = encode_qr_id(
|
|
90
|
+
code="ACT-001",
|
|
91
|
+
id="3101679980",
|
|
92
|
+
company="Acme Corp S.A.",
|
|
93
|
+
email="billing@acme.example",
|
|
94
|
+
address="123 Main St, San José, Costa Rica",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Write to a file
|
|
98
|
+
with open("invoice_qr.svg", "w") as f:
|
|
99
|
+
f.write(svg)
|
|
100
|
+
|
|
101
|
+
# Or serve directly
|
|
102
|
+
# Content-Type: image/svg+xml
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Exceptions raised:**
|
|
106
|
+
|
|
107
|
+
| Exception | Cause |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `ImportError` | `segno` is not installed (`pip install 'qrid[encode]'`) |
|
|
110
|
+
|
|
111
|
+
## Payload format
|
|
112
|
+
|
|
113
|
+
The QR code data is a UTF-8 JSON object encoded as standard base64 (no line-breaks):
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"v": 1,
|
|
118
|
+
"code": "ACT-001",
|
|
119
|
+
"id": "3101679980",
|
|
120
|
+
"company": "Acme Corp S.A.",
|
|
121
|
+
"email": "billing@acme.example",
|
|
122
|
+
"address": "123 Main St, San José, Costa Rica"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Field | Type | Description |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `v` | `int` | Payload schema version. Currently always `1`. |
|
|
129
|
+
| `code` | `str` | Installation or activity code that links the QR to an internal record. |
|
|
130
|
+
| `id` | `str` | Tax / company registration ID. |
|
|
131
|
+
| `company` | `str` | Legal company name (UTF-8, including accented characters). |
|
|
132
|
+
| `email` | `str` | Primary billing or contact e-mail address. |
|
|
133
|
+
| `address` | `str` | Physical address of the company. |
|
|
134
|
+
|
|
135
|
+
## Requirements
|
|
136
|
+
|
|
137
|
+
| Dependency | Version | Required for |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| Python | `>= 3.9` | Always |
|
|
140
|
+
| `segno` | `>= 1.6` | `encode_qr_id()` only |
|
|
141
|
+
|
|
142
|
+
## Running tests
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pip install 'qrid[dev]'
|
|
146
|
+
pytest
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|
qrid-1.0.0/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# qrid — Python
|
|
2
|
+
|
|
3
|
+
Decode and encode **MergeID electronic invoice QR codes**.
|
|
4
|
+
|
|
5
|
+
Python port of [`qrid/codec`](https://packagist.org/packages/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.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Decode only (no extra dependencies)
|
|
11
|
+
pip install qrid
|
|
12
|
+
|
|
13
|
+
# Decode + encode SVG
|
|
14
|
+
pip install 'qrid[encode]'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Decode
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from qrid import decode_qr_id
|
|
23
|
+
|
|
24
|
+
# `encoded` is the raw string value scanned from a MergeID QR code
|
|
25
|
+
payload = decode_qr_id(encoded)
|
|
26
|
+
|
|
27
|
+
print(payload["v"]) # Payload schema version (int, currently 1)
|
|
28
|
+
print(payload["code"]) # Installation / activity code (e.g. "ACT-001")
|
|
29
|
+
print(payload["id"]) # Tax or company ID (e.g. "3101679980")
|
|
30
|
+
print(payload["company"]) # Company legal name
|
|
31
|
+
print(payload["email"]) # Billing e-mail address
|
|
32
|
+
print(payload["address"]) # Physical address
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`decode_qr_id` strips surrounding whitespace before decoding, so strings
|
|
36
|
+
copied with accidental padding are handled transparently.
|
|
37
|
+
|
|
38
|
+
**Exceptions raised:**
|
|
39
|
+
|
|
40
|
+
| Exception | Cause |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `ValueError` | Input is not valid base64 |
|
|
43
|
+
| `json.JSONDecodeError` | Decoded bytes are not valid JSON |
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import json
|
|
47
|
+
from qrid import decode_qr_id
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
payload = decode_qr_id(raw)
|
|
51
|
+
except ValueError:
|
|
52
|
+
# QR data was not base64
|
|
53
|
+
...
|
|
54
|
+
except json.JSONDecodeError:
|
|
55
|
+
# QR data decoded but was not the expected JSON structure
|
|
56
|
+
...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Encode (requires `qrid[encode]`)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from qrid import encode_qr_id
|
|
63
|
+
|
|
64
|
+
svg = encode_qr_id(
|
|
65
|
+
code="ACT-001",
|
|
66
|
+
id="3101679980",
|
|
67
|
+
company="Acme Corp S.A.",
|
|
68
|
+
email="billing@acme.example",
|
|
69
|
+
address="123 Main St, San José, Costa Rica",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Write to a file
|
|
73
|
+
with open("invoice_qr.svg", "w") as f:
|
|
74
|
+
f.write(svg)
|
|
75
|
+
|
|
76
|
+
# Or serve directly
|
|
77
|
+
# Content-Type: image/svg+xml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Exceptions raised:**
|
|
81
|
+
|
|
82
|
+
| Exception | Cause |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `ImportError` | `segno` is not installed (`pip install 'qrid[encode]'`) |
|
|
85
|
+
|
|
86
|
+
## Payload format
|
|
87
|
+
|
|
88
|
+
The QR code data is a UTF-8 JSON object encoded as standard base64 (no line-breaks):
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"v": 1,
|
|
93
|
+
"code": "ACT-001",
|
|
94
|
+
"id": "3101679980",
|
|
95
|
+
"company": "Acme Corp S.A.",
|
|
96
|
+
"email": "billing@acme.example",
|
|
97
|
+
"address": "123 Main St, San José, Costa Rica"
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Field | Type | Description |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `v` | `int` | Payload schema version. Currently always `1`. |
|
|
104
|
+
| `code` | `str` | Installation or activity code that links the QR to an internal record. |
|
|
105
|
+
| `id` | `str` | Tax / company registration ID. |
|
|
106
|
+
| `company` | `str` | Legal company name (UTF-8, including accented characters). |
|
|
107
|
+
| `email` | `str` | Primary billing or contact e-mail address. |
|
|
108
|
+
| `address` | `str` | Physical address of the company. |
|
|
109
|
+
|
|
110
|
+
## Requirements
|
|
111
|
+
|
|
112
|
+
| Dependency | Version | Required for |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| Python | `>= 3.9` | Always |
|
|
115
|
+
| `segno` | `>= 1.6` | `encode_qr_id()` only |
|
|
116
|
+
|
|
117
|
+
## Running tests
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install 'qrid[dev]'
|
|
121
|
+
pytest
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qrid"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Decode and encode MergeID electronic invoice QR codes"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Valentin Secades Mendez", email = "vsecades@qxdev.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["qrcode", "qr", "invoice", "mergeid", "codec", "decoder"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
encode = ["segno>=1.6"]
|
|
29
|
+
dev = ["pytest>=7.0", "segno>=1.6"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/Quality-XP-Development-SESSA/qrid-python"
|
|
33
|
+
Repository = "https://github.com/Quality-XP-Development-SESSA/qrid-python"
|
qrid-1.0.0/qrid/codec.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import binascii
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
from typing import TypedDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QRIdPayload(TypedDict):
|
|
9
|
+
v: int
|
|
10
|
+
code: str
|
|
11
|
+
id: str
|
|
12
|
+
company: str
|
|
13
|
+
email: str
|
|
14
|
+
address: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def decode_qr_id(encoded: str) -> QRIdPayload:
|
|
18
|
+
"""Decode a base64-encoded QR ID string into its structured payload.
|
|
19
|
+
|
|
20
|
+
Mirrors QRId\\Codec::decodeQRId() in qrid/codec (PHP).
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
encoded: The base64 string read from a MergeID QR code.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
A TypedDict with keys: v, code, id, company, email, address.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: When the base64 input is malformed.
|
|
30
|
+
json.JSONDecodeError: When the decoded content is not valid JSON.
|
|
31
|
+
"""
|
|
32
|
+
trimmed = encoded.strip()
|
|
33
|
+
try:
|
|
34
|
+
json_bytes = base64.b64decode(trimmed, validate=True)
|
|
35
|
+
except binascii.Error:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
"Invalid base64 input: could not decode the provided string."
|
|
38
|
+
)
|
|
39
|
+
return json.loads(json_bytes.decode("utf-8"))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def encode_qr_id(
|
|
43
|
+
code: str,
|
|
44
|
+
id: str,
|
|
45
|
+
company: str,
|
|
46
|
+
email: str,
|
|
47
|
+
address: str,
|
|
48
|
+
) -> str:
|
|
49
|
+
"""Encode invoice identity fields and return an SVG QR code string.
|
|
50
|
+
|
|
51
|
+
Mirrors QRId\\Codec::encodeQRId() in qrid/codec (PHP).
|
|
52
|
+
Requires the encode extra: pip install 'qrid[encode]'
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
code: Installation or activity code.
|
|
56
|
+
id: Tax / company registration ID.
|
|
57
|
+
company: Company legal name (UTF-8).
|
|
58
|
+
email: Primary billing or contact e-mail address.
|
|
59
|
+
address: Physical address of the company.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
An SVG markup string containing the generated QR code.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ImportError: When the segno package is not installed.
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
import segno
|
|
69
|
+
except ImportError:
|
|
70
|
+
raise ImportError(
|
|
71
|
+
"segno is required for encode_qr_id(). "
|
|
72
|
+
"Install with: pip install 'qrid[encode]'"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
payload: QRIdPayload = {
|
|
76
|
+
"v": 1,
|
|
77
|
+
"code": code,
|
|
78
|
+
"id": id,
|
|
79
|
+
"company": company,
|
|
80
|
+
"email": email,
|
|
81
|
+
"address": address,
|
|
82
|
+
}
|
|
83
|
+
encoded = base64.b64encode(
|
|
84
|
+
json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
85
|
+
).decode()
|
|
86
|
+
|
|
87
|
+
qr = segno.make_qr(encoded)
|
|
88
|
+
buf = io.BytesIO()
|
|
89
|
+
qr.save(buf, kind="svg", nl=False)
|
|
90
|
+
return buf.getvalue().decode("utf-8")
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from qrid import QRIdPayload, decode_qr_id, encode_qr_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# ── helpers ───────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
def encode(payload: dict) -> str:
|
|
12
|
+
return base64.b64encode(
|
|
13
|
+
json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
14
|
+
).decode()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def sample_payload(**overrides) -> QRIdPayload:
|
|
18
|
+
p: QRIdPayload = {
|
|
19
|
+
"v": 1,
|
|
20
|
+
"code": "ACT-001",
|
|
21
|
+
"id": "3101679980",
|
|
22
|
+
"company": "Acme Corp S.A.",
|
|
23
|
+
"email": "billing@acme.example",
|
|
24
|
+
"address": "123 Main St, San José, Costa Rica",
|
|
25
|
+
}
|
|
26
|
+
p.update(overrides) # type: ignore[typeddict-item]
|
|
27
|
+
return p
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ── decode_qr_id ──────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
class TestDecodeQRId:
|
|
33
|
+
def test_returns_all_fields(self):
|
|
34
|
+
result = decode_qr_id(encode(sample_payload()))
|
|
35
|
+
|
|
36
|
+
assert result["v"] == 1
|
|
37
|
+
assert result["code"] == "ACT-001"
|
|
38
|
+
assert result["id"] == "3101679980"
|
|
39
|
+
assert result["company"] == "Acme Corp S.A."
|
|
40
|
+
assert result["email"] == "billing@acme.example"
|
|
41
|
+
assert result["address"] == "123 Main St, San José, Costa Rica"
|
|
42
|
+
|
|
43
|
+
def test_handles_utf8(self):
|
|
44
|
+
result = decode_qr_id(
|
|
45
|
+
encode(sample_payload(
|
|
46
|
+
company="Société Générale",
|
|
47
|
+
address="Paseo Colón, San José, Costa Rica",
|
|
48
|
+
))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
assert result["company"] == "Société Générale"
|
|
52
|
+
assert result["address"] == "Paseo Colón, San José, Costa Rica"
|
|
53
|
+
|
|
54
|
+
def test_trims_surrounding_whitespace(self):
|
|
55
|
+
result = decode_qr_id(" " + encode(sample_payload()) + " ")
|
|
56
|
+
assert result["code"] == "ACT-001"
|
|
57
|
+
|
|
58
|
+
def test_raises_value_error_on_invalid_base64(self):
|
|
59
|
+
with pytest.raises(ValueError, match="Invalid base64 input"):
|
|
60
|
+
decode_qr_id("not-valid-base64!!!")
|
|
61
|
+
|
|
62
|
+
def test_raises_on_non_json_payload(self):
|
|
63
|
+
with pytest.raises(json.JSONDecodeError):
|
|
64
|
+
decode_qr_id(base64.b64encode(b"not json").decode())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ── encode_qr_id ──────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
class TestEncodeQRId:
|
|
70
|
+
def test_returns_svg_string(self):
|
|
71
|
+
svg = encode_qr_id(
|
|
72
|
+
code="ACT-001",
|
|
73
|
+
id="3101679980",
|
|
74
|
+
company="Acme Corp S.A.",
|
|
75
|
+
email="billing@acme.example",
|
|
76
|
+
address="123 Main St",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert isinstance(svg, str)
|
|
80
|
+
assert "<svg" in svg.lower()
|
|
81
|
+
|
|
82
|
+
def test_round_trip(self):
|
|
83
|
+
p = sample_payload()
|
|
84
|
+
encoded = base64.b64encode(
|
|
85
|
+
json.dumps(p, ensure_ascii=False).encode("utf-8")
|
|
86
|
+
).decode()
|
|
87
|
+
|
|
88
|
+
decoded = decode_qr_id(encoded)
|
|
89
|
+
|
|
90
|
+
assert decoded["code"] == p["code"]
|
|
91
|
+
assert decoded["id"] == p["id"]
|
|
92
|
+
assert decoded["company"] == p["company"]
|