A wallet, card-acceptance and lending platform — designed, built and run by us.
A2Pay operates a merchant and consumer financial platform across South Africa. Lakshaya is
its engineering team: we architected the system, wrote it, integrated it into the national
payment rails, and we run it in production today.
Money enters the platform through card acceptance and inbound bank credits, moves across
an internal ledger that handles fees, holds and reversals, and leaves through four
separate payment rails. Merchants and consumers onboard through a verification workflow
that clears identity, bank-account, FICA and company-registry checks before a wallet can
ever be funded.
Nothing about that is a single integration. It is a ledger that must always balance, a
bank file that must be byte-perfect, an onboarding journey that must satisfy a regulator,
and a set of third parties that each fail in their own way. That is the work.
Two .NET 9 service APIs — wallet & payments, lending & origination
Three web applications — back office, point of sale, mobile onboarding
Four payment rails behind one ledger
Three environments at parity, defined entirely in Terraform
Engagement at a glance
Client
A2Pay
Relationship
Architect, build, operate
Backend
.NET 9 · clean architecture · CQRS
Front end
Angular 19 · three applications
Data
SQL Server · versioned migrations
Cloud
Azure · Terraform · containers
Identity
OIDC · permission-based authz
Assurance
Unit, integration & contract tests
4
Payment rails integrated behind a single wallet ledger
320B
Fixed-length bank records built, sequenced and reconciled automatically
10s
Dispatch and reconciliation cycle for outbound bank files
100%
Infrastructure as code across QA, staging and production
What we built
Four systems that have to agree with each other.
Wallet & transaction ledger
Customer, merchant, fee and system wallets with sequenced wallet codes, fee accounting,
statements and searchable transaction history. Every money movement carries an idempotency
token, so a retried request can never double-debit, and every failure path has an
explicit reversal.
Fee & system wallets
Idempotency tokens
Reversals
Statements
Payment rails & bank files
PayShap proxy payments with remittance information, duplicate-submit protection and a
background status monitor; real-time credit; one-day EFT; and host-to-host batch files in
fixed-length 320-byte records delivered over SFTP — with acknowledgements, full and
partial rejections, duplicates and unpaids all parsed and matched back to the ledger.
PayShap
Real-time
One-day EFT
CPS host-to-host
Onboarding, KYC & merchant verification
A custom workflow engine drives wallet onboarding and merchant enrolment as typed state
machines with permission groups, fault recovery and step-by-step audit history. Identity,
bank-account, FICA and company-registry checks run inside the flow, with name-match
scoring and every third-party response persisted.
Workflow engine
IDV & FICA
Bank account verification
Registry validation
Card acceptance & lending
Tap on Phone acceptance turning an NFC handset into a terminal, sub-merchant gateway
onboarding and webhook-driven settlement into the wallet. Alongside it, a lending module
covering applications, affordability, two-stage approval, agreements, disbursement,
billing and claims.
Tap on Phone
Sub-merchant onboarding
Affordability & approvals
Collections
The hard parts
Where the engineering actually went.
Any team can move a balance between two rows. These are the problems that decide whether a
payments platform survives its second year.
Reconciliation
CONTINUOUS
Batch rails answer asynchronously, in files, hours later, and sometimes reject only part
of what you sent. A background service dispatches outbound files on a ten-second cycle
and ingests every response type the bank produces, updating individual payments rather
than whole batches — so a single rejected item never strands the other 999.
Exactly-once money
INVARIANT
A timeout is not a failure — it is an unknown. Idempotency tokens, an explicit payment
state machine and status polling let the platform re-ask "what happened to this payment?"
safely, instead of guessing and paying twice. Declined refunds and failed transactions
reverse cleanly, once.
Partner failure
ISOLATED
Every external provider sits behind its own typed client with retry, exponential backoff,
timeout and full request/response audit logging. A scheme outage or a 500 from a
verification provider degrades one capability for a few minutes; it does not take the
platform down or corrupt the ledger.
Re-platforming
IN FLIGHT
The estate has been moved from a self-managed Kubernetes deployment onto Azure-native
services described entirely in Terraform — WAF-protected ingress, private networking,
managed secrets and configuration, and identical QA, staging and production definitions.
No environment is a hand-built snowflake.
The difference between a demo and a payments platform is everything that happens after the
happy path — the rejected file, the timed-out proxy, the duplicate submit, the reversal at
17:59 on a Friday. That is the part we engineer first.