opsofts / laravel-mtn-momo
A minimal MTN MoMo (Collections / Request to Pay) integration for Laravel, built directly on Laravel's own HTTP client.
Requires
- php: ^8.2
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
README
A minimal MTN MoMo Collections (Request to Pay) integration for Laravel, built directly on Laravel's own HTTP client (Illuminate\Support\Facades\Http). No external HTTP SDK dependency — just illuminate/support/illuminate/http, which ship with every Laravel app and are maintained on the same cadence as the framework itself.
This package deliberately covers only the "generic MoMo prompt" flow — pushing a native approve/decline prompt to a customer's phone via the Collections API. Disbursements (payouts) and Remittances are not implemented. A dial-in USSD short-code portal (customer dials your own business code) is a separate, much bigger undertaking involving telecom licensing and regulatory compliance in each country — not something an API package can give you, and out of scope here by design.
What it does
- Provision an API User + API Key — one-time setup helpers (
provisionApiUser(),createApiKey()) - Request a payment —
requestToPay(), pushes MTN's native approval prompt to the payer's phone - Check a transaction's status —
getTransactionStatus(), the authoritative source of truth for whether a payment succeeded
Bearer token fetching and caching (1-hour tokens, refreshed automatically) is handled for you once you've provisioned credentials.
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
- An MTN MoMo Collections subscription for the country you're operating in, from momodeveloper.mtn.com
Install
composer require opsofts/laravel-mtn-momo php artisan vendor:publish --tag=mtn-momo-config
One-time credential setup
Unlike the other gateway packages, MTN MoMo's long-lived credentials (api_user/api_key) aren't just copied from a dashboard — you generate them yourself, once, using this package:
use Opsofts\LaravelMtnMomo\Facades\MtnMomo; // Step 1: provision an API User (only needs your subscription key, already in .env) $result = MtnMomo::provisionApiUser(callbackHost: 'yourapp.com'); // $result['apiUserId'] -- save this as MOMO_API_USER // Step 2: generate that user's API Key $result = MtnMomo::createApiKey($apiUserId); // $result['apiKey'] -- save this as MOMO_API_KEY
Run this once (e.g. in php artisan tinker, or a one-off Artisan command), save the two values to .env, and you never need to call these two methods again.
.env
MOMO_COLLECTION_SUBSCRIPTION_KEY=...
MOMO_API_USER=...
MOMO_API_KEY=...
MOMO_TARGET_ENVIRONMENT=sandbox
MOMO_ENVIRONMENT=sandbox
Usage
use Opsofts\LaravelMtnMomo\Facades\MtnMomo; use Illuminate\Support\Str; // Request a payment - pushes an approval prompt to the customer's phone $request = MtnMomo::requestToPay([ 'amount' => '5000', 'currency' => 'NGN', // or "GHS" for Ghana, etc. 'externalId' => $order->reference, 'payer' => [ 'partyIdType' => 'MSISDN', 'partyId' => '2348012345678', // customer's MoMo-registered phone number ], 'payerMessage' => 'Payment for order #1234', 'payeeNote' => 'Internal note', ], referenceId: (string) Str::uuid()); // $request['referenceId'] -- store this against the order; you need it to check status // Poll for the result (or wait for your configured webhook, then confirm with this call) $status = MtnMomo::getTransactionStatus($request['referenceId']); if ($status['status'] === 'SUCCESSFUL') { // mark the order/payment as paid } elseif ($status['status'] === 'FAILED') { // handle failure } // otherwise still PENDING -- keep polling (e.g. from a queued job) or wait for the webhook
What this package deliberately does not do
No Disbursements, no Remittances, no card/wallet storage. It only ever talks to MTN's Collections API and hands back what MTN returns — persisting references/statuses on your own models is the calling application's responsibility, same as with any SDK.
Testing
Http::fake() works exactly as it does for any Laravel HTTP client usage — fake your configured base URL for both the token endpoint and API calls, rather than hitting the real API. No custom test helpers are provided; standard Laravel HTTP testing covers this package fully.
License
MIT.