Transaction Toolkit

Turning live transaction history into a repeatable investigation workflow

A local-first analytics and diagnostics tool that snapshots payment transaction history into a normalized local dataset, then supports repeated analysis across declines, authentication, retries, customer behavior, concentration, and operational signals.

Role
Developer Support Engineer
Stack
TypeScript • React • Express • SQLite • Tailwind • Recharts • OpenAI

The Problem

The Transaction History API supports live queries, and that's exactly what it's built for. But support and operations investigation often means slicing the same time window from several different angles — by decline reason, processor, card type, retry behavior, 3DS outcome, customer behavior, concentration — one after another, against a system designed for point-in-time lookups rather than repeated analytical passes.

The problem wasn't fetching transactions. It was: how do you turn a live API response into a stable investigation workspace that can be examined repeatedly without continually re-querying the source system?

Snapshot Once, Analyze Many Ways

Large date ranges are fetched in 30-day windows and parsed from XML, then normalized into a stable internal transaction model before anything is persisted. That normalized data lands in SQLite as an immutable snapshot — one merchant, one date range, frozen in place. Every analysis module reads from that same snapshot instead of interpreting raw API responses on its own.

Source
Transaction History API
Normalization
Paginated XML FetchField Normalization
Snapshot
Immutable Snapshot (SQLite)
Analysis
Declines3DS / AuthRetriesCustomersConcentrationSignals
Output
Structured Reports

Designing for Investigation

The application isn't a static report generator. One normalized snapshot supports several independent questions, each backed by its own analysis module reading through a shared query layer rather than the raw API — with signal ranking designed to surface what's actually material rather than dump every available metric.

Declines3DS & AuthenticationRetriesCustomersConcentrationSignals

The value of the snapshot is not persistence alone — it creates a stable analytical boundary that every investigation module can share.

AI Without Giving Up Determinism

For the reports that support it, the deterministic report is computed every time — regardless of whether AI is enabled, available, or succeeds. The LLM was never the fallback architecture; it's an optional layer on top of a report generator that always runs on its own.

Computed Metrics Payload
Deterministic Reportalways runsOptional LLM Explanationif enabled & available
Final Report

AI explains a computed result. It does not decide what the result is.

Both paths — the deterministic report and the optional LLM explanation — consume the same pre-computed, typed metrics payload, so anything the model explains is grounded in the same numbers the deterministic report already produced. The payload contract structurally excludes raw transaction rows: no customer IDs, no invoice IDs, no cardholder data reach the model at all. OpenAI is the only implemented provider.

What AI Does

  • Explains already-computed metrics for three report types: Investigation Overview, Decline Investigation, and 3DS Performance.
  • Reads from a pre-aggregated, typed payload — the same one the deterministic report uses.

What AI Never Does

  • Query the database or choose which records to inspect
  • Retrieve documents or access raw transaction rows
  • Calculate metrics or select the report schema
  • See customer IDs, invoice IDs, or cardholder data

Traceable Reports & Local-First Security

Traceable Reports

Every generated report stores enough metadata to reconstruct how it was produced: payload version, prompt version, generated-by mode (llm or deterministic), LLM status, an explicit reason when the LLM was skipped or failed, the complete aggregate payload, the complete prompt, and the generated markdown itself.

Versioned payload and prompt contracts preserve the context behind historical reports even as report logic evolves.

Local-First Security

The application is intentionally local and single-operator, not multi-tenant SaaS. Gateway passwords and the OpenAI API key are encrypted at rest with AES-256-GCM, using a random IV per encryption and a scrypt-derived key. The encryption key exists only in process memory while the vault is unlocked, and is explicitly zeroed before being discarded when it locks — there's no persistent login or session system, so restarting the app returns it to a locked state.

The security model matches the product boundary: sensitive credentials remain local, encrypted at rest, and unavailable once the vault is locked.

Results

  • Built an end-to-end snapshot ingestion pipeline around a payment transaction-history API.
  • Normalized heterogeneous API responses into a shared analytical model.
  • Created reusable analysis modules for declines, authentication, retries, customers, concentration, and operational signals.
  • Implemented three structured investigation report types.
  • Designed optional AI reporting around pre-aggregated, PII-excluding typed payloads.
  • Preserved deterministic report generation regardless of AI availability.
  • Added versioned report lineage and encrypted local credential storage.

Technical Summary

Frontend
React • TypeScript • Vite • Tailwind • Recharts
Backend
Node.js • Express • TypeScript • SQLite
Integration
Transaction History API • XML normalization (fast-xml-parser) • OpenAI
Architecture
Immutable snapshots • Shared query layer • Typed report contracts • Deterministic + optional AI reporting • Encrypted local vault (Node crypto)

Let’s Work Together

Interested in working together or have a question? Reach out and let’s talk!