opsofts/laravel-mtn-momo

A minimal MTN MoMo (Collections / Request to Pay) integration for Laravel, built directly on Laravel's own HTTP client.

Maintainers

Package info

github.com/tomibady/laravel-mtn-momo

pkg:composer/opsofts/laravel-mtn-momo

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-06 15:57 UTC

This package is auto-updated.

Last update: 2026-08-06 16:00:48 UTC


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 paymentrequestToPay(), pushes MTN's native approval prompt to the payer's phone
  • Check a transaction's statusgetTransactionStatus(), 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.