Search by

mohamed-sinani / mobile-money-php

mohamed-sinani

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

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-17 17:01 UTC

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-curl
  • ext-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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Run tests (composer test)
  4. Run static analysis (composer analyse)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. 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.