Fixed-Geometry Physical-Digital QR Generator. This service creates a deterministic, copy-sensitive QR with an embedded center pattern and returns a Base64 PNG for client-side rendering or storage. The geometry is fixed to preserve Error Correction Level H compatibility.
- Payload structure is fixed to 34 bytes.
- Token length is ~81 chars: Base64Url(48) + "." + HMAC(32).
- Output QR size is 600x600 px with border=1.
- Center pattern crop for verification is 154x154 px.
- Verification thresholds: authentic > 0.70, fake < 0.55.
The service embeds a unique, copy-sensitive "Center Pattern" into a standard QR code. This pattern acts as a physical-digital fingerprint, distinguishing it from standard, easily copyable QRs.
Figure 1A: Standard Generic QR (Easily cloned) |
Figure 1B: Protected QR (Copy-sensitive embedded pattern) |
- Node.js API: REST interface, validation, token packing, audit storage.
- Python Core: Stateless QR generator and verifier using Base64 I/O.
- MongoDB: Audit and verification logs.
protected-qr-service/
docker/
Dockerfile.node
Dockerfile.python
python-core/
app.py
qr_center.py
qr_decoder.py
qr_protected.py
requirements.txt
src/
config/
controllers/
dtos/
middleware/
routes/
services/
utils/
docker-compose.yml
swagger.yaml
README.md
POST /api/v1/qr/generatePOST /api/v1/qr/verifyGET /health
Swagger spec: swagger.yaml
All input fields must be hex strings of fixed length:
data_hash: 8 hex chars (4 bytes)metadata_series: 16 hex chars (8 bytes)metadata_issued: 16 hex chars (8 bytes)metadata_expiry: 16 hex chars (8 bytes)
Example conversion guide:
- 4 bytes (8 hex chars):
a1b2c3d4 - 8 bytes (16 hex chars):
1234567890abcdef
{
"data_hash": "a1b2c3d4",
"metadata_series": "1234567890abcdef",
"metadata_issued": "0011223344556677",
"metadata_expiry": "8899aabbccddeeff"
}POST /api/v1/qr/verify with multipart form-data field image.
Success response:
{
"success": true,
"data": {
"token": "base64url.payload.hmac",
"qr_image_base64": "iVBORw0..."
}
}Verification response:
{
"is_authentic": true,
"confidence_score": 0.85,
"decoded_meta": {
"data_hash": "a1b2c3d4",
"metadata_series": "1234567890abcdef",
"metadata_issued": "0011223344556677",
"metadata_expiry": "8899aabbccddeeff"
}
}Error response:
{
"success": false,
"error": {
"message": "Invalid request body",
"details": {
"errors": {
"data_hash": ["Required"]
}
}
}
}- Docker + Docker Compose
- Node.js 18+
- Python 3.9+
docker-compose up -d --buildServices:
- API: http://localhost:8080
- Python core: http://localhost:8000
- MongoDB: mongodb://localhost:27017
cd protected-qr-service
npm installcd protected-qr-service
npm run build
npm run startTerminal 1 (Node API):
cd protected-qr-service
npm run devTerminal 2 (Python core):
cd protected-qr-service/python-core
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --host 0.0.0.0 --port 8000Generate:
curl -X POST http://localhost:8080/api/v1/qr/generate \
-H "Content-Type: application/json" \
-d '{
"data_hash": "a1b2c3d4",
"metadata_series": "1234567890abcdef",
"metadata_issued": "0011223344556677",
"metadata_expiry": "8899aabbccddeeff"
}'Verify:
curl -X POST http://localhost:8080/api/v1/qr/verify \
-F "image=@/path/to/qr.png"- Structured logs are emitted as JSON via Winston.
- HTTP access logs use Morgan and flow into Winston.
- Use your platform log aggregator (e.g., ELK, CloudWatch, Stackdriver) for indexing.
See .env.example.
| Name | Required | Description |
|---|---|---|
PORT |
Yes | API HTTP port. |
MONGO_URI |
Yes | MongoDB connection string. |
MONGO_DB |
Yes | MongoDB database name. |
PYTHON_SERVICE_URL |
Yes | Base URL for the Python core service. |
HMAC_SECRET |
Yes | HMAC secret for token signing. |
LOG_LEVEL |
No | Log level for structured output. |
REQUEST_TIMEOUT_MS |
No | Timeout for Python core requests. |
- The QR geometry and thresholds are contract-bound and should not be modified.
- The Python core is stateless and uses Base64 I/O only.
- MongoDB stores audit logs and verification results for compliance.
- If verification returns zero confidence, check image quality and ensure the QR is not cropped.
- If the Python core fails to start, verify dependencies in python-core/requirements.txt.
- If generate requests time out, increase
REQUEST_TIMEOUT_MSin the environment.
- Rotate
HMAC_SECRETregularly in production. - Place the API behind TLS and a rate limiter.
- Use a private network for internal Python core access.
Found a bug? Please report it using our Bug Report Form.
Have an idea for a new feature? Submit a Feature Request Form.
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: nhat.dang2004.cv@gmail.com
Made with care by MinzNhat

