Skip to content
Case studyIndependent product · February–September 2026

Turning legacy payment files into structured XML

Felix Hellström

Felix Hellström

Founder, Klarhimmel

Document stacks connected through a navy gateway by a blue ribbon, with a check token on the output

Accounting systems can produce files in a format that the next system cannot use. I built a small web service to explore that problem for Swedish Bankgirot payment files: take a legacy file, parse its records and return structured ISO 20022 XML.

This is an independent product project. It is a converter, rather than a service that submits payments to a bank.

One job, with explicit steps

The conversion follows a simple sequence: detect the input format, parse its fields, build an internal model, generate XML and validate the result against a bundled schema before returning it.

The tool handles LB supplier-payment files and BgMax incoming-payment reports. Its documented output formats are pain.001.001.03 for LB and camt.054.001.02 for BgMax. The message versions are part of the implementation, rather than a promise that every bank accepts the output.

Treat the file format as a contract

The legacy files use fixed-width records. The parser reads fields by position, so a misplaced field can produce the wrong interpretation even when a file looks plausible.

An early implementation had field-position errors. Checking those positions against the format specification became a necessary part of the work. Payment amounts are held internally as integer öre and converted to decimal amounts when the XML is written.

Make conversion available through a small interface

The Python service provides conversion, format detection, conversion with a summary and batch conversion. A plain web page offers upload and download controls.

The implementation includes XML schema validation and tests using fictional payment data. Files are processed within the request; the conversion design does not require a database of payment records.

What was built

The build covers parsers for LB and BgMax fixed-width records, a shared internal data model with XML generators, validation against bundled ISO 20022 schemas, a web interface and API with batch conversion, and test fixtures with documentation for the format mappings.

The service began as a Python/FastAPI application and later moved to a Cloudflare Python Worker. That hosting change revealed an operational gap: the original in-memory rate limiter depended on threads and was disabled in the Worker environment. Restoring an effective deployment-level limit remains a task before broader use.

What the checks can and cannot establish

Schema validation checks the shape of the generated XML. A bank can require a particular message version, additional fields or its own profile. A valid XML document is therefore not evidence of bank acceptance.

The project write-up records a responding health endpoint on 6 October 2026. It does not establish production bank acceptance, customer adoption or conversion volumes. Those would require separate verification.

What I would do next

Start with the receiving bank's requirements and representative, approved sample files. Confirm the format mapping and acceptance process before widening the service, and recheck the hosting safeguards after every platform change.

The lesson extends beyond payments: when converting between systems, make the mapping explicit and keep structural validation separate from acceptance by the destination.

The illustration represents conversion and checking. It contains no real payment information.

See your own company the way the market does

A free Snapshot reads your website and reports your competitors, your audience, your visibility in search and AI answers, and three ranked opportunities.