Skip to main content
YK logo
← Back to projects

Aqua Finance Middleware Frontend

The consumer finance application journey for marine and outdoor-leisure dealer financing

Overview

Aqua Finance is the consumer-facing application journey for marine and outdoor-leisure dealer financing — the multi-screen flow a customer works through to apply for financing at the point of sale, from the initial quote through document review to a signed contract. It's a sibling project to Ascent, built on a similar stack but for a different lender and dealer network, with its own set of issuer-specific requirements.


The problem

Consumer finance applications live or die on the small stuff: a phone number field that rejects a valid US number, a footer that shows the wrong lender's disclosures, a date picker that lets someone enter an invalid date of birth. None of these are hard problems individually, but a lending flow has dozens of them, and each one is a point where a real customer can get stuck applying for money they're trying to spend at a dealer, in person, while someone waits.


What I built

Issuer-controlled footer

The footer's content — disclosures, lender branding, links — needed to switch based on which issuing bank was backing a given application, rather than being fixed per deployment. I made the footer read from issuer configuration so the same build could serve multiple issuers correctly, which matters for a white-label-adjacent lending product where the legal disclosures genuinely differ by issuer.

US phone number validation

Added proper US telephone-number validation and a phone field on the document review screen, using pattern-based validation rather than a loose regex that would let through numbers that look plausible but aren't dialable.

Input hardening

Restricting date inputs to sensible ranges, issuer-specific disclaimers, and fixing a bug where the frontend's index page would 404 instead of redirecting into the flow — a small thing, but the kind of small thing that looks like the whole app is broken if a customer hits it first.

Removing moment entirely

Migrated every date-handling call off moment in favour of date-fns. moment is a large, no-longer-actively-developed dependency, and carrying it just for date formatting was pure bundle weight and future maintenance risk. This is unglamorous dependency hygiene, but on a lending app where every extra kilobyte is time-to-interactive on someone's phone in a dealership, it's worth doing properly rather than leaving for later.


Engineering decisions

Configuration over conditionals for issuer differences

The footer and disclosure work could have been done as if (issuer === 'x') branches scattered through components. I pushed the issuer-specific content into configuration instead, so adding a new issuer's disclosures is a data change rather than a new code path to test. It's the same lesson I kept relearning across the Luna Protocol projects — the parts of a multi-issuer product that feel like plumbing are the parts most worth getting right early.

A full dependency removal, not a partial migration

Rather than leaving moment in place for legacy call sites and using date-fns only for new code — the usual compromise under time pressure — I went through and removed every moment usage. Partial migrations are how a codebase ends up depending on two date libraries indefinitely; finishing the job was worth the extra time.


Stack

Layer Choice
Framework React · Create React App
State Redux Toolkit
Forms Formik + Yup
Styling styled-components · Sass · Tailwind CSS
Testing Cypress (Cucumber preprocessor) · Jest · Testing Library · MSW
Email test gmail-tester for email-based E2E flows

Reflections

This project is a good reminder that not every meaningful contribution is a new feature. The moment removal, the phone validation, the issuer-controlled footer — none of these show up in a demo, but they're the difference between a lending flow that works for every customer in every scenario and one that mostly works. On a product where the "unhappy path" is someone standing in a dealership unable to finish applying, "mostly works" isn't good enough.