This checklist covers the critical items to validate before opening your integration to end customers. Go through it in order — each section builds on the previous one.Documentation Index
Fetch the complete documentation index at: https://developers.ligdicash.com/llms.txt
Use this file to discover all available pages before exploring further.
Security
API keys are not exposed on the client side
API keys are not exposed on the client side
The Apikey and Auth Token must not appear in browser JavaScript, in a mobile app, or in a public Git repository.
- Server-side environment variables only (
process.env,.envnot committed) -
.gitignoreincludes the.envfile - No key in application logs
- No key in API responses returned to the frontend
The callback re-verifies status via the confirm endpoint
The callback re-verifies status via the confirm endpoint
Never trust the payload received in the callback — it can be forged. Always call
/confirm with the token stored at creation.- The callback handler calls
/checkout-invoice/confirmbefore any business action - The token used for
confirmis the one stored at creation (not the callbacktoken) - A malformed payload or one with an unknown
transaction_idis silently ignored - See Callback security
The callback_url is publicly reachable
The callback_url is publicly reachable
LigdiCash must be able to reach your callback endpoint from the internet.
- URL reachable from an external IP (not
localhostor a private IP) - HTTPS recommended (HTTP accepted by LigdiCash but discouraged in production)
- No firewall rule blocking LigdiCash IPs
- If whitelisting is required: contact LigdiCash support for the IPs to allow (max 3 IPs per project)
Transaction identifiers are non-guessable
Transaction identifiers are non-guessable
A sequential
transaction_id (1, 2, 3, etc.) lets an attacker enumerate your transactions.- Use a UUID v4 or a prefixed ID with a random suffix (
txn_+ timestamp + random) - See transaction_id pattern
Technical
Hosted payin flow
Hosted payin flow
-
customeris empty ("") in every hosted payin example -
cancel_urlpoints to the cancellation page,return_urlto the success page (not swapped) - The payment URL opens in the same tab, a new tab, or a popup — never in an iframe
- The frontend verifies the real status via the backend after returning to
return_url(the redirect is not proof of payment)
Direct payin flow (if applicable)
Direct payin flow (if applicable)
-
external_idandotpare empty ("") in requests where they are not used - The authentication mode (USSD OTP / SMS / Approval) is correctly documented in the UX for each operator
- For SMS OTP mode: the initial request is submitted with
otp: "", then resubmitted with the received OTP - For Approval mode (Moov): the callback is the only confirmation — no insufficient polling
Callback handling
Callback handling
- The handler is idempotent (two calls with the same
transaction_idonly execute the business logic once) - Both callback formats are handled (
application/jsonandapplication/x-www-form-urlencoded) - See Callback idempotency and Parse custom_data
Database and traceability
Database and traceability
- The transaction is created in the database before the LigdiCash call
- The LigdiCash
tokenis stored as soon as the invoice is created - Inbound and outbound logs (LigdiCash requests + received callbacks) are kept
- See Recommended architecture
Polling fallback
Polling fallback
- A periodic job checks transactions stuck in
pendingfor more than a few minutes - Polling stops after a reasonable delay (e.g. 24h) — transactions never automatically flip to
notcompleted - Polling uses the
tokenstored at creation, not a token from the callback
Payout (if applicable)
Payout to LigdiCash wallet
Payout to LigdiCash wallet
- The recipient has a LigdiCash account (required for
/withdrawal/create) - The semantics of
top_up_walletare correctly implemented:1= funds in the wallet,0= automatic transfer to the linked mobile money account
Direct payout to mobile money
Direct payout to mobile money
- The phone number is in
22670XXXXXXXformat (no+, no spaces) - The
/straight/payoutendpoint is used (not/withdrawal/create) for recipients without a LigdiCash account
Business and operations
Merchant account and API project
Merchant account and API project
- The LigdiCash merchant account is active and verified (KYC completed)
- The API project is in production mode (not a test account)
- The operators you need are enabled on the API project (check in the dashboard)
- Visa enabled separately if you need it (specific contract)
Error handling in the UX
Error handling in the UX
- Every LigdiCash error code is mapped to a user-readable message
- The user is clearly informed if their payment fails, with an invitation to retry
- See Common errors
Communication with LigdiCash
Communication with LigdiCash
- Your
callback_urlhas been tested under real conditions - You have a LigdiCash integration Microsoft Teams group for production emergencies
- If IP whitelisting is required: your server IPs have been sent to LigdiCash technical support
Pre-production testing
Before going live, run at least one end-to-end test for each flow you’re integrating.| Scenario | What to test |
|---|---|
| Successful payment | Status becomes completed, order is confirmed |
| Customer cancels the payment | cancel_url is reached, order stays pending then expires |
| Callback received twice | Business logic runs only once |
Transaction still pending after 10 min | Fallback polling updates it correctly |
| Invalid API key | The error is caught and logged, the user sees a generic message |
Related pages
- Recommended architecture — backend structure
- Callback security — re-verification pattern
- transaction_id pattern — identify transactions
- Error handling — codes and strategies
