Before you start
You need a credential set from AptaPay:app_id, key_id, api_key and
signing_secret. You will be issued a test set and a live set — start
with test.
1
Store your credentials
Put them in your secret manager, not in source control.The signing secret and the outbound secret are different values. The
first signs your requests to us; the second verifies our webhooks to you.
.env
2
Add the signing helper
Every request carries four headers, with an HMAC-SHA256 over a ten-field
canonical string. Copy the reference implementation from
Authentication — it needs
nothing but Node’s built-in
crypto.There is no login call and no token endpoint. You sign per request.3
Confirm auth works
Call a cheap read first, so a failure here is unambiguously about signing
and not about money.A
check-auth.ts
200 means your signing is correct. A 401 means it is not — work
through 401 triage, starting with your clock.4
Check the corridor you need
A corridor is country + currency + method. Confirm yours is supported and
learn its limits rather than hardcoding them.See Capabilities and corridors.
5
Charge a customer
Amounts are integer minor units. UGX, RWF, XAF and XOF are
zero-decimal, so You get
500000 UGX is five hundred thousand shillings.charge.ts
201 with a gateway reference and, almost always, status
pending. The customer sees their provider’s own prompt — a mobile money
PIN request, a redirect, or an OTP.6
Wait for the real answer
A charge does not settle synchronously. Treat the webhook as the
outcome, or poll if you must:Do not treat
pending as failure and do not retry it with a new
idempotency key.7
Receive and verify the webhook
Register your
callback_url_test, then verify every delivery before
trusting it — see Webhooks for the verifier.Correlate on data.metadata, which echoes what you sent at charge time.
Do not parse our reference; it carries none of your structure.What to get right before going live
Branch on the error flags, not the status code
Branch on the error flags, not the status code
Every error carries exactly one of
terminal, retriable or ambiguous.
An ambiguous outcome must never be reversed — poll it to a terminal
status instead. See Errors.Reuse the idempotency key on retries
Reuse the idempotency key on retries
Never mint a new key to get past a failure. Same operation, same key —
that is what stops one charge becoming two. See
Idempotency.
Verify amounts on settlement
Verify amounts on settlement
Check
observed_amount_minor and observed_currency against what you
expected, not just status.Deduplicate on event_id
Deduplicate on event_id
Delivery is at-least-once and unordered. Track processed
event_ids and
use sequence to discard stale arrivals.Next
Collections
Money in, end to end.
Payouts
Money out, and the float model.
Authentication
The signing scheme in full.
API reference
Every endpoint.