essay / payments
A smart router solved what application code could not scale
When integrating multiple encryption services across acquired brands, the answer was not more code. It was an HAProxy rule.
Expedia Group grew through acquisition. Each acquired brand came with its own payment stack, including its own encryption and decryption services for handling sensitive payment instruments. As we consolidated these brands onto a unified payment platform, we faced a routing problem: given an instrument, determine which cipher service can decrypt it, and call the right one.
The initial proposal was straightforward. Add application code to inspect the instrument, determine its origin, and route to the appropriate cipher service. It would work for the current integration. It would not work for the next five.
The pattern underneath the problem
I raised this in the design review. Every time we integrated a new brand, we would need to add more routing logic to the application. Each integration would make the code more complex, harder to test, and more fragile. The routing concern would be scattered across the application rather than isolated.
I worked with the engineers to reduce the problem to its basic form. Given an instrument and one piece of identifying information, encrypt or decrypt using a particular cipher service. The interfaces for all the different cipher services were nearly identical. The identifying information was already embedded in the instrument GUID or could be prefixed trivially.
The solution
We placed an HAProxy instance between the application and the cipher services. Routing rules inspected specific bits of the instrument GUID and forwarded requests to the correct backend. The application code remained unchanged. It called one endpoint. The router handled the rest.
The latency overhead was under 10 milliseconds for the additional hop. The project delivered on time.
Why this mattered beyond the immediate problem
When the next brand integration came, the application team did not need to touch their code. We added an HAProxy routing rule and, if needed, a thin wrapper around the new cipher service API to conform to the uniform interface. The configurations were documented on Confluence and effectively plug-and-play for each new use case.
The broader lesson: when you find yourself adding conditional routing logic into application code for the second time, it is probably an infrastructure concern pretending to be an application concern. Moving it to the right layer simplifies everything downstream.