Inspiration

Small businesses and freelancers regularly verify incoming supplier invoices by hand before sending money: Does the supplier match our purchase order? Do net + VAT add up to the billed gross total? Did someone already pay this invoice reference? Letting an autonomous LLM trigger payments directly creates unacceptable accounting and security risk—so we designed a strict separation between probabilistic AI assistance and deterministic payment authorization.

What it does

SyntraVariga Invoice & Payment Guard runs a six-stage auditable workflow under branch feature/invoice-guard-public (ai-studio/):

  1. Structured Field Proposal (POST /api/ai/extract): Extracts vendor, invoice_number, net_amount, tax_amount, gross_amount, and amount from fictitious English or German invoice text. Missing placeholders (---, N/A) and ambiguous conflicting totals are surfaced with explicit manual-review warnings rather than guessed. Runs 100% locally via a transparent deterministic regex parser (non-AI fallback) by default and calls Google Gemini (configured gemini-3.8-flash primary with resilient Flash failover—verified live on gemini-3.5-flash-lite) only with explicit user opt-in (allow_external_ai: true).
  2. Human Inspection & Confirmation: A reviewer inspects, edits, or selects ambiguous candidates manually. Any field edit immediately invalidates any prior sealed verification receipt.
  3. Deterministic Integer-Cent Verification (invoiceGuard.ts): Evaluates confirmed fields strictly in integer cents (Net + Tax === Gross === Payable Amount), applies Unicode NFKC supplier comparison against the expected purchase order, and supports optional SHA-256 duplicate-invoice checks (DUPLICATE_INVOICE). Only deterministic rules decide FIELDS_CONSISTENT vs. REVIEW_REQUIRED.
  4. Tamper-Evident Audit Ledger & Zero-PII Explainer (POST /api/ai/explain): Seals each review inside an append-only RFC 8785 canonical JSON + FIPS 180-4 SHA-256 chain stored in ACID SQLite. By default it uses a deterministic local rule dictionary (non-AI); when explaining findings with explicit Gemini opt-in, only { status, finding_codes } leave the server—zero invoice vendor names, numbers, or amounts.
  5. Server-Bound PayPal Sandbox Checkout & Crash Recovery: Reserves the verified one-time receipt in SQLite (BEGIN IMMEDIATE) with deterministic PayPal-Request-Id idempotency keys before calling https://api-m.sandbox.paypal.com/v2/checkout/orders, binds EUR currency, verified_amount_eur, and custom_id to the server-side receipt, enforces explicit buyer approval before capture, validates raw POST /capture responses before querying GET /v2/checkout/orders/{id} when custom_id is omitted (rejecting any amount/status/capture-ID contradictions fail-closed), and transitions gateway timeouts or interrupted operations into UNKNOWN_RECONCILIATION_REQUIRED for manual reconciliation rather than blind retries.
  6. Batch Sample Triage & Isolated Negative Self-Tests: Demonstrates deterministic triage across 5 synthetic invoices and executes 4 isolated negative checks (receipt forgery guard, post-review amount mismatch, atomic replay block, and Zero-PII payload guard) in a temporary SQLite store without touching the main audit ledger.

How we built it

  • Frontend (ai-studio/src/): React 19, TypeScript, Tailwind CSS, Lucide icons, WCAG contrast & keyboard-accessible Audit Ledger explorer, mobile-responsive layout, and persistent SQLite payment resume/reconciliation views.
  • Backend & Persistence (ai-studio/server.ts, persistentStore.ts): Node 24 (node:sqlite WAL + native node:crypto SHA-256), Express with exact scheme://host:port validation and single-instance PID locking (acquireSingleInstanceServerLock), strict PayPal response verification (paypalSandbox.ts), and bounded request timeouts with resilient Flash model failover on external Gemini requests.

Challenges we ran into & Honest Boundaries

  • No magic AI claims: LLMs can misread numbers or hallucinate missing VAT fields. Blocking placeholders and keeping payment locks purely deterministic prevented prompt or model quirks from unlocking checkout.
  • Database recovery vs. multi-process safety: Startup crash recovery locks transient RESERVED_ORDER and CAPTURE_PENDING rows into UNKNOWN_RECONCILIATION_REQUIRED, so supporting two Express processes on one database file would falsely lock sibling requests. We enforce a per-database .server.pid lock in startServer() and clearly scope supported deployment to one server process per database.
  • Explicit non-claims: Passing deterministic rules confirms internal arithmetic and purchase-order consistency on user-confirmed fields—it is not proof of legal invoice authenticity, supplier identity verification, or absolute prevention of out-of-band double payments.

Accomplishments & Verification

  • Automated Test Matrix: 51 TypeScript tests (npm test—including multi-process child crash recovery, concurrent SQLite lock race checks, AIza*/AQ.* key & Flash failover checks, strict PayPal capture contradiction & POST-before-GET reconciliation checks, exact host/origin checks, and isolated HTTP end-to-end lifecycle) + 7 reference Python tests (npm run test:python), passing tsc --noEmit and Vite production build (0 warnings).
  • Performance Optimization: Replaced repeated pure-JS full-ledger passes with single-transaction native node:crypto verification (600 events: GET /api/ledger 87.51 ms -> 34.78 ms, POST /api/review 173.50 ms -> 51.34 ms).
  • Live External APIs Verified (Fictitious Data Only, Clearly Separated from Local Non-AI Fallback):
    • Google Gemini (LIVE_GEMINI_API): Verified authenticated live /api/ai/extract (Beispieltechnik GmbH / 119.00 EUR, HTTP 200, SQLite ledger event 12) and Zero-PII /api/ai/explain (REVIEW_REQUIRED + ["EXPECTED_AMOUNT_MISMATCH"], HTTP 200, SQLite ledger event 13) via AQ.* auth key; automatic capacity failover executed cleanly on gemini-3.5-flash-lite when gemini-3.8-flash hit transient 503/504 limits and recorded the active model_id in SQLite. Default unchecked mode remains deterministic local parsing/lookup (non-AI).
    • PayPal Sandbox REST (LIVE_PAYPAL_SANDBOX_REST, https://api-m.sandbox.paypal.com): Verified live Sandbox order 8DH65564PK409905U (119.00 EUR, bound to verified receipt via custom_id), real DE Sandbox Personal test-buyer approval, captured settlement (3ND85370G4181781M, COMPLETED, 119.00 EUR), reconciliation handling, and SQLite state persistence across full server restart (LEDGER: PASS).

What we learned & What's next

Separating probabilistic AI extraction from deterministic financial state machines makes AI workflows auditable. Next steps include external head-hash anchoring, multi-currency invoice rules, and webhook reconciliation with merchant accounting ledgers.

Built With

Share this project:

Updates

Submission history