MTN MoMo Payment Gateway
Accept mobile money payments from MTN Mobile Money (MoMo) users across Africa. Integrates with the MTN MoMo Collections API to create “Request to Pay” transactions, enabling customers to approve payments directly from their phones.
Request to Pay
Initiate payment requests that customers approve via USSD prompt on their MTN phone.
Sandbox Provisioning
One-click generation of sandbox API credentials directly from the admin panel.
Real-time Polling
Automatic payment status polling with progress bar. Webhook support for production environments.
Encrypted Credentials
All API credentials are encrypted at rest using Laravel’s Crypt facade before database storage.
Use Cases
E-Commerce in Africa
You run an online shop on Larapen targeting customers in MTN-served countries (Uganda, Ghana, Cameroon, Ivory Coast, Benin, Congo, Liberia). Customers pay for orders using their MTN MoMo wallet: no credit card required.
- Install the Shop and MoMo add-ons.
- Configure MTN MoMo credentials in the admin panel.
- Customers select “MTN Mobile Money” at checkout, enter their phone number, and approve the payment on their phone.
Digital Product Sales
You sell digital products (e-books, software, templates) to mobile-first users who prefer mobile money over card payments.
- Pair with the Shop add-on’s digital product delivery.
- After MoMo payment confirmation, the download link is automatically provided.
Service Bookings
Any payable entity implementing the Payable contract can use MoMo for payment,
making it extensible beyond the Shop add-on.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
- The Shop add-on (required dependency)
- An MTN MoMo developer account at momodeveloper.mtn.com
- A subscription to the Collections product on the MTN MoMo developer portal
Installation
Step 1: Place the Add-on
Copy or symlink the momo folder into your Larapen "extensions/addons" directory:
Step 2: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate MTN MoMo Payment Gateway.
Step 3: Run Migrations
This creates the momo_transactions table for tracking payment references and statuses.
Step 4: Set Permissions
The add-on registers 2 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 5: Configure
Navigate to Admin → MTN MoMo → Settings and configure your API credentials. For sandbox testing, use the Provisioning feature to auto-generate credentials. See Configuration.
Configuration
All settings are managed in Admin → MTN MoMo → Settings
(stored in the settings table, group momo).
Configuration defaults are defined in config/momo.php.
| Setting | Description | Default |
|---|---|---|
momo_subscription_key |
Ocp-Apim-Subscription-Key from the MTN MoMo developer portal. Stored encrypted. | (empty) |
momo_api_user_id |
UUID v4 of the API User. Generated during provisioning or obtained from the MTN Partner Portal. Stored encrypted. | (empty) |
momo_api_key |
API Key generated for the API User. Stored encrypted. | (empty) |
momo_environment |
Deployment environment: sandbox or production. |
sandbox |
momo_target_environment |
Target environment header value. sandbox for testing, or a country-specific code for production (see Payment Options). |
sandbox |
momo_currency |
Currency code for payments. Must be EUR in sandbox. Country-specific in production. |
EUR |
momo_callback_host |
Your domain for receiving webhook callbacks (production only). Must support HTTPS. | (empty) |
Environment Variables
MTN Developer Portal Setup
- Create an account at momodeveloper.mtn.com.
- Subscribe to the Collections product.
- Navigate to your Profile page and copy the Primary Key (or Secondary Key): this is your Subscription Key.
- For sandbox: use the admin panel’s Provisioning feature to auto-generate the API User ID and API Key.
- For production: obtain credentials from the MTN MoMo Partner Portal and enter them in API Credentials.
Admin: Settings
The settings page (MTN MoMo → Settings) is organized into four sections:
Sandbox Provisioning
This section automates the sandbox credential generation workflow:
- Enter your Subscription Key (from the MTN developer portal Profile page).
- Optionally enter a Callback Host (defaults to
webhook.sitefor sandbox). - Click Generate API Credentials.
- The system automatically:
- Generates a UUID v4 for the API User ID
- Creates the API User via
POST /v1_0/apiuser - Generates the API Key via
POST /v1_0/apiuser/{id}/apikey - Encrypts and stores all three credentials in the database
- Sets the environment to
sandboxand currency toEUR
- The generated credentials are displayed once in a success alert with copy buttons. Save them externally: the API Key is not retrievable again from MTN.
Test Key
The Test Key button performs a diagnostic API call (creates a temporary API User) to verify the Subscription Key is valid. Returns HTTP 201 on success, with request/response details for debugging.
API Credentials
Three password-style fields with toggle-visibility buttons:
- Subscription Key: Ocp-Apim-Subscription-Key from the MTN portal.
- API User ID: UUID v4 created during API User provisioning.
- API Key: Generated from the API User.
Crypt::encryptString()
before being stored in the database. They are decrypted only when needed for API calls.
Leave a field blank to keep the current stored value.
Payment Options
- Environment:
sandboxorproduction. Determines which MTN API base URL is used. - Target Environment: Sent as the
X-Target-Environmentheader. Usesandboxfor testing, or one of the country-specific values for production:
| Country | Target Environment Value | Currency |
|---|---|---|
| Uganda | mtnuganda |
UGX |
| Ghana | mtnghana |
GHS |
| Cameroon | mtncameroon |
XAF |
| Ivory Coast | mtnivorycoast |
XOF |
| Benin | mtnbenin |
XOF |
| Congo | mtncongo |
XAF |
| Liberia | mtnliberia |
LRD |
| Sandbox | sandbox |
EUR |
- Currency: Must match the target environment (see table above). Must be
EURin sandbox. - Callback Host: Your domain for receiving webhook callbacks (production only). HTTPS is required.
Integration Info
The bottom of the settings page displays:
- The Callback URL (
https://your-domain.com/momo/callback) for copying into the MTN portal. - A reference list of all valid target environment values by country.
Payment Flow
The complete payment journey from checkout to confirmation:
1. Checkout Selection
The customer selects “MTN Mobile Money” as their payment method on the checkout page.
A phone number input field is displayed (the momo::payment-form Blade view).
2. Phone Number Entry
The customer enters their MTN MoMo phone number (e.g., +233XXXXXXXXX).
The number is normalized (spaces, dashes, and parentheses removed) before submission.
3. Order Creation
The form is submitted via AJAX. The Shop add-on creates the Order record,
then calls MomoGateway::createPaymentIntent().
4. Request to Pay
The gateway:
- Obtains an OAuth2 Bearer token from MTN (
POST /collection/token/). - Generates a UUID reference ID for the transaction.
- Sends a “Request to Pay” to MTN (
POST /collection/v1_0/requesttopay). - Stores a
MomoTransactionrecord withPENDINGstatus. - Returns the reference ID to the frontend.
5. USSD Approval
The customer receives a USSD prompt on their MTN phone and enters their PIN to approve the payment.
6. Frontend Polling
While the customer approves, the frontend JavaScript polls GET /momo/status/{referenceId}
every 5 seconds (configurable), displaying a progress bar. See Polling Mechanism.
7. Confirmation
When polling returns succeeded, the frontend redirects to GET /momo/confirm?reference_id={id}.
The MomoController::confirm() method verifies the final status, marks the order as paid, and redirects to the order success page.
Webhooks
In production, MTN MoMo sends callbacks to your server when a payment status changes.
The callback URL is https://your-domain.com/momo/callback.
/momo/callback
Description
Receives MTN MoMo payment status callbacks. CSRF protection is disabled for this endpoint. Also accepts POST requests for flexibility.
Processing
The WebhookController delegates to MomoGateway::handleWebhook(), which:
- Extracts the
reference_idfrom theX-Reference-Idheader or payload. - Finds the matching
MomoTransactionrecord. - Updates the transaction status and financial transaction ID.
- If
SUCCESSFUL: marks the order as paid and creates a ShopTransactionrecord. - If
FAILED: marks the payment as failed and creates a failedTransactionrecord.
Response (JSON)
Refunds
refund() method returns a failure with an explanatory message.
Refunds must be processed manually through the MTN MoMo Partner Portal
or via the separate Disbursements API.
Shop Add-on Integration
The MoMo add-on integrates with the Shop add-on through the PaymentGatewayInterface contract:
Gateway Discovery
The MomoGateway class is tagged as payment.gateways in the service container.
The Shop add-on discovers it automatically alongside other payment gateways (e.g., Stripe).
Payable Interface
The Shop’s Order model implements App\Contracts\Payable, which provides:
getPayableAmount(): the order totalgetPayableIdentifier(): the order numbergetPaymentSuccessUrl(): redirect URL after successful paymentgetPaymentCancelUrl(): redirect URL after failed/cancelled paymentmarkAsPaid(): updates order status to COMPLETED and payment status to PAID
Transaction Recording
Two types of transaction records are created:
- MomoTransaction: tracks the MTN API reference ID, status, and payer phone number.
- Shop Transaction: created by the webhook/confirm handler for the order’s payment history.
Polymorphic Support
The MomoTransaction model uses a morphTo relationship (payable_type / payable_id),
allowing any model that implements Payable to use MoMo for payments: not just Shop orders.
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Clear Caches
Step 4: Verify
Visit MTN MoMo → Settings and use Test Key to confirm your credentials still work.
Troubleshooting
“Failed to obtain MTN MoMo access token”
Check that:
- All three API credentials (Subscription Key, API User ID, API Key) are configured correctly.
- The Subscription Key has not expired or been revoked.
- Your server can reach
sandbox.momodeveloper.mtn.com(sandbox) orproxy.momoapi.mtn.com(production). - The API User ID and API Key match: they must be from the same provisioning session.
Provisioning fails: HTTP 401
- Verify you have subscribed to the Collections product on momodeveloper.mtn.com.
- Copy the Primary Key from your Profile page, not from the product page.
- Ensure the subscription is still active (not expired).
- Check for leading/trailing whitespace when pasting the key.
Provisioning fails: HTTP 409
An API User with the generated UUID already exists. Simply click Generate API Credentials again: a new UUID is generated each time.
“A phone number is required for MTN MoMo payments”
The customer did not enter their phone number in the checkout form.
Ensure the MoMo payment form view (momo::payment-form) is being loaded correctly
and the phone input is visible when MoMo is selected as the payment method.
Payment stays “pending” indefinitely
- In sandbox: use one of the test phone numbers (see Sandbox Testing).
- In production: the customer may not have approved the USSD prompt on their phone.
- After the polling timeout (~5 minutes), the user is shown a timeout message.
- The payment can still be confirmed later via webhook if the customer approves after the timeout.
“Payment was declined or cancelled”
- The customer declined the USSD prompt or entered an incorrect PIN.
- The customer’s MoMo account has insufficient funds.
- The MTN reason code is appended to the error message for debugging (e.g.,
PAYER_NOT_FOUND,NOT_ENOUGH_FUNDS).
“Request was blocked” (non-JSON response)
A WAF (Web Application Firewall) or proxy blocked the API request. This can happen when the MTN API gateway rejects the request at the infrastructure level. Check server logs for the full response body and verify your Subscription Key and callback host configuration.
Webhook not working in production
- Ensure
momo_callback_hostis set to your domain (withouthttps://prefix). - The callback host in your settings must match the
providerCallbackHostset during API User creation. - Your server must accept PUT and POST requests at
/momo/callbackwithout CSRF verification. - Verify HTTPS is working correctly on your domain.
Currency mismatch errors
- Sandbox requires
EUR: any other currency will fail. - Production requires the country-specific currency matching the target environment (see Payment Options).
Sandbox Test Phone Numbers
Use these phone numbers in sandbox mode to test different payment outcomes:
| Phone Number | Result |
|---|---|
46733123453 |
Successful payment |
46733123454 |
Payment declined |
46733123455 |
Payment declined |
46733123456 |
Payment declined |