There are a handful of projects that I still think about years later, not because they became successful products, but because they changed the way I think about engineering. The most interesting problems are not always the ones with difficult implementations. Sometimes they sit in uncomfortable spaces where technology has moved faster than regulation, where there are no established patterns to follow, and where every technical decision quietly becomes an ethical one. This is one of those projects.
Long before I found myself working on high performance electronic trading systems in London, I was an undergraduate studying Physics, Climate Science and Scientific Computing at the Kwame Nkrumah University of Science and Technology in Kumasi. Like many students who enjoyed building software, I spent much of my free time working with startups. One of them was Credit Locus, a small fintech company in Ghana. We wanted to make lending more accessible to small and medium sized enterprises by building a system that could assess whether a business could realistically afford a loan. Lending decisions in Ghana were still heavily dependent on manual reviews and paperwork, and many businesses that were perfectly capable of repaying loans struggled to demonstrate their financial health in ways traditional institutions understood.
When I joined, I assumed the difficult part would be the affordability model. We spent weeks discussing which financial signals mattered most, how transaction histories revealed spending behaviour, how recurring income should be weighted against irregular deposits, and whether historical cash flow alone was enough to estimate future affordability. Eventually we arrived at something we were genuinely happy with. It wasn't perfect, but it was good enough to evaluate real applicants. Then almost immediately we realised we had solved the wrong problem.
The model depended on transaction history, and the only practical way of obtaining it was asking applicants to download bank statements and upload them. Log into internet banking, select a date range, download a PDF, upload it, parse it, extract the numbers, feed the model. On paper it was sensible. In practice it failed constantly. People uploaded the wrong statement, selected the wrong date range, sent scanned documents our parser struggled with, or abandoned the application entirely. The people we were trying to help were not struggling because they lacked financial records. They were struggling because we had designed a process that assumed they were comfortable navigating internet banking portals and managing files. Many business owners were busy running shops, restaurants, pharmacies or trading businesses. Asking them to become document managers before they could apply for a loan introduced friction that had nothing to do with lending.
Engineers often become fascinated by the hardest technical problem in the room while quietly ignoring the simplest human one. We had spent weeks refining an affordability model that very few people could actually reach. The obstacle wasn't machine learning. It was a PDF. Once we recognised that, the question became whether the PDF needed to exist at all.
Open banking was beginning to gain attention internationally. Plaid had already shown how compelling the experience could be: instead of uploading statements, an application could request permission to access banking information directly through secure APIs. Transaction histories, balances and account information retrieved automatically. It felt almost magical. The problem was that we weren't building in the United States. Ghana had no open banking framework, no regulations defining how banks should expose customer data to third parties, no standardised APIs, and no consent protocols equivalent to the environment that made companies like Plaid possible.
I spent weeks reading everything I could find. Plaid was the obvious starting point. Their architecture made sense within American banking regulation: institutional cooperation, interfaces encouraged by interoperability and data portability rules. But I was also watching a startup in Nigeria and another in South Africa that were looking at building open banking APIs of their own. They were still relatively young, and initially I assumed they had arrived at roughly the same solution through similar technical approaches to Plaid. The more I read, the less convinced I became. Both seemed to be operating where the regulatory foundations for official bank APIs simply didn't exist yet. If banks weren't exposing official interfaces, where exactly were they getting the data from?
My curiosity shifted away from lending almost entirely. I was thinking about infrastructure. At first I assumed I had missed something: an undocumented API programme, private bank partnerships that never made it into the public domain. I read product docs, developer blogs, interviews, technical discussions. There was no hidden developer portal waiting to be discovered. Eventually I stopped looking at the companies and started looking at the mobile applications themselves. That was the moment the project became a reverse engineering problem.
I already understood how backends worked. HTTP, session management, authentication flows, how a mobile client talks to a server — none of that was new. What I didn't understand was how a startup in Nigeria or South Africa could productise access to banking data in markets with no open banking framework, no regulatory sandbox, and no publicly documented bank APIs. If the documentation wasn't going to answer that, the banking applications might. Underneath the interface, every mobile banking app is just another HTTP client. Balance enquiries, transaction histories, beneficiary lookups and account updates all become requests to a backend somewhere. If I could observe that conversation, I might understand the infrastructure those startups were building on top of.
I wasn't trying to break into banks or hunt for vulnerabilities. I was trying to reverse engineer a product category that already seemed to exist without the regulatory conditions that normally make it possible. So I treated the banking application like any other HTTP client I would inspect. I routed device traffic through Burp Suite and later mitmproxy, expecting to watch requests travel between the app and the bank's servers. A proxy sits between client and destination, decrypts HTTPS with a trusted certificate installed on the device, lets you inspect traffic, and forwards it on. Instead I got connection failures almost immediately. The applications refused to communicate.
At first I assumed I had configured something incorrectly. I checked the proxy setup, reinstalled certificates, tried multiple devices. The setup wasn't the problem. The applications knew I was watching. That was my introduction to certificate pinning.
HTTPS normally relies on a chain of trust rooted in certificate authorities the operating system already trusts. A proxy works by inserting itself into that chain and presenting its own certificate. Certificate pinning breaks that assumption. The application embeds knowledge of the server's expected certificate or public key and refuses to communicate if anything changes. From the app's perspective, my proxy wasn't helping me inspect traffic. It was impersonating the server. That is not a flaw. Pinning exists to protect users against real man-in-the-middle attacks on compromised networks. From an engineering perspective it was a beautifully simple idea. From my perspective, it meant I had another problem to solve.
What fascinated me wasn't the protection itself but where it lived. Somewhere inside the application there had to be code validating certificates, constructing requests, managing tokens and serialising responses. I started spending less time looking at packets and more time looking at applications. Android packages are remarkably transparent once you understand how an APK is structured. Resources, manifests, package structures, dependency graphs and large portions of application logic can often be reconstructed through static analysis. I moved between decompilers, bytecode, manifests and networking libraries, building a mental model of how these apps were organised. The objective was never to recover source code or reconstruct a UI. I cared about the networking layer: authentication, request construction, session management, response parsing. If I could understand those components, I could map the conversation with the backend.
Static analysis eventually became dynamic analysis. Instead of asking what classes existed, I wanted to know when they executed. Hooking frameworks, runtime instrumentation and carefully placed inspection points replaced guesswork with evidence. Every successful login revealed another piece of the authentication lifecycle. Every account summary exposed another endpoint. Every transaction history request revealed another JSON schema. What looked opaque slowly became a protocol that could be reasoned about. The satisfying part of reverse engineering isn't finding a hidden endpoint. It's the moment scattered observations fit together: a token captured earlier explains a header you couldn't identify, a meaningless parameter turns out to be an account identifier, an endpoint that returned errors begins responding once the request sequence is reconstructed. By then I wasn't thinking about banking apps anymore. I was thinking about distributed systems.
Authentication established identity. Tokens represented trust. Requests became state transitions. Responses updated local state. The interface users interacted with every day was really just a visual representation of a protocol unfolding over time. Once I could more or less visualise the authentication flow from memory, the question changed. I was no longer asking whether I could understand the protocol. I was wondering whether I could recreate just enough of it to remove the PDF from our lending process altogether. The mobile application wasn't particularly special. It was orchestrating a conversation with backend services that already knew how to do everything we needed. Its value was the experience. The underlying APIs were doing the real work.
Suppose a customer wanted to apply for a loan through Credit Locus. Instead of logging into internet banking, downloading a PDF and waiting for us to parse it, what if they authenticated once? With explicit consent, we could establish a valid session on their behalf and retrieve the same financial information the mobile app was already displaying. No document parsing, no OCR errors, no assumptions about whether someone knew where their bank stored monthly statements.
Our existing pipeline looked something like this:
Customer
│
▼
Internet Banking Portal
│
Download Statement (PDF)
│
▼
Upload to Credit Locus
│
PDF Parser
│
Financial Data Extraction
│
Loan Eligibility ModelEvery stage introduced failure. Scanned PDFs, wrong date ranges, formatting that varied by institution, parsers that needed constant maintenance. We weren't really evaluating affordability. We were recovering structured information from documents never designed for machines. The architecture I began sketching looked considerably simpler.
Customer
│
▼
Authorised Authentication
│
Session Establishment
│
Secure Token
│
▼
Transaction Retrieval
│
Financial Analysis Pipeline
│
Loan Eligibility ModelThe eligibility algorithm barely changed. Everything around it did. One of the earliest architecture lessons I learned: good engineering isn't always about making the clever part more sophisticated. Sometimes it's about removing everything surrounding it that shouldn't have existed in the first place.
Over the following weeks I built prototype connectors for several major banks in Ghana. Each institution had slightly different request structures, authentication mechanisms and response formats, but the pattern was consistent. Authenticate, establish session context, retrieve balances, account metadata and transaction history, normalise into a common internal representation, feed the affordability pipeline. In modern terms it was an aggregation layer. At the time we didn't have language for it. We were trying to stop asking customers to upload PDFs.
One decision we made very early: customer credentials would never become part of our system. Authentication happened only long enough to establish a legitimate session with the user's bank. Credentials were discarded immediately and never written to persistent storage. Even in development I was uncomfortable handling passwords. If we could accomplish the objective without retaining them, retaining them simply wasn't an option.
As the technical problems disappeared, the legal ones emerged. Nothing we were doing relied on exploiting vulnerabilities or bypassing authentication. Every request was made on behalf of an authenticated customer who had explicitly authorised us to access their own financial information for a loan application. We weren't seeing anything the customer couldn't already see in their banking app. What made the situation unusual was that the surrounding regulatory framework hadn't caught up. No open banking legislation in Ghana. No standardised APIs. No licensing requirements for third-party financial data aggregation, because as far as the ecosystem was concerned the concept barely existed. Technology had made something possible before institutions had decided how society wanted that possibility to be used.
As engineers we're often taught to think in binaries: secure or insecure, permitted or prohibited, API or no API. Reality is rarely that tidy. Sometimes there are no established conventions because the industry hasn't reached that point yet. In those moments, engineering stops being an exercise in implementation and becomes an exercise in judgement. I paid closer attention to that startup in Nigeria and the one in South Africa, not to copy them, but to understand how they were navigating the same uncertainty. It was reassuring that we weren't alone, and it reinforced something I had begun to suspect. We weren't really building a loan platform anymore. Without fully intending to, we had started building one of the earliest versions of an open banking platform for Ghana.
For weeks I had been consumed by whether we could build it. I realised I hadn't spent nearly enough time asking whether we should. Technically the architecture made sense. Consent was explicit. Credentials weren't retained. Sessions lasted only as long as needed. Every decision tried to minimise trust rather than maximise it. And yet something remained unresolved. The question was no longer whether the system respected the user's consent. It was whether consent alone was enough.
Looking back, it is easy to say that what we were attempting became open banking. Today there are frameworks, developer platforms, documented security requirements and clearer responsibilities. None of that really existed for us. We weren't operating inside an established framework. We were standing just before one. There was no RFC to read, no regulator publishing guidance, no accepted vocabulary for many of the questions we were asking. Eventually we decided not to move forward with the platform.
From a business perspective it was disappointing. We had invested enormous time into mobile banking architectures, authentication protocols and financial data pipelines. Like many startups, Credit Locus eventually returned its investment and shut down before that work became a product. For a while I saw that as failure. I don't anymore. Watching the African fintech ecosystem mature has been strangely reassuring. Open banking has continued to develop across the continent. That startup in Nigeria and the one in South Africa have grown into important parts of that ecosystem under increasingly mature regulatory environments. The architecture we imagined looks remarkably similar to systems that now exist legitimately: official APIs instead of inferred ones, standardised authentication instead of reconstructed flows, governed consent instead of product design interpreted as policy. The technical ideas weren't wrong. The timing was.
That project stayed with me for reasons that have little to do with reverse engineering. Engineering exists inside society, not outside it. A system can be technically elegant and still arrive before the institutions needed to support it. Curiosity and judgement have to grow together. Curiosity without judgement can become recklessness. Judgement without curiosity rarely builds anything interesting. Good engineering requires both. I sometimes think back to that undergraduate version of myself in front of Burp Suite, mitmproxy and decompiled Android applications, convinced the next packet capture would reveal another piece of the puzzle. At the time I thought I was learning about mobile applications and distributed systems. I was learning something more enduring: the hardest engineering decisions are rarely technical. They are the ones you make after you've proved to yourself that something is possible.
Notes
This memoir intentionally omits implementation details that would enable reproduction against real banking systems. The techniques discussed here are presented to explain the engineering thought process behind the project rather than to provide operational guidance.
The work described took place before open banking frameworks were established in Ghana and was undertaken in the context of exploring technical feasibility for a consent-based financial data aggregation platform. The project was never deployed commercially, customer credentials were never retained, and the work ultimately informed a decision not to proceed because the surrounding regulatory environment had not yet matured. Looking back, I consider that decision to have been one of the most valuable outcomes of the entire project.