Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Mpesa Laravel Package

itsmurumba/laravel-mpesa

A Laravel package for integrating Safaricom M-Pesa into your applications. It handles STK Push, C2B and B2C payments, transaction status queries, and callback processing behind a clean, configurable API so you can accept and send mobile money in minutes.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Unchanged by this release. The package remains a thin, config-driven wrapper around Safaricom's Daraja API, which fits naturally if the consuming product is a Laravel monolith or modular monolith where M-Pesa is one payment rail among several. What it provides is the HTTP/client layer (STK Push, C2B, B2C, status/reversal-style calls), not a payment orchestration layer. The STK Push flow is inherently asynchronous — request, user prompt, callback — so the architectural value depends on how well the surrounding system handles queues, callbacks, persistence, and reconciliation. Treat the package as an adapter, not a domain: define an internal gateway contract (e.g., MpesaGateway interface, our own DTOs and exception types) and let this package sit behind it. That gives us a clean swap point if the upstream goes stale (MIT license makes a fork viable) and prevents Safaricom-specific shapes from leaking into order, billing, or ledger code. It also keeps multi-tenant concerns — per-tenant shortcodes, passkeys, callback URLs — in our config layer rather than scattered through call sites. The v2.0.0-beta.1 release does not change this picture, and the interface boundary is now more valuable than it was when the last assessment was written.

