There's a particular feeling familiar to anyone who has shipped software: the moment something looks finished. The endpoint responds. The tests pass. Everything suggests it's ready.
Sometimes that's true. Sometimes you've only proved your own assumptions.
The payment integration is built. The privacy layer is built — your phone knows where you are; Refueler doesn't. The order routing is built. What isn't built, in the sense of having been proven against something other than my own expectations, are the two components described below. They are the remaining blockers before the first user touches this.
A handshake that hasn't shaken
Part of how Refueler stays out of the payment layer is a webhook verification step. When a payment provider sends a notification — order settled, payment failed, retry needed — that notification arrives as an HTTP request signed with an HMAC derived from a shared secret. My implementation reproduces the same digest locally and compares. If they match, the request is genuine. If they don't, it's discarded.
The check exists. It's been in the codebase for a while now, passing every test I've thrown at it — and every one of those tests was written by me, using payloads I constructed and signed myself. Which is a bit like marking your own homework and being delighted to discover you got full marks. What it has not done is verify a single notification that actually arrived from the provider's infrastructure, signed by their system, with their canonicalisation of the request body and their timing on the timestamp. Until it does, I don't know whether my digest matches theirs, whether my replay window is correctly sized, or whether the implementation survives a production retry sequence.
PAYMENT PROVIDER │ │ POST /webhook ▼ Receive request │ ▼ Recreate HMAC digest │ ▼ Compare signatures / \ Match Reject │ Process
- ✓ Payload generated by me
- ✓ Signature generated by me
- ✓ Verification logic written
- ✓ Unit tests passing
- □ Live provider payload
- □ Live provider signature
- □ Provider canonicalisation confirmed
- □ Replay attacks tested
- □ Production retries under jitter
The same lesson, a different track
The second piece is a PKCE authentication chain — the mechanism that gets a commuter from "I want to log in" to "I am logged in" on their phone, when the login itself happens in a browser rather than inside the app. PKCE was chosen specifically because it doesn't require a client secret stored on the device, which matters when the device can be lost, jailbroken, or inspected. The right choice on paper. The right choice in the simulator.
On a physical device, the OAuth callback occasionally arrives before the PKCE verifier can be recovered from Keychain. Whether that's a race condition between the browser returning control and the Keychain write completing, a persistence issue specific to how the item was stored, or something else entirely is exactly what still needs investigation. What surfaces is a login that appears to complete, and then doesn't. The simulator never shows this. Physical hardware does.
PHONE │ ▼ Generate verifier │ ▼ Store in Keychain │ ▼ Open browser │ ▼ User logs in │ ▼ OAuth callback │ ▼ Read verifier ← ✗ Failure observed here │ ▼ Exchange code │ ▼ Logged in
- ✓ Verifier generated correctly
- ✓ Keychain write initiated
- ✓ Browser opens, user logs in
- ✓ OAuth callback received
- ✓ Simulator: full flow completes
- □ Physical device: verifier readable
- □ Race condition ruled out
- □ Survives app backgrounding
- □ Consistent across clean installs
- □ Token refresh after expiry
What connects these two isn't the engineering. It's the instinct. Both pieces produced output that looked correct. Both passed every check I'd thought to write.
What done actually means
Actually done ✗ · Live payload: never tested · Replay: never tested
- □ Live provider payload verified end-to-end against production secret
- □ Malformed and replayed requests correctly rejected under load
- □ Implementation survives provider retry sequences with timing jitter
- □ Vault secret confirmed round-trip from storage through signature comparison
Actually done ✗ · Physical device: fails · Cause: under investigation
- □ Physical device completes full exchange consistently across clean installs
- □ Keychain timing issue identified and resolved
- □ Login survives app backgrounding and interruption mid-flow
- □ Token refresh confirmed working after session expiry on device
Neither of these is a months-long rebuild. The webhook work closes once I have a sandbox endpoint receiving live provider notifications — that's the next concrete step. The Keychain issue is most likely a timing problem that the simulator papers over, which makes it diagnosable once I have the right device instrumented properly.
I could simply have delayed the launch and said nothing. I'd rather explain the specific engineering reasons than ask anyone to take "coming soon" on trust.
Refueler is not a payment processor. It doesn't hold your money. Your phone knows where you are; Refueler doesn't. Most of what the app asks you to trust is timing, not custody. But timing infrastructure still touches the moment a payment is confirmed and the moment your identity is verified, and you should know exactly how seriously the gap between "looks done" and "is done" is being taken before you ever have reason to test it yourself.
Good engineering isn't the absence of bugs. It's resisting the urge to call something finished before reality has had its turn — and then publishing the gap so that when it closes, the closure means something.