Download the PHP package siberfx/mpesa-payment without Composer
On this page you can find all versions of the php package siberfx/mpesa-payment. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download siberfx/mpesa-payment
More information about siberfx/mpesa-payment
Files in siberfx/mpesa-payment
Package mpesa-payment
Short Description Modern M-Pesa payment gateway for PHP 8.4+ β Safaricom Daraja (Kenya) and Vodacom M-Pesa OpenAPI (Tanzania, DRC, Lesotho). Works standalone or with Laravel 12/13.
License MIT
Informations about the package mpesa-payment
M-Pesa Payment Gateway for PHP & Laravel
A modern, fully typed M-Pesa integration for PHP 8.4 / 8.5, usable standalone or with Laravel 12 / 13.
| Market | API | Driver |
|---|---|---|
| π°πͺ Kenya (Safaricom) | Daraja | daraja |
| πΉπΏ Tanzania (Vodacom) | M-Pesa OpenAPI | openapi |
| π¨π© DR Congo (Vodacom) | M-Pesa OpenAPI | openapi |
| π±πΈ Lesotho (Vodacom) | M-Pesa OpenAPI | openapi |
Contents
- Features
- Installation
- Standalone usage
- Laravel usage
- API reference (Daraja)
- API reference (Vodacom OpenAPI)
- Complete examples
- Pitfalls & best practices
- Error handling
- Testing
Features
Daraja (Kenya)
- M-Pesa Express (STK push) and STK status query β Pay Bill and Buy Goods
- C2B URL registration (v2) and sandbox simulation
- B2C payouts (v3) β business, salary and promotion payments
- B2B payments (pay bill, buy goods, transfers) and B2B Express Checkout (USSD push)
- Account balance, transaction status and reversals
- Dynamic QR code generation
- KRA tax remittance
- M-Pesa Ratiba standing orders (recurring payments)
Vodacom OpenAPI (Tanzania, DRC, Lesotho)
- C2B single stage, B2C, B2B, reversal and transaction status
- Automatic session-key creation, RSA encryption and renewal
Engineering
- Multiple named accounts across markets in one app
- Access tokens / session keys cached (any PSR-16 cache; Laravel cache in Laravel)
- Safe retries: money-moving requests are only retried if the connection was never established, so a payment is never sent twice
- Automatic token refresh on expiry
- Security credentials generated from the Safaricom certificate on the fly
- Phone number normalisation (
0712β¦,+254 712β¦,712β¦β254712β¦) - Typed callback objects (
StkCallback,C2BTransaction,ResultCallback) including balance parsing - Typed exceptions carrying Daraja error codes and request IDs
- PSR-3 logging with credentials redacted
- Laravel: auto-discovered provider and facade, callback routes that dispatch events, C2B validation hook, callback token + IP allow-list,
mpesa:register-urlscommand
Requirements
- PHP 8.4 or 8.5 with
ext-opensslandext-json - Laravel 12 or 13 (optional)
Installation
Standalone usage
Multiple accounts and markets
Pass any PSR-16 cache (Redis, APCu, filesystemβ¦) so access tokens survive between requests. Without one, an in-memory cache is used for the lifetime of the object.
Laravel usage
The service provider and Mpesa facade are auto-discovered. Publish the config:
Minimal .env for Kenya:
Or inject Siberfx\MpesaPayment\Mpesa anywhere the container resolves dependencies.
Callback routes and events
The package registers these POST routes (prefix configurable via MPESA_ROUTES_PREFIX, default payments/callbacks):
| Route | Event |
|---|---|
/payments/callbacks/stk |
StkCallbackReceived |
/payments/callbacks/c2b/validation |
C2BValidationReceived |
/payments/callbacks/c2b/confirmation |
C2BConfirmationReceived |
/payments/callbacks/result/{type?} |
ResultReceived |
/payments/callbacks/timeout/{type?} |
TimeoutReceived |
/payments/callbacks/b2b-checkout |
B2BCheckoutCallbackReceived |
/payments/callbacks/ratiba |
StandingOrderCallbackReceived |
Any callback URL you leave empty in the config is filled in automatically from these routes, using MPESA_CALLBACK_BASE_URL (or APP_URL), with ?account=<name>&token=<MPESA_CALLBACK_TOKEN> appended. Events live in Siberfx\MpesaPayment\Laravel\Events:
Note: Safaricom rejects C2B URLs containing words such as
mpesa,safaricom,exe,cmd,sqlorquery, which is why the default prefix ispayments/callbacks.
Validating C2B payments
Then register your URLs with Safaricom:
Securing callbacks
MPESA_CALLBACK_TOKENβ callback requests without the matchingtokenquery parameter get a403.MPESA_VERIFY_CALLBACK_IP=trueβ only addresses inmpesa.security.allowed_ips(Safaricom's published callback IPs by default) are accepted. Behind a proxy or load balancer, configure Laravel's trusted proxies so the real client IP is seen.
API reference (Daraja)
All methods return an ApiResponse (accepted(), failed(), description(), get(), toArray(), array access, plus helpers such as checkoutRequestId() and conversationId()).
STK push (M-Pesa Express)
C2B
B2C
B2B and B2B Express Checkout
Balance, status, reversal
The final results arrive on your result URL; parse them with ResultCallback::fromArray($payload). For balances, $result->balances() returns structured rows.
Dynamic QR
KRA tax remittance
M-Pesa Ratiba (standing orders)
API reference (Vodacom OpenAPI)
These APIs are synchronous β the response already contains the final result (output_ResponseCode INS-0 on success).
Known markets (vodacomTZN, vodacomDRC, vodacomLES) get their country and currency automatically. For other markets on the same platform, set country, currency and country_code explicitly. For DRC, set currency to CDF if you settle in Congolese francs.
Callback parsing without Laravel
Complete examples
1. Laravel checkout with STK push (end to end)
Migration β keep the identifiers M-Pesa gives you, you will need them to match callbacks:
Controller β start the payment:
Listener β the callback is the source of truth:
Fallback job β covers callbacks that never arrive:
2. Paybill (C2B) payments with invoice validation
3. B2C payouts with traceable IDs
4. Plain PHP (no framework) with a file cache
5. Tanzania checkout (Vodacom OpenAPI)
6. Daily balance check (scheduled)
Pitfalls & best practices
These are the mistakes that most often cost money or hours in M-Pesa integrations. Read them before going live.
"Accepted" is not "paid"
$response->accepted() on an STK push, B2C, B2B, balance or reversal request only means M-Pesa queued the request. The outcome arrives later on your callback. Mark an order as paid only when the callback (or an STK query) reports result code 0. Also check that the amount in the callback matches what you expected.
Callbacks are unreliable: design for it
- They can arrive late, twice, or not at all. Make every handler idempotent. Key on
CheckoutRequestID(STK),TransID(C2B) orOriginatorConversationID(B2C/B2B), and ignore records that are no longer pending. - Always schedule a fallback (
stk()->query()orbalance()->transactionStatus()) for payments that are still pending after 1β2 minutes. - Querying an STK push too early returns an error meaning "still being processed". Treat that as "retry later", not as a failure.
- Answer callbacks quickly. Do the heavy work in queued listeners (
ShouldQueue), otherwise M-Pesa may time out and retry.
Never blindly resend a payment
If a B2C/B2B/reversal request throws ConnectionException (for example a timeout), the money may already have moved. The package deliberately does not retry in that case. Before resending, check with transactionStatus(originatorConversationId: ...) using the ID you passed in. That is why passing your own originatorConversationId is strongly recommended.
Callback URLs
- They must be public HTTPS URLs.
localhostand private IPs never receive callbacks. In the sandbox, use a tunnel such as ngrok or Expose and setMPESA_CALLBACK_BASE_URLto the tunnel URL. - C2B URLs must not contain
mpesa,safaricom,exe,exec,cmd,sqlorqueryin any casing. Keep the defaultpayments/callbacksprefix, or choose one that avoids these words. - In production, C2B URL registration is effectively a one-time setup per shortcode. Get the URLs right first. Changing them later usually involves Safaricom support.
- The validation URL is only called if external validation is enabled on your shortcode, which you must request from Safaricom. Otherwise only the confirmation URL is hit.
- Keep the callback routes on the
apimiddleware group. If you move them toweb, Laravel's request-forgery (CSRF) protection will reject M-Pesa's POSTs. - The callback token travels in the URL and can end up in access logs. Treat it as a secret you can rotate: change
MPESA_CALLBACK_TOKENand re-register the URLs. - When enabling
MPESA_VERIFY_CALLBACK_IPbehind a load balancer or Cloudflare, configure Laravel's trusted proxies. Otherwise every request appears to come from the proxy and is rejected.
Credentials and environments
- Sandbox and production use different certificates. A security credential encrypted with the wrong one fails with result code
2001("initiator information is invalid"). The same happens with a wrong or expired initiator password. - Never commit consumer keys, passkeys, initiator passwords or certificates. Load them from environment variables or a secrets manager.
- In standalone mode, always pass a persistent PSR-16 cache. Without one, every PHP request fetches a new access token, which is slow and can hit Daraja rate limits.
- The STK password includes a timestamp in Nairobi time. The package generates it, but a server clock that is far off (no NTP) still causes "invalid timestamp" errors.
Data rules the API enforces
- Kenyan amounts must be whole numbers β₯ 1. The package throws
ValidationExceptionfor10.50instead of silently rounding. Round deliberately in your own code. - The STK
referenceis limited to 12 characters (validated). The description is cut to 13 characters. - In C2B v2 callbacks the customer's
MSISDNis masked or hashed. Do not use it to identify customers. Use the bill reference or your own account number instead. - Customer names in callbacks may be empty or partial. Never make them required.
Vodacom OpenAPI specifics
- C2B single-stage calls are synchronous and wait for the customer to enter their PIN. Raise the HTTP timeout (for example
'http' => ['timeout' => 120]), or the request will time out while the customer is still paying. - A newly created session key can take a short while to become active. The package renews the session once on
401. If the first call after deployment still fails, retry after a few seconds. - Currency is fixed per market (TZS, LSL, USD for DRC). If your DRC contract settles in CDF, set
currencyexplicitly.
Operational
C2BValidation::using()stores a static callback. Register it in a service provider'sboot()method, not per request (this matters under Octane).- Reconcile daily against your M-Pesa statement. Callbacks are a notification channel, not an accounting ledger.
- Log with a dedicated channel (
MPESA_LOG_CHANNEL). Sensitive fields are redacted, but phone numbers and amounts are not. Apply your own retention policy.
Error handling
| Exception | When |
|---|---|
ConfigurationException |
Missing credentials, callback URLs or unknown accounts |
ValidationException |
Invalid phone numbers, amounts or arguments |
AuthenticationException |
Token or session could not be obtained |
RequestException |
The API returned an HTTP error (statusCode, errorCode, requestId, body) |
ConnectionException |
The API could not be reached |
All extend Siberfx\MpesaPayment\Exceptions\MpesaException.
Testing
To test your own code, pass a Guzzle client with a MockHandler to Mpesa::make(..., http: $client), or bind a pre-configured Mpesa instance in the Laravel container.
Security
If you discover a security issue, please email [email protected] instead of opening a public issue.
License
The MIT License (MIT). See LICENSE.md.
All versions of mpesa-payment with dependencies
ext-json Version *
ext-openssl Version *
guzzlehttp/guzzle Version ^7.9
psr/log Version ^3.0
psr/simple-cache Version ^3.0