Skip to content

M-Pesa (Kenya)

M-Pesa is the world’s most successful mobile money service. Operated by Safaricom in Kenya, it processes roughly 50% of Kenya’s GDP in transaction value annually. For many Kenyans, M-Pesa is money.


PropertyValue
OperatorSafaricom PLC
TypeMobile Money (STK Push)
CountryKenya
CurrencyKES
FlowUSSD Push (STK Push)
Programmatic RefundsYes (Reversal API)
Sandbox AvailableYes (solid)
SettlementT+1 business days

Zirzir uses STK Push (Lipa Na M-Pesa Online) under the hood:

  1. Your app calls zirzir.charge() with the customer’s phone number
  2. Safaricom sends an STK Push prompt to the customer’s phone
  3. Customer enters their M-Pesa PIN to approve
  4. Safaricom sends a callback to your callbackUrl
  5. You verify and fulfill the order

checkoutUrl is null for M-Pesa — the customer interacts entirely via their phone.


Register at developer.safaricom.co.ke to get sandbox credentials immediately. Production credentials require Go Live approval (1-2 weeks).

CredentialDescription
consumerKeyYour app’s consumer key
consumerSecretYour app’s consumer secret
businessShortCodeYour PayBill or Till number
passKeyLipa Na M-Pesa passkey (provided after approval)

const zirzir = new Zirzir({
provider: 'mpesa',
credentials: {
consumerKey: process.env.MPESA_CONSUMER_KEY!,
consumerSecret: process.env.MPESA_CONSUMER_SECRET!,
businessShortCode: process.env.MPESA_SHORT_CODE!,
passKey: process.env.MPESA_PASS_KEY!,
environment: 'production' // or 'sandbox'
}
})

phone is required and must be in 2547XXXXXXXX format (country code, no +).

const tx = await zirzir.charge({
amount: 1000,
currency: 'KES',
email: 'customer@example.com',
phone: '254712345678',
txRef: 'order_001',
callbackUrl: 'https://yourapp.com/webhooks/zirzir',
})
// tx.checkoutUrl is null — customer prompted via STK Push
console.log(tx.status) // 'pending'

M-Pesa OAuth tokens expire every 3600 seconds. The Zirzir SDK handles refresh automatically.

STK Push callbacks can arrive before the initial API response completes. Always verify via API, not just callbacks.

Must be 2547XXXXXXXX. No + prefix, no leading 0. Zirzir normalizes common formats but we recommend passing the canonical format.

Minimum STK Push transaction is 1 KES.

Your callback endpoint must respond within 5 seconds or Safaricom marks it as failed.

Production and sandbox use different base URLs and different credentials — easy to mix up.


Merchant Fees (Lipa Na M-Pesa — Buy Goods)

Section titled “Merchant Fees (Lipa Na M-Pesa — Buy Goods)”
Transaction Amount (KES)Fee
1 - 1000 KES
101 - 5007 KES
501 - 1,00013 KES
1,001 - 1,50023 KES
1,501 - 2,50033 KES
2,501 - 3,50053 KES
3,501 - 5,00057 KES
5,001 - 7,50078 KES
7,501 - 10,00090 KES
10,001 - 15,000100 KES
15,001 - 20,000105 KES

PayBill fees vary by industry and are negotiated with Safaricom.


M-Pesa supports reversals via the Reversal API:

const refund = await zirzir.refund('order_001')

Reversals are processed quickly but are subject to Safaricom approval.


  • Cycle: T+1 business days (reliable)
  • Currency: KES
  • Method: Bank transfer to registered business account
  • Settlement days: Monday - Friday
  • Reporting: Via M-Pesa portal or API

M-Pesa’s settlement is more reliable and consistent than most African payment gateways.


  • callbackUrl must be publicly accessible (no localhost)
  • Firewall may block Safaricom’s callback IP ranges
  • HTTPS required in production
  • URL must respond within 5 seconds

Tokens expire every 3600 seconds. Ensure you’re refreshing proactively.

Customer dismissed the USSD prompt. The transaction is permanently failed — you cannot re-push the same request.