Integration feasibility. Still low friction mechanically: Composer install, Laravel auto-discovery, publishable config, facade. Two things change with this release. First, there is now a 2.x line in beta, so "install the package" is no longer an unambiguous instruction — we must choose a line and pin it. Second, the only change documented in v2.0.0-beta.1 is a CI fix enabling installable beta pre-releases from develop (PR #19). A major version bump whose changelog contains no breaking-change description is itself the finding: either the breaking changes exist and are undocumented, or the major bump was cut for packaging/CI reasons without a migration note. Both readings require us to read the diff between v1.0.9-dev.10 and v2.0.0-beta.1 before recommending either line. The feasibility work outside the package is unchanged: publicly reachable HTTPS callbacks (tunnel for local dev), per-app sandbox credentials from the Safaricom portal, and production shortcode/passkey provisioning outside our control. Verify by reading source, not the README — timeout handling, error-code mapping, retry/backoff, whether request payloads (containing phone numbers) are logged, and whether callback verification and replay protection are provided or left to us. My prior remains that callback security and idempotency are our responsibility.

Technical risk. Three clusters, one of which has materially worsened:

  • Release hygiene and API drift. This is the new lead risk. We have observed v1.0.9-dev.4 → v1.0.9-dev.10 → v2.0.0-beta.1 in a short window: repeated dev-tagged releases, no published release timestamps in the metadata we consume, and a major version bump with a one-line changelog. That pattern is consistent with a single maintainer iterating directly on develop and tagging as they go. Daraja itself also evolves — new endpoints appear, older behaviors get deprecated, sandbox/production drift is common — and a release process this opaque makes it hard to tell whether a given upstream change is a Daraja-tracking fix or a packaging refactor. The concrete danger: we cannot answer "what breaks if we upgrade" from the changelog, so every major is a full source diff for us.
  • Adoption and bus factor. 20 stars is a weak social signal; one maintainer, no vendor SLA, limited peer review of the code paths that move money. Unchanged, and the release pattern above reinforces rather than softens it. The "Laravel 5 through 12" support claim is a breadth red flag — verify a real CI matrix rather than permissive Composer constraints, and specifically confirm whether 2.x narrowed the supported range.
  • Correctness under failure. Payments packages tend to be optimistic: happy path handled well, duplicate callbacks, late callbacks, user-ignored prompts, and unmatched C2B payments left to the integrator. These are the failure modes that generate finance tickets, and no release note here suggests they've been addressed.

Key questions to answer before committing.

  1. What actually breaks in 2.0.0? Is there a migration guide, an UPGRADE.md, or a diff summary beyond the CI fix? If the bump is cosmetic (CI/packaging only), we should say so and treat 1.x and 2.x as behaviorally equivalent; if it isn't, we need the breaking-change list before touching it.
  2. Does 2.x change the supported Laravel/PHP matrix? A major is the natural moment to drop Laravel 5–8 and old PHP. That would be good for us (less compat surface, more plausible CI) but changes our pinning story.
  3. Is 1.x still maintained — security fixes, Daraja drift patches — or has develop moved to 2.x exclusively? If 1.x is frozen, the beta becomes the only forward path, which changes the risk calculus.
  4. Does it cover the endpoint set we actually need (STK Push, C2B confirmation/validation, B2C, Transaction Status, Reversal, Account Balance), or only STK Push?
  5. Is there a CI matrix proving the claimed Laravel/PHP range, and does it run tests against Daraja mocks?
  6. Does it expose idempotency keys, or must we key on CheckoutRequestID/MerchantRequestID ourselves?
  7. How does it surface Daraja error responses — typed exceptions, raw arrays, or swallowed?
  8. Does it support runtime/tenant-specific configuration, or only a single global shortcode?
  9. Does it register routes (a callback controller) that could collide with ours?
  10. What is its logging posture with respect to MSISDNs and credentials?

Integration Approach

Stack fit. Best fit is a Laravel 10/11/12 service on PHP 8.1+. Because the package spans many Laravel majors it is almost certainly a thin, largely version-agnostic wrapper — good for us (little to break on framework upgrade), and a mild signal of test depth. Pin an exact version and an explicit stability. The v2.0.0-beta.1 release exists specifically to make beta pre-releases installable from develop, which means an overly permissive minimum-stability or a dev-develop constraint in our root composer.json can silently pull a pre-release into a payments path. Lock to a stable release, keep minimum-stability: stable (using prefer-stable rather than dev where a dev constraint is unavoidable), and let Dependabot/Renovate surface updates rather than floating.

Migration path. Three scenarios now, not two:

  • Replacing hand-rolled cURL/Guzzle code. Introduce the internal gateway interface first, implement it with the package, and run shadow mode — dual-send to the legacy client and the new one in sandbox/staging, diff the responses, then cut over. Cheap because the surface is HTTP calls, not state.
  • Replacing another M-Pesa package. Write an adapter mapping the incumbent's public methods onto the new one so call sites don't move. Migrate endpoints one at a time — Transaction Status first (read-only, low blast radius), then STK Push, then B2C — rather than big-bang.
  • Upgrading 1.x → 2.x within the package. Treat as its own mini-migration. Diff the public API, run our adapter test suite against both, and only then bump. Do not combine this with a framework upgrade in the same change set.

Compatibility checks. Confirm illuminate/support and guzzlehttp/guzzle constraints resolve against our lockfile for the line we pick; confirm PHP extension requirements; confirm the facade alias and config key names don't collide; confirm any auto-registered routes are namespaced and can be disabled if we route callbacks ourselves (we probably should, so we control verification and idempotency). For the beta specifically, verify it installs cleanly under our stability settings before it appears in a Renovate PR, so a bot doesn't surprise us.

Sequencing.

  1. Release triage (new, do first). Diff v1.0.9-dev.10...v2.0.0-beta.1. Determine whether the major is cosmetic or breaking. Ask upstream for a migration note and for a statement on 1.x maintenance.
  2. Sandbox spike with Safaricom test credentials; prove STK Push → callback end-to-end through a tunnel, on the pinned line.
  3. Define the internal gateway contract and DTOs; implement the package as one adapter.
  4. Build what the package won't give us: callback idempotency (unique constraint on CheckoutRequestID), persistence of request/response/raw callback, a timeout sweeper that reconciles via Transaction Status, and a reconciliation job for unmatched C2B payments.
  5. Security review: callback source verification, secret handling via a secrets manager rather than .env on disk, PII redaction in logs.
  6. Staging against a production shortcode in a controlled, low-value window.
  7. Canary a small percentage of traffic with a rollback switch (feature flag to the legacy adapter).
  8. Full rollout with a written runbook and a fork-on-standby plan.

Operational Impact

Maintenance. Modest if the package stays thin — most Laravel upgrades are a constraint bump and a smoke test. The real exposure is upstream process, and this release is evidence for it: a major version published with a one-line changelog and no published timestamp means our upgrade triage cost is dominated by source diffing, not by Composer. Budget one to two engineer-days per Laravel major for validation, plus an explicit allowance for release triage on each upstream major, plus discretionary time for patches we forward-port. Under MIT we can fork and vendor into a private repository at any time, and the case for doing so strengthens if 1.x stops receiving Daraja-drift fixes. Assign a named owner for the dependency — and that owner should be subscribed to upstream releases so a 2.0.0 stable or a 1.x security tag doesn't surface first as a Renovate PR.

Support. No vendor, no SLA, no paid tier — support is GitHub issues with low community volume, which in practice means we are the support organization. Escalation for API-side incidents goes to Safaricom Daraja support, and that path is slow; our on-call runbook must assume we can diagnose from our own logs and reconcile manually. Document Daraja escalation contacts and expected response windows as launch readiness. Do not depend on upstream responsiveness for a production incident: if the bug is in the wrapper, we patch our fork.

Scaling. Unchanged — the package is an HTTP client and doesn't constrain throughput; our queue workers, database, and Daraja's per-shortcode rate limits do. Put all outbound calls behind queued jobs with bounded concurrency, exponential backoff, and a circuit breaker so a Daraja slowdown doesn't exhaust PHP-FPM workers; set explicit connect/read timeouts (STK Push waits on a human); make the callback endpoint stateless and horizontally scalable, persisting the raw payload before any business logic runs. Watch for connection reuse if Guzzle clients are constructed per call.

Failure modes to design for.

  • Daraja 5xx / timeouts — retry with idempotency keys; never blindly retry B2C.
  • Callback never arrives — user ignores or dismisses the prompt; a sweeper must time out pending transactions and query Transaction Status rather than leaving orders in limbo.
  • Duplicate callbacks — Safaricom retries; enforce uniqueness on CheckoutRequestID and make processing idempotent.
  • Callback arrives before we've committed the request record — a real race; use upsert semantics keyed on the checkout ID.
  • Credential rotation and IP allowlist changes — model secrets as rotatable config, not constants.
  • Sandbox/production drift — sandbox success is necessary but not sufficient; run a small production smoke transaction per release.
  • Silent failures — if the package swallows or flattens exceptions, alerting goes dark; wrap calls and assert on typed outcomes.
  • Accidental pre-release adoption — a dev-develop constraint or permissive stability flag pulling 2.0.0-beta.x into production; guard in CI with a lockfile diff check and an explicit deny on -dev/-beta/-alpha tags for this package.

Ramp-up. A mid-level Laravel engineer is productive with the package in half a day. The domain is the long pole: shortcodes, passkeys, STK versus C2B versus B2C semantics, settlement and reconciliation, and Safaricom's error taxonomy take one to two weeks to internalize. Plan onboarding around a sandbox walkthrough, a credential-provisioning procedure owned by one person, a written runbook for stuck transactions, and a pre-production review that explicitly confirms callback verification and idempotency are in place. Add one item for the current release: a short written note in the repo on which package line we're on, why, and what the upgrade trigger is.

Recommendation. Adopt behind an internal gateway interface, pin an exact stable version, keep minimum-stability: stable, and assume ownership. The package earns its place as a convenience wrapper that saves a few days of Daraja plumbing; it does not earn a place as a load-bearing dependency without our own idempotency, reconciliation, and observability wrapped around it. Do not adopt v2.0.0-beta.1. Stay on the pinned 1.x line while it receives Daraja-drift fixes, complete the source diff and get an upstream answer on breaking changes and 1.x support, then move to 2.x once it reaches a stable tag — with the adapter's test suite as the gate, not the changelog. Given the low adoption signal and the release-cadence pattern observed here, maintain a fork-ready posture and re-evaluate at each upstream major and each of our own Laravel major upgrades.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel