MAA INVEST Pay Docs
Getting started

Sandbox & testing

Test credentials, test phone numbers, test webhooks and how the sandbox differs from live.

The sandbox is a full copy of the checkout flow that never touches M-Pesa, a paybill or any wallet. It uses the same endpoint, the same signing and the same field validation as live, so a request that works here works in production.

Test phone numbers#

Enter one of these on the test checkout page (or pre-fill it with customer_phone). Any other valid Safaricom number succeeds.

NumberResultStatusReason you get
0708000001Payment succeedspaidReceipt like TEST1A2B3C
0708000002Customer cancels the promptfailedRequest cancelled by user.
0708000003Insufficient M-Pesa balancefailedThe balance is insufficient for the transaction.
0708000004Customer enters the wrong PINfailedThe initiator information is invalid.
0708000005Customer never answers (times out)failedDS timeout, the customer could not be reached.

The simulated customer answers after about 3 seconds (the "never answers" number fails after 20 seconds). After a failure you can try another number on the same checkout (up to 5 attempts, 5 seconds apart).

What is different from live#

  • Test receipts start with TEST. Webhook and status responses include "sandbox": true; live ones do not.
  • Sandbox webhooks are not retried automatically. Use Resend webhook on the Merchant Gateway page. (Live retries for about 2 days.)
  • The wait between prompts is 5 seconds (live: 15 seconds).
  • Test checkouts are kept for 30 days and capped at 500 per day. Clear test data deletes them all.
  • Just like live, the same invoice_id with the same amount returns the existing checkout (an already paid one shows as paid). Use a new invoice_id for each test, or clear your test data.
  • The webhook still has to reach a public HTTPS URL with a valid certificate.

Testing WHMCS and WooCommerce#

The plugins can be downloaded from the sandbox panel. Paste the test Merchant ID and secret into their settings and leave the Checkout URL as it is; the plugin then runs in test mode. Switch to your live ID and secret when you go live.

Stuck on an integration? Open a ticket from the Support page in your account and include your invoice_id, the time, and the HTTP status you returned.