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: Upload the Add-on
In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.
Step 2: Activate the Add-on
Find MTN MoMo Payment Gateway in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.
Step 3: Configure
Navigate to Admin → MTN MoMo → Settings and enter your API credentials. For sandbox testing, use the Provisioning feature to auto-generate credentials. See Configuration.
Purchase Code (License Key)
MTN MoMo Payment Gateway is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate MTN MoMo Payment Gateway in Admin panel → Add-ons.
Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.
| Platform / Marketplace | How you get the purchase code | Where to find it again |
|---|---|---|
| bedigit.com Store In-site purchase (Shop) |
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. | My Account → My Licenses on bedigit.com |
| Gumroad | Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. | The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com |
| Envato Market CodeCanyon |
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. | Envato account → Downloads → License certificate & purchase code |
1. bedigit.com Store (in-site purchase)
- As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
- It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
- The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
- The matching invoice is under My Account → My Orders.
2. Gumroad
- A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
- The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
- When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
- Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.
3. Envato Market (CodeCanyon)
- Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
- Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
- An Envato purchase code looks like
12345678-90ab-cdef-1234-567890abcdef(8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one. - Official Envato article: Where Is My Purchase Code?
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.
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
There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.
Method 1: Admin Panel Upload (Recommended)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- Go to Admin panel → System Update (
/admin/update) to apply any pending database migrations.
Method 2: Manual File Replacement
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
php artisan migrate
Pending migrations run once, so the command is safe to repeat.
Step 3: Clear Caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
Step 4: Verify
Visit MTN MoMo → Settings and use Test Key to confirm your credentials still work.
Uninstallation
Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find MTN MoMo Payment Gateway and click Deactivate.
- Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
- Its database tables and all the data they hold are kept, and its files stay under
extensions/addons/momo/. Nothing is deleted. - The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
- Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.
Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.
Removing
Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:
- Deactivate MTN MoMo Payment Gateway (see Uninstallation).
- Click Remove on its card and confirm the prompt.
The admin panel then, in one pass:
- runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
- revokes the permissions declared in its
addon.json; - rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/momo/,public/vendor/momo/andstorage/app/public/addons/momo/; - deletes the add-on directory
extensions/addons/momo/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/momo/. In that last case, give that user write permission on the directory and on its parent, then try again.
Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.
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 |
MTN MoMo Payment Gateway v1.0.0: Part of the Larapen CMS platform.
© BeDigit. All rights reserved.