Skip to main content

Validate and normalize Stripe, Shopify, and custom billing data for Factur-X/ZUGFeRD

Project description

TrustRender

Validate and normalize billing data from Stripe, Shopify, and custom systems before Factur-X/ZUGFeRD embedding.

If you're bridging a non-compliant billing platform into EU e-invoicing, libraries like factur-x and drafthorse assume your data is already correct. TrustRender catches arithmetic mismatches, field misalignment, missing required fields, and structural problems before compliant XML gets embedded.

pip install trustrender
trustrender validate invoice.json

What it does

Takes invoice JSON from Stripe, Shopify, custom billing APIs, or legacy exports and tells you whether it's safe to embed as Factur-X/ZUGFeRD.

$ trustrender validate quickbooks_invoice.json

Invoice:   INV-2026-5541
From:      Summit Analytics Co.
To:        Horizon Financial
Items:     2
Total:     $10,524.50

Normalizations (25):
  DocNumber → invoice_number        CompanyName → sender.name
  TxnDate → invoice_date            customer.Name → recipient.name
  Line → items                      SubTotal → subtotal
  ... and 19 more

PASS — invoice data is valid

Safe to embed in Factur-X/ZUGFeRD PDF.

Bad data gets blocked:

$ trustrender validate ocr_extracted_invoice.json

BLOCKED — 2 problem(s)

  items[1] total is wrong
    You entered $459.00 but math says $450.00
    Fix the line total or the price/quantity.

  Subtotal is wrong
    Lines add up to $12,459.00 but you listed $12,450.00

This invoice cannot be processed until the problems above are fixed.

Install

pip install trustrender

Core install requires only drafthorse. No Typst, no browser, no heavy deps.

Optional extras:

pip install "trustrender[zugferd]"    # XSD/Schematron validation
pip install "trustrender[render]"     # PDF rendering via Typst
pip install "trustrender[all]"        # everything

Requires Python 3.11+.

Python API

from trustrender import validate_invoice

result = validate_invoice({
    "invoiceNo": "INV-001",
    "vendor": {"companyName": "Acme Corp"},
    "customer": {"Name": "Client Inc"},
    "LineItems": [{"desc": "Widget", "qty": 2, "unitPrice": 50, "amount": 100}],
    "SubTotal": 100,
    "tax": 8.50,
    "TotalAmt": 108.50,
}, zugferd=True)

if result["render_ready"] and result.get("zugferd_ready"):
    # safe to call factur-x / drafthorse
    print("All checks passed")
else:
    for error in result["errors"]:
        print(f"BLOCKED: {error['message']}")

validate_invoice() returns:

  • status: "ready" | "ready_with_warnings" | "blocked"
  • render_ready: bool
  • canonical: normalized invoice dict (all fields in canonical names)
  • errors: list of blocking issues with rule_id, path, expected/actual
  • warnings: advisory issues
  • normalizations: field-level provenance (what was renamed, coerced, computed)
  • zugferd_ready: bool (if zugferd=True)

What it normalizes

90+ vendor field aliases across QuickBooks, Xero, Stripe, and generic CSV/ERP formats:

Source field Canonical field
DocNumber, invoiceNo, inv_no, ref invoice_number
CompanyName, account_name, bill_from_name sender.name
customer, billTo, Contact recipient
Line, LineItems, entries, products items
UnitPrice, cost, rate, unitCost unit_price
Amount, LineAmount, line_total line_total
SubTotal, net_total, sub_total subtotal
TotalAmt, grand_total, amount_due total

Plus: type coercion ("$1,234.56"1234.56), date parsing ("April 10, 2026"2026-04-10), computed defaults (missing line_total = qty × price), near-match typo detection (invioce_number → suggests invoice_number).

What it checks

7 deterministic semantic checks, all arithmetic:

Check What it catches
identity.invoice_number Missing or empty invoice number
identity.sender_name Missing vendor/sender name
identity.recipient_name Missing recipient/buyer name
items.non_empty No line items
arithmetic.line_total line_total ≠ qty × unit_price
arithmetic.subtotal subtotal ≠ sum of line_totals
arithmetic.total total ≠ subtotal + tax_amount

No AI. No heuristics. Every check is deterministic and objectively verifiable.

CLI

trustrender validate <data.json> [--zugferd] [--format text|json]
trustrender ingest <data.json> [-o canonical.json]
trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931]
trustrender preflight <template> <data.json> [--zugferd en16931]
trustrender serve --templates <dir> [--port 8190]
trustrender doctor [--smoke]

Integration with factur-x / drafthorse

TrustRender validates and normalizes. You generate and embed with the library of your choice.

from trustrender import validate_invoice

# Step 1: Validate with TrustRender
result = validate_invoice(messy_data, zugferd=True)
if not result["render_ready"] or not result["zugferd_ready"]:
    raise ValueError(f"Invoice blocked: {result['errors']}")

# Step 2: Use the canonical payload with drafthorse or factur-x
canonical = result["canonical"]
# ... your existing ZUGFeRD generation code here

EN 16931 e-invoicing (narrow scope)

Catches many document-level and ZUGFeRD/EN 16931 readiness issues before embedding. Currently supports:

  • Domestic German B2B invoices with standard VAT, EUR, SEPA payment
  • Single or mixed VAT rates (7% + 19%)
  • Invoice type 380 and credit note 381
  • PDF/A-3b with embedded CII XML (requires trustrender[render])

Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR currencies.

See docs/einvoice-scope.md for the full scope matrix.

Optional: PDF rendering

If you also want TrustRender to generate PDFs (not just validate):

pip install "trustrender[render]"
trustrender render invoice.j2.typ data.json -o invoice.pdf --zugferd en16931

Rendering uses Typst — no browser, no Chromium. Fast and deterministic.

What this is not

  • Not a full AP automation platform
  • Not an e-invoice compliance certification
  • Not an AI-powered data fixer (all corrections are deterministic)
  • Not a replacement for factur-x or drafthorse — it's the validation layer you run before them

Development

pip install -e ".[dev]"
trustrender doctor --smoke
pytest

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trustrender-0.3.0.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trustrender-0.3.0-py3-none-any.whl (1.7 MB view details)

Uploaded Python 3

File details

Details for the file trustrender-0.3.0.tar.gz.

File metadata

  • Download URL: trustrender-0.3.0.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for trustrender-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b94fa27d9c753ea0401717d8571eb97d22a1e965204f6104d227e9c922d8c460
MD5 275755a4a0deab8c45c274a9a770e844
BLAKE2b-256 f4a2a98d5daec34e7327b570ba5e451518a7386946d7b82c6cf6522f94c04e91

See more details on using hashes here.

File details

Details for the file trustrender-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: trustrender-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for trustrender-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7ba02dc5617c37df7458064ca3cd963db5bc8e9d35e1e028d509a9e763c1072
MD5 cd0ae7381cbce125ab61404b61fead11
BLAKE2b-256 a6e81e4c31ae11cad26335cc15d36e0e9a3a173eb5df0c21d16efb02bc428496

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page