Testing
There is no separate sandbox environment to point at. Your account is the sandbox until go-live is approved, on the production base URL with your normal API key. That means the code you test with is the code you ship: no host swap, no key swap, nothing left to get wrong on launch day.
What is restricted before approval
While your project's go-live status is anything other than APPROVED:
| Restriction | Detail |
|---|---|
| Recipients | Collections and payouts only reach counterparties on your Test Recipients whitelist |
| Whitelist size | Up to 3 phone numbers, plus a separate list of test bank accounts |
| Daily cap | TZS 100,000 per day, counted separately for collections and disbursements |
Everything else behaves exactly as it will in production: real channels, real webhooks, real fees, real money. These are small live transactions, not simulations, so use your own numbers and expect to be charged.
Registering test recipients
Add them in the dashboard under Settings → Test Recipients, or over the API:
curl https://core-prod.malipopay.co.tz/api/v1/project/test-numbers \
-H "apiToken: YOUR_API_KEY" \
-H "Authorization: Bearer YOUR_DASHBOARD_TOKEN"
That returns { numbers, count, max }, where max is 3. To add a phone recipient, POST the same path:
{ "type": "PHONE", "msisdn": "255712345678", "label": "My test line" }
msisdn accepts 0744..., 255744... or +255744... and is normalised for you. Bank recipients go on the same endpoint with type: "BANK" plus bankCode, bankName and accountNumber. PUT /api/v1/project/test-numbers/{id} edits one and DELETE on the same path removes it.
These routes need a dashboard session in addition to the API key and are owner-only, so in practice the dashboard is the easier route. Adding a fourth recipient, or one that is already listed, returns HTTP 409.
A payment to a number that is not on the whitelist is rejected with HTTP 400 and a message naming the project. It is not a silent failure, and it is not a channel problem.
Checking your current limits
curl https://core-prod.malipopay.co.tz/api/v1/payment/limits \
-H "apiToken: YOUR_API_KEY"
This returns the limit actually being enforced for each direction along with today's usage, which is the fastest way to tell whether you are hitting a cap or a genuine failure.
What to test before going live
- One successful collection on each channel you intend to use, checked at your webhook rather than only at the API response.
- One failed collection (let the USSD prompt time out) so you exercise the
payment.failedpath. - One disbursement all the way through: create, approve from your whitelisted IP, and confirm
payout.confirmedarrives. - A replayed webhook, to confirm your handler is idempotent.
- A repeated collection with the same
reference, to confirm you get the existing payment back rather than a second charge.