This is not a story about a great idea I had in the shower. It's a story about
being hungry, tired, and slightly greedy, and deciding that the fastest path out
of that was to build something for a bank.
I'm writing it down because most "how I built X" posts skip the embarrassing
part. They start with "I noticed a gap in the market" and end with "and then it
was a huge success." That's not what happened here. What happened is: I was
desperate, I built an SDK, it mostly broke, I fixed it, and then I wrote about
it because that's how I process things.
Here's the honest version.
The foolish idea
I'd been looking at ALATPay's developer documentation for a different reason —
I wanted to integrate their payment gateway into something I was building. The
docs were fine. The JavaScript SDK was fine. But there was no Java SDK.
My first thought was the wrong one, and I want to be specific about why it was
wrong.
I thought: "If I build the Java SDK, maybe they'll pay me."
Not "maybe they'll hire me." Not "maybe it'll be a good portfolio piece." I
genuinely thought a bank would notice a random developer's unrequested
open-source project and decide to compensate them for it. That is not how banks
work. I knew that at the time, in the abstract. I chose not to know it in the
specific, because I was hungry and tired and looking for something to be true
that wasn't.
The SDK took five days to build. The compensation has, so far, been zero
naira. The portfolio piece has, so far, been worth more than I expected.
If you're reading this because you're thinking about building something for a
company hoping they'll pay you for it: don't do that. Build it because you
need it, or because it's a portfolio piece, or because you want to learn the
API. But don't build it for the money. The money is not coming.
What "everything broke" actually means
I want to describe this carefully because "everything broke" is what I told
a friend at the time, and it's accurate but not useful.
What actually happened is: everything broke at a specific moment, and then
one thing at a time after that.
The first version didn't work at all. The authentication was wrong. ALATPay
uses three headers — Authorization, Business-Id, and
Ocp-Apim-Subscription-Key — and I was sending two of them. The third one
(Ocp-Apim-Subscription-Key) is an Azure API Management convention that I
hadn't encountered before. I sent it under the wrong name, or not at all,
depending on the hour of the night.
I got 401s for a full day. Every 401 response told me the same thing — "invalid
subscription key" — and every time I read it, I thought "that can't be right, I
put the key in." I read it as a bug in their system, not a bug in my code. It
was a bug in my code.
The second version authenticated correctly but couldn't parse the response. The
list of transactions in ALATPay's response has more fields than I'd modelled.
My Jackson classes were missing feeAmount and settlementId and a dozen other
things. Jackson doesn't crash when it sees unknown fields by default — it
ignores them — but it does crash when it sees a field it thinks it knows, with
a type that doesn't match. Which was happening, because I'd guessed at the
types.
The third version worked. Then the tests failed.
This is the part that took longest, and it's the part I want to talk about
because it's not in any tutorial.
The test problem nobody warns you about
When you're building an SDK, you can't test it against the real API. Not
comfortably. Every test call costs you a real request against a real merchant
account, and if you're testing in production you're going to trip rate limits
and log a lot of nonsense in someone's audit trail.
So you use a fake server. In Java, that's usually MockWebServer — it spins up
a local HTTP server that you can tell to respond with whatever you want.
The problem I hit is that MockWebServer is a different server from ALATPay.
It doesn't authenticate, it doesn't rate-limit, it doesn't have a schema. You
tell it what to respond, and it responds. That's the whole point. But it means
that a test that passes against MockWebServer can still fail against ALATPay,
because the test is verifying my assumptions about ALATPay, not ALATPay.
The first test I wrote checked that my SDK could fetch a list of transactions.
It passed. Then I ran the same code against the real API and got a 404. My test
had been testing whether Jackson could parse a JSON string I'd written, not
whether my SDK could talk to a real server. Which is a real thing to test, but
it's not the thing I thought I was testing.
The fix was to be honest about what the tests verify. The README now says:
"All tests use MockWebServer — no real API credentials required to run them."
Which is true. It's not the same as "the SDK works against ALATPay" and I
learned not to imply that it was.
The five days between "shipped" and "actually shipped"
The commit history tells this part better than I can.
My initial commit was on October 11th. Then, five days later, on October 16th,
the last commit message is: "URL-encode user input in query and path
segments."
That's the whole story of the intermediate five days. I shipped. I told myself
it was done. Then I kept finding things.
The URL-encoding thing is a good example because it's the kind of bug that
doesn't show up in testing. If you pass a transaction ID with a space in it —
or a slash, or an ampersand — the URL is malformed and the server returns
nonsense. My tests used IDs that were safe. Real transaction IDs are not always
safe. Five days later I noticed and fixed it.
There's a version of this story where I say "I shipped the SDK in one weekend,
here's the link." That version is true in the sense that the repo existed after
the weekend. It's not true in the sense that the SDK was finished, because the
SDK wasn't finished. It was started, and it kept being started for five more
days.
I think the "shipped in a weekend" framing is a lie that hurts the people who
read it. It makes them feel bad about the fact that their project is still
breaking after a week. Their project is supposed to be still breaking after a
week. Yours was too.
What the SDK actually doesn't do
The README is honest about this, and I want to be honest about it here too:
Bank transfer is stubbed. The endpoint for generating virtual accounts
isn't in the public API documentation, so my BankTransferService throws
UnsupportedOperationException and tells you to check the docs. It's a
placeholder. It doesn't work.
USSD and static wallets are "planned." Planned means not built. I have
notes on what they'd look like. I haven't written them.
The SDK isn't on Maven Central. You can't add it as a dependency yet. You
have to clone it and build it locally. That's a real friction point, and I
haven't paid the cost of getting a publishing account set up.
You need your own credentials. The SDK doesn't come with test keys, and it
doesn't have a demo mode. To run it against anything, you need a merchant
account with ALATPay, three credentials from their dashboard, and the patience
to get them. Without those, the SDK is a very well-documented way to throw
AlatPayAuthException.
That last point is the one that limits the SDK most in practice, and it's the
one I'd change first if I were doing it again. A version of this with a
sandbox credential flow — or with a mock mode that returns fixture data without
touching the network — would be more useful than the version I shipped.
I didn't build that because I didn't know how to. That's the honest reason.
Not "I chose not to because of scope." I didn't know how.
What it was actually worth
The SDK has, at this point, been read by fewer than a hundred people. It has,
as far as I know, zero users. Nobody has opened a pull request. Nobody has sent
me an issue.
By the metrics that matter to a venture capitalist, it's a failure.
By the metrics that matter to me, it's not, because of three things that
happened after I built it:
I learned what an SDK actually is. Not "a wrapper around an API" in the
abstract, but the specific mechanics: a client object, a config object, typed
exceptions, a shared HTTP layer, tests that mock the server. I can read any
other SDK now and understand what it's doing, because I built one.
I learned how banks think about security. The three-header scheme on
ALATPay isn't decorative. It's a specific model of what can go wrong and who's
responsible for what. Building a client for it meant I had to understand it,
and understanding it taught me more about payments infrastructure than reading
a blog post would have.
I got a piece of code I can point at. When I talk to people about what I
do now — when I say I build SDKs — I have a URL. That URL is a thing I made,
and it exists, and it has tests, and it has a license, and somebody can clone
it and run it. That's worth more than the compensation I was imagining when I
started.
The compensation did not arrive. The thing I actually got was better.
If you're going to build one anyway
Build it for yourself, or for the portfolio, or because you want to learn the
API. Not because you're hoping a company will notice.
Here are the four things I'd tell anyone building their first SDK:
1. Get the auth headers right on day one. Before you build anything else,
build a single request that authenticates correctly. If the API has three
headers, test all three before you write the client. The 401 loop I spent a
day in is the most avoidable part of this entire story.
2. Model the response types conservatively. Use @JsonIgnoreProperties(ignoreUnknown = true) on every response class. Don't guess at field types. Test against a real response before you decide a field is a long instead of a String.
3. Be honest about what your tests test. MockWebServer is for verifying your own logic, not the remote server. Say that in the README.
4. Ship it before it's done, and then keep shipping it. The five-day gap between my first commit and my last is the realistic version. Nobody builds a working SDK in a weekend. If you tell yourself you have to, you'll never start.
The link
The SDK is at
github.com/Agenda-X-PR/alatpay-java-sdk.
It's MIT licensed. It works for transaction listing, single transaction
lookup, and settlements. It doesn't work for bank transfers yet, because ALATPay
hasn't published the endpoint, and I'm not going to pretend I can guess at it.
If you're a Java developer integrating ALATPay, use it. If it saves you the
401 loop I spent a day in, it's done its job.
If you're a developer thinking about building something for a company hoping
they'll pay you — read the first section of this post again, and then don't
do that.