iankumu/mpesa
iankumu/mpesa is a Laravel package that streamlines Safaricom M-Pesa integration. It supports STK Push, C2B, B2C, transaction status, reversals, and callback handling through a clean, configurable API, making it easier to build payments, collections, and disbursement flows.
Architecture fit. The package is a Laravel-native wrapper around Safaricom M-Pesa's STK Push (Lipa na M-Pesa), C2B, B2C, and transaction-status endpoints. Architecturally it is a standard Composer dependency that registers a service provider, ships a publishable config file and migrations, and exposes an expressive facade-style API with events and logging hooks. That is the shape of a peripheral integration package, not a core platform component — it belongs at the edge of the domain, behind our own payment abstraction, not woven through business logic. Its fit is strongest in Laravel monoliths or modular monoliths where M-Pesa is a payment rail inside a broader checkout, disbursement, or collections flow; it is a weaker fit in multi-tenant or polyglot architectures where a non-Laravel service also needs to initiate payments, because the package is not a standalone SDK and cannot be consumed outside PHP/Laravel.
Integration feasibility. High on the mechanical side, moderate on the domain side. Mechanically: install via Composer, publish config and migrations, set sandbox credentials, expose callback routes, and the package handles auth token acquisition and callback plumbing. The friction is not installation; it is that M-Pesa is an asynchronous, externally-owned state machine. Callbacks must be publicly reachable over HTTPS, excluded from CSRF, and processed idempotently — Safaricom retries and can deliver duplicates. Feasibility therefore hinges on whether the team treats callback handling as a durable, observable workflow (queued job, persisted raw payload, state transition, reconciliation job) rather than a controller that fires and forgets. The package gives us the transport; it does not give us the ledger.
Technical risk. Moderate, and the dominant risks are vendor and domain risks rather than code risks.
Key questions.
Stack fit. Direct fit for Laravel applications, with Laravel 13 explicitly supported as of 2.3.0. The practical stack prerequisites to confirm before committing: PHP version constraints, queue driver (callback processing should be queued, not inline), a publicly routable HTTPS endpoint per environment, and a secrets store for credentials. The package should sit behind an internal PaymentGateway-style interface that our domain code calls, so the package is an implementation detail rather than an import scattered across the codebase.
Migration path. Treat it as an additive integration, not a migration — there is no legacy M-Pesa code to displace unless we already have hand-rolled calls, in which case the path is strangle-and-replace:
Compatibility. Verify the package's composer.json platform constraints against our Laravel and PHP versions, then verify adjacent dependencies — Composer resolution failures usually come from packages pinning Laravel or PHP, not from the target package. Confirm whether 2.3.0 supports both the current and target Laravel majors; if it supports both, adopt it ahead of the framework upgrade to avoid compounding change. Confirm the package's CI matrix covers the Laravel version we actually run. Confirm callback route registration does not collide with existing routes and that CSRF exemptions are scoped narrowly.
Sequencing. Sandbox validation first (cheap, kills the idea early if the API surface does not fit our use cases) → adapter interface and durability layer second (our code, our control) → framework-version alignment third → pilot fourth → production rollout last. Do not sequence the package adoption in parallel with a Laravel major upgrade unless the package supports both versions; otherwise a failure has two possible causes and diagnosis cost doubles. Keep the package pinned to an exact minor version in composer.lock and upgrade deliberately.
Maintenance. Low ongoing engineering effort if the integration is thin, but this is not a "set and forget" dependency. Budget for: tracking Safaricom API and credential changes, tracking the package's Laravel-major cadence as an early-warning signal, quarterly review of open issues and release recency, and an owner for the adapter layer. Establish a fork contingency now — confirm we can vendor the package and patch it, and record that decision. Given a 55-star package on a payments path, the realistic posture is "adopt with a named owner and a documented exit," not "adopt and move on."
Support. The support burden is customer-facing and time-sensitive: failed payments generate tickets fast. Required before launch: a runbook for stuck or ambiguous transactions, a way for support staff to look up a transaction by M-Pesa receipt or our reference, defined customer messaging for timeout versus failure versus pending, and an escalation path for callback outages. Confirm whether the maintainer offers any support channel and set expectations internally that upstream support is best-effort.
Scaling. The package itself is unlikely to be the bottleneck; Safaricom's rate limits, latency, and availability will be. Scaling considerations: queue-based callback processing so a callback burst does not degrade request handling, exponential backoff with idempotency on retries, bounded concurrency against Safaricom endpoints, and a transaction table that will grow monotonically and needs partitioning or archival planning. STK Push timeouts should be modeled as an indeterminate state resolved by status query, not as a failure — that distinction drives both UX and reconciliation accuracy. Load-test the reconciliation job, which is the component most likely to become a nightly bottleneck.
Failure modes. Plan for each explicitly:
Ramp-up. Low for a Laravel-fluent engineer on the mechanics — the package is intentionally expressive and the setup is a publish-and-configure exercise. The real ramp-up is domain literacy: M-Pesa receipt semantics, STK timeout behavior, C2B validation versus confirmation, B2C result codes, and the difference between "no response" and "failed." Budget roughly a sprint for a mid-level engineer to reach confident delivery including the durability and reconciliation layer, and much less if we already have payments experience in-house. The adapter layer pays for itself here too: it gives new engineers one interface to read instead of the package's internals.
How can I help you explore Laravel packages today?