Installation
composer require roazagba/apimtnmomo
Publish the config file:
php artisan vendor:publish --provider="Roazagba\Apimtnmomo\ApimtnmomoServiceProvider"
Configuration
Update .env with MTN MoMo credentials:
MTN_MOMO_API_KEY=your_api_key
MTN_MOMO_SECRET_KEY=your_secret_key
MTN_MOMO_BASE_URL=https://sandboxapi.momodeveloper.mtn.com/v1/
First Use Case: Initiate a Transaction
use Roazagba\Apimtnmomo\Facades\Apimtnmomo;
$response = Apimtnmomo::pay(
amount: 1000, // Amount in cedi (₵)
phone: '233551234567',
reference: 'ORDER-12345',
currency: 'GHS',
callbackUrl: 'https://yourdomain.com/momo/callback'
);
config/apimtnmomo.php (adjust sandbox/live mode, timeouts, and logging).Roazagba\Apimtnmomo\Facades\Apimtnmomo (primary entry point for all API calls).Payments (Collect Money)
$response = Apimtnmomo::pay([
'amount' => 5000,
'phone' => '233541234567',
'reference' => 'INV-'.Str::uuid(),
'currency' => 'GHS',
'callbackUrl' => route('momo.callback'),
]);
Route::post('/momo/callback', [MomoCallbackController::class, 'handle']);
Use Apimtnmomo::verifyCallback() to validate the signature.Disbursements (Send Money)
$response = Apimtnmomo::disburse([
'amount' => 2000,
'phone' => '233541234567',
'reference' => 'REF-'.Str::uuid(),
'currency' => 'GHS',
]);
$status = Apimtnmomo::checkTransactionStatus('REF-12345');
$response = Apimtnmomo::airtime([
'phone' => '233541234567',
'amount' => 5,
'network' => 'MTN', // or 'VODAFONE', 'AIRTEL', 'TIGO'
]);
$bulkResponse = Apimtnmomo::bulkPay([
'transactions' => [
['amount' => 1000, 'phone' => '233541234567', 'reference' => 'REF1'],
['amount' => 1500, 'phone' => '233541234568', 'reference' => 'REF2'],
],
'currency' => 'GHS',
]);
Environment Separation
Use .env to toggle between sandbox and live mode:
MTN_MOMO_ENV=sandbox # or 'live'
Logging
Enable debug logging in config/apimtnmomo.php:
'debug' => env('MTN_MOMO_DEBUG', false),
Logs are stored in storage/logs/momo.log.
Retry Logic
Implement exponential backoff for failed requests (e.g., using retry package):
use Illuminate\Support\Facades\Retry;
Retry::times(3)->attempt(function () {
$response = Apimtnmomo::pay([...]);
});
Database Storage Store transaction references in your DB for reconciliation:
// Example model
class MomoTransaction extends Model {
protected $fillable = ['reference', 'amount', 'phone', 'status', 'raw_response'];
}
Webhook Validation Always verify MTN’s callback signature:
public function handle(Request $request) {
if (!Apimtnmomo::verifyCallback($request)) {
abort(403, 'Invalid callback');
}
// Process the transaction
}
Sandbox vs. Live Mode
sandbox to live before production.if (config('apimtnmomo.env') === 'live') {
// Enable production-specific logic
}
Phone Number Format
233541234567 for Ghana).$phone = '233'.ltrim($rawPhone, '0');
Callback Timeouts
max_execution_time or use queue jobs:
Queue::push(MomoCallbackJob::class, $request->all());
Rate Limits
Route::middleware(['throttle:10,1'])->group(function () {
Route::post('/momo/webhook', [...]);
});
Currency Mismatch
currency: 'USD' when MTN only supports GHS for Ghana.currency: 'GHS' for Ghanaian endpoints.Enable Debug Mode
MTN_MOMO_DEBUG=true
Check storage/logs/momo.log for raw API responses.
Test with Sandbox First
08090909090).Common Error Codes
| Code | Meaning | Solution |
|---|---|---|
| 400 | Invalid request | Validate input data |
| 401 | Authentication failed | Check api_key/secret_key |
| 403 | Insufficient funds | Notify user |
| 404 | Resource not found | Verify transaction reference |
| 500 | Server error | Retry or contact MTN support |
Custom Responses Override default responses by extending the facade:
class CustomApimtnmomo extends \Roazagba\Apimtnmomo\Facades\Apimtnmomo {
public static function pay(array $data) {
$response = parent::pay($data);
// Transform response (e.g., add custom fields)
return $response->merge(['custom_field' => 'value']);
}
}
Add New Endpoints
The package follows a simple Apimtnmomo::method() pattern. To add a custom endpoint:
// In a service class
public function customEndpoint(array $data) {
return Http::withHeaders([
'Authorization' => 'Bearer '.config('apimtnmomo.api_key'),
'Content-Type' => 'application/json',
])->post(config('apimtnmomo.base_url').'custom', $data);
}
Event Dispatching
Trigger events for transactions (e.g., MomoTransactionProcessed):
event(new MomoTransactionProcessed($response));
Testing Use Laravel’s HTTP tests to mock MTN responses:
$response = Http::fake([
'sandboxapi.momodeveloper.mtn
How can I help you explore Laravel packages today?