yamoon/mtn-momo-api

Laravel client for the MTN Mobile Money (MoMo) Open API — Collection, Disbursement and Remittance products.

Maintainers

Package info

github.com/Yamoon224/MTN-MOMO-API

pkg:composer/yamoon/mtn-momo-api

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-07-30 12:59 UTC

This package is auto-updated.

Last update: 2026-07-30 13:01:02 UTC


README

Unofficial Laravel client for the MTN Mobile Money (MoMo) Open API, covering the three sandbox products:

  • Collection — request money from a customer (requesttopay, requesttowithdraw)
  • Disbursement — pay money out to a customer (transfer, refund)
  • Remittance — cross-border transfer to a customer (transfer)

Each product is provisioned independently on the MoMo Developer portal (its own subscription key, API user and API key), but is reachable from a single facade.

This is not an official MTN package. MTN occasionally changes scopes/fields per market — always sanity-check payloads against your own subscription's Postman collection on momodeveloper.mtn.com before going to production.

Installation

composer require yamoon/mtn-momo-api

Laravel's package auto-discovery registers the service provider and the Momo facade automatically.

Publish the config file:

php artisan vendor:publish --tag=momo-config

Configuration

Copy the relevant variables from .env.example into your app's .env. You get subscription keys from the "Products" tab on the MoMo Developer portal; API user/key are generated per product (see Sandbox provisioning below).

MOMO_ENVIRONMENT=sandbox

MOMO_COLLECTION_SUBSCRIPTION_KEY=
MOMO_COLLECTION_API_USER=
MOMO_COLLECTION_API_KEY=
MOMO_COLLECTION_TARGET_ENVIRONMENT=sandbox

MOMO_DISBURSEMENT_SUBSCRIPTION_KEY=
MOMO_DISBURSEMENT_API_USER=
MOMO_DISBURSEMENT_API_KEY=
MOMO_DISBURSEMENT_TARGET_ENVIRONMENT=sandbox

MOMO_REMITTANCE_SUBSCRIPTION_KEY=
MOMO_REMITTANCE_API_USER=
MOMO_REMITTANCE_API_KEY=
MOMO_REMITTANCE_TARGET_ENVIRONMENT=sandbox

Only configure the products you actually use — each is lazily instantiated on first access.

Sandbox provisioning

Before you have API user/key credentials, you only need a subscription key (copied from the portal) to provision them:

use Mtn\MomoApi\Facades\Momo;

$apiUser = Momo::collection()->createApiUser('https://example.com/momo/callback');
$apiKey = Momo::collection()->createApiKey($apiUser);

// Store these as MOMO_COLLECTION_API_USER / MOMO_COLLECTION_API_KEY

Repeat per product (Momo::disbursement(), Momo::remittance()) — each has its own sandbox user/key even though the provisioning endpoint shape is identical.

Usage

Collection — request a payment

use Mtn\MomoApi\DTO\Party;
use Mtn\MomoApi\Facades\Momo;

$referenceId = Momo::collection()->requestToPay(
    amount: '500',
    payer: Party::msisdn('46733123454'),
    externalId: 'order-1234',
    payerMessage: 'Invoice #1234',
    payeeNote: 'Thanks for your order',
);

// Poll for the result (or listen on your callback URL for the async notification)
$status = Momo::collection()->requestToPayStatus($referenceId);
// ['amount' => '500', 'currency' => 'EUR', 'status' => 'SUCCESSFUL', ...]

Disbursement — pay a customer

use Mtn\MomoApi\DTO\Party;
use Mtn\MomoApi\Facades\Momo;

$referenceId = Momo::disbursement()->transfer(
    amount: '1000',
    payee: Party::msisdn('46733123454'),
    externalId: 'payout-5678',
);

$status = Momo::disbursement()->transferStatus($referenceId);

Remittance — cross-border transfer

use Mtn\MomoApi\DTO\Party;
use Mtn\MomoApi\Facades\Momo;

$referenceId = Momo::remittance()->transfer(
    amount: '2500',
    payee: Party::msisdn('46733123454'),
    externalId: 'remit-91011',
);

$status = Momo::remittance()->transferStatus($referenceId);

Common to all three products

Momo::collection()->getBalance();
// ['availableBalance' => '100', 'currency' => 'EUR']

Momo::collection()->isAccountHolderActive(\Mtn\MomoApi\Enums\PartyIdType::MSISDN, '46733123454');
// true|false

Momo::collection()->basicUserInfo('46733123454');
// requires the "basic userinfo" scope on your subscription

Party also has ::email() and ::partyCode() constructors for the other partyIdType values MTN supports.

Without the facade

use Mtn\MomoApi\Momo;

$momo = app(Momo::class);
$momo->collection()->requestToPay(/* ... */);

Error handling

Failed HTTP calls throw Mtn\MomoApi\Exceptions\MomoRequestException, which exposes MTN's error payload:

use Mtn\MomoApi\Exceptions\MomoRequestException;

try {
    Momo::collection()->requestToPay('500', Party::msisdn('46733123454'), 'order-1');
} catch (MomoRequestException $e) {
    $e->momoErrorCode();    // e.g. "PAYER_NOT_FOUND", "NOT_ENOUGH_FUNDS"
    $e->momoErrorMessage(); // MTN's human-readable message
    $e->response();         // the underlying Illuminate\Http\Client\Response
}

Failures to obtain an OAuth2 token throw Mtn\MomoApi\Exceptions\MomoAuthenticationException.

Token caching

Access tokens are cached automatically (per product, via your app's default cache store) until shortly before they expire. Configure the store and safety buffer in config/momo.php:

'cache' => [
    'store' => env('MOMO_CACHE_STORE'), // null = default app store
    'prefix' => env('MOMO_CACHE_PREFIX', 'mtn_momo_api'),
    'expiry_buffer' => env('MOMO_CACHE_EXPIRY_BUFFER', 60), // seconds
],

Testing your integration

The package uses Laravel's HTTP client under the hood, so you can fake it in your own tests:

use Illuminate\Support\Facades\Http;

Http::fake([
    'https://sandbox.momodeveloper.mtn.com/collection/token/' => Http::response([
        'access_token' => 'fake-token',
        'expires_in' => 3600,
    ]),
    'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttopay' => Http::response('', 202),
]);

Running the package's own test suite

composer install
composer test

License

MIT — see LICENSE.md.