mohamed-sinani / mobile-money-php
PHP SDK for mobile money payments, disbursements, and hosted checkout sessions.
Package info
github.com/mohamed-sinani/payment-gateway-sdk
pkg:composer/mohamed-sinani/mobile-money-php
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^9.0|^10.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-17 18:29:01 UTC
README
A PHP SDK for mobile money payments, disbursements, and hosted checkout sessions.
Requirements
- PHP 8.1+
ext-curlext-json
Installation
composer require mohamed-sinani/payment-gateway-sdk
Quick Start
use PaymentGateway\PaymentGateway; $gateway = new PaymentGateway('your_api_key', [ 'base_url' => 'https://api.example.com', 'api_version' => '2026-01-25', ]); // Create a mobile money payment $payment = $gateway->mobileMoney(5000, '0754123456') ->customer('John', 'Doe', 'john@example.com') ->send(); echo $payment->reference(); // Payment reference echo $payment->status(); // "pending"
Load your API key from an environment variable rather than committing it to source:
$gateway = new PaymentGateway(getenv('API_KEY'));
Payments
Mobile Money
Collect payments via Airtel Money, M-Pesa, Mixx by Yas, or Halotel.
$payment = $gateway->mobileMoney(5000, '0754123456') ->customer('John', 'Doe', 'john@email.com') ->webhookUrl('https://yoursite.com/webhooks/payment') ->metadata(['order_id' => 'ORD-12345']) ->idempotencyKey('order-12345-attempt-1') ->send();
Phone numbers are normalized automatically — 0754..., 255754..., and +255754... all work.
Get Payment Status
$payment = $gateway->getPayment('6a490816-799b-4fc9-b9b6-2ec67c54e17e'); echo $payment->status(); // "pending", "completed", "failed", "voided", "expired"
List Payments
$result = $gateway->listPayments(['limit' => 20, 'offset' => 0]); foreach ($result['items'] as $payment) { echo $payment->reference() . ': ' . $payment->status() . "\n"; }
Search Payments
$payment = $gateway->searchPayments('6a490816-799b-4fc9-b9b6-2ec67c54e17e');
Get Account Balance
$balance = $gateway->getBalance(); echo $balance->available(); // Available amount echo $balance->currency(); // "TZS"
Disbursements
Mobile Money Payout
$payout = $gateway->payout(5000, 'mobile') ->mobileRecipient('0754123456', 'John Doe') ->narration('Salary payment') ->metadata(['employee_id' => 'EMP-001']) ->send();
Bank Transfer Payout
$payout = $gateway->payout(100000, 'bank') ->bankRecipient('CRDB', '0200000000', 'Jane Smith') ->narration('Invoice payment INV-2026-001') ->send();
Get Payout Status
$payout = $gateway->getPayout('667c9279-846f-4001-b046-fdecab204f4f'); echo $payout->status(); // "pending", "completed", "failed", "reversed"
Calculate Payout Fee
$fee = $gateway->calculatePayoutFee(5000); echo $fee['fee_amount']; // Transaction fee echo $fee['total_amount']; // Amount + fee
Payment Sessions
Create hosted checkout pages for your customers.
Basic Session
$session = $gateway->session(50000) ->customer('John Doe', '+255712345678', 'john@example.com') ->description('Order #12345') ->redirectUrl('https://yoursite.com/success') ->webhookUrl('https://yoursite.com/webhooks/payment') ->send(); echo $session->checkoutUrl(); // Hosted checkout URL echo $session->paymentLinkUrl(); // Shareable payment link
Custom Amount (Donation)
$session = $gateway->session() ->customAmount(1000, 500000) ->description('Donation') ->send();
Rich Checkout with Line Items
$session = $gateway->session(150000) ->lineItems([ ['name' => 'Widget', 'quantity' => 2, 'unit_price' => 75000], ]) ->display([ 'show_line_items' => true, 'line_items_style' => 'cards', 'button_text' => 'Buy Now', ]) ->send();
List and Cancel Sessions
$result = $gateway->listSessions(['limit' => 10, 'status' => 'pending']); $gateway->cancelSession('sess_abc123def456');
Webhooks
Verify Webhook Signatures
Webhook::capture() reads the raw request body, verifies the HMAC-SHA256 signature, rejects stale timestamps, and returns a parsed event.
use PaymentGateway\Webhook; $event = Webhook::capture(); if ($event->isPaymentCompleted()) { $reference = $event->reference(); // Mark order as paid } http_response_code(200);
The signing key is read from the PAYMENT_GATEWAY_WEBHOOK_SECRET environment variable by default. To pass it explicitly:
$event = Webhook::capture(['signing_key' => $config['webhook_secret']]);
Event Types
| Method | Event |
|---|---|
isPaymentCompleted() |
payment.completed |
isPaymentFailed() |
payment.failed |
isPaymentVoided() |
payment.voided |
isPaymentExpired() |
payment.expired |
isPayoutCompleted() |
payout.completed |
isPayoutFailed() |
payout.failed |
isPayoutReversed() |
payout.reversed |
Important: Don't read php://input yourself and re-serialize the JSON — that breaks the signature. Webhook::capture() handles the raw body for you.
Error Handling
The SDK throws PaymentGatewayException on any non-2xx response.
use PaymentGateway\PaymentGatewayException; try { $payment = $gateway->mobileMoney(5000, '0754123456') ->send(); } catch (PaymentGatewayException $e) { echo $e->getMessage(); // Error message echo $e->errorCode(); // e.g. "validation_error", "unauthorized" echo $e->statusCode(); // HTTP status code print_r($e->responseBody()); // Full error response }
Common Error Codes
| Code | Description |
|---|---|
unauthorized |
Invalid or missing API key |
insufficient_scope |
API key lacks required scope |
validation_error |
One or more fields invalid |
not_found |
Resource doesn't exist |
payment_failed |
Payment processing error |
rate_limit_exceeded |
Too many requests (60/min) |
Idempotency
The SDK supports idempotent requests to prevent duplicate transactions:
$payment = $gateway->mobileMoney(5000, '0754123456') ->idempotencyKey('order-12345-attempt-1') ->send();
- Keys must be 30 characters or fewer
- Keys are valid for 24 hours
- Same key + same body = returns cached response
- Same key + different body = returns error
Configuration
$gateway = new PaymentGateway('your_api_key', [ 'base_url' => 'https://api.example.com', // API base URL 'api_version' => '2026-01-25', // API version header 'timeout' => 30, // Request timeout (seconds) 'connect_timeout' => 10, // Connection timeout (seconds) ]);
Public API
| Class | Namespace |
|---|---|
PaymentGateway |
PaymentGateway\PaymentGateway |
PaymentBuilder |
PaymentGateway\PaymentBuilder |
PayoutBuilder |
PaymentGateway\PayoutBuilder |
SessionBuilder |
PaymentGateway\SessionBuilder |
Webhook |
PaymentGateway\Webhook |
PaymentGatewayException |
PaymentGateway\PaymentGatewayException |
Payment |
PaymentGateway\Models\Payment |
Payout |
PaymentGateway\Models\Payout |
Session |
PaymentGateway\Models\Session |
Balance |
PaymentGateway\Models\Balance |
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run tests (
composer test) - Run static analysis (
composer analyse) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See CONTRIBUTING.md for detailed guidelines.
Security
If you discover a security vulnerability, please see SECURITY.md for responsible disclosure instructions.
License
This project is licensed under the MIT License - see the LICENSE file for details.