FinTech banking & payments platform
Zolvat
Zolvat is an EU business banking platform - multi-currency accounts, SEPA and SWIFT transfers, cards, and payroll - regulated as an Electronic Money Institution by the Central Bank of Cyprus. I worked on the core engineering team building the Vue 3 frontend, on the parts where getting the interface wrong has consequences: opening accounts, verifying identity, and moving money.
- Role
- Software Engineer, core team
- Period
- May 2022 - Aug 2024
- Stack
- Vue 3, TypeScript, Pinia, Vite, REST
Two onboardings wearing one product
A personal account and a corporate account are the same product to a marketing page and completely different problems to a frontend. Personal signup is short and self-serve. Corporate onboarding collects company details, directors, ownership structure, and supporting documents - enough that nobody finishes it in one sitting, and plenty that they get wrong on the first attempt.
So the corporate flow had to be resumable: step state that survives leaving the page, validation that explains what a field wants rather than just rejecting it, and uploads that can be revisited without restarting. Both flows were built from the same component primitives and Pinia stores, which is what kept two very different journeys feeling like one platform instead of two products bolted together.
KYC is a state machine, not a form
Submitting verification is where most of the design work actually is. The moment an application goes in, the user is in a state they cannot control - under review, more information needed, rejected - and the interface has to represent waiting honestly.
That meant treating verification status as first-class state rather than a flag: surfacing exactly which document or field compliance came back on, keeping the rest of the application intact while one piece is re-submitted, and never implying an account is usable before it is. Shared status components meant personal and corporate applicants saw consistent language for the same underlying state.
SEPA and SWIFT don't take the same fields
A single 'send money' screen cannot serve both rails. SEPA is predictable - IBAN, BIC, euro area, well-defined validation. SWIFT is not: beneficiary bank details, correspondent and intermediary routing, and requirements that shift by destination country and currency.
The transfer flow changes shape depending on the rail, and validates as much as possible in the browser. That matters because the alternative is a payment that looks accepted and comes back rejected days later, by which point the user has moved on and the money has not. Mass payments raised the same problem at scale: payroll and supplier runs submit hundreds of transfers as one operation, so the UI needed per-row review and per-row failure rather than one all-or-nothing result.
The confirmation step is the product
Transfers are irreversible and compliance checks can hold one mid-flight, which sets a different bar for the last screen before submission. Amounts, fees, currency conversion, and beneficiary details all have to be reviewable in one place, and the submit action has to be safe against a double click, a slow network, and a user who reloads because nothing appeared to happen.
The platform also enforces Strong Customer Authentication and 3-D Secure, so a flow can be interrupted by an authentication challenge and has to resume cleanly on the other side without resubmitting. Most of the engineering in a payments frontend goes into these paths, not the happy one.
Want the longer version, or something similar built for your team?
Get in touch