Search by

taitech / azampay

tairocruiz

A Laravel package for integrating your application with AzamPay payment services

Package info

github.com/tairocruiz/azampay

pkg:composer/taitech/azampay

Statistics

Installs: 32

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

1.3.0 2026-10-23 06:36 UTC

This package is auto-updated.

Last update: 2026-10-23 06:37:05 UTC


README

A robust PHP package for seamless integration with Azampay payment services, supporting Mobile Money (MNO), Bank, and Merchant payment options.

Features

  • Mobile Money (MNO) payments — Airtel, Tigo, Halopesa, Azampesa, Mpesa
  • Bank payments (CRDB, NMB)
  • Disbursements, name lookup and payout status
  • Payment partners and hosted checkout pages
  • RSA-verified payment callbacks
  • Http::fake() support in Laravel tests
  • Environment switching (Sandbox/Production)
  • Simple credential management
  • Flexible service selection
  • Laravel 9.x, 10.x, 11.x, and 12.x support
  • Automatic service discovery and registration
  • Built-in facades and dependency injection
  • Spatie Activity Log integration for database logging
  • Comprehensive payment event tracking

Installation

Standard Installation

composer require taitech/azampay

Requirements

  • PHP: ^8.1
  • Laravel: ^9.0|^10.0|^11.0|^12.0
  • GuzzleHTTP: ^7.5

Laravel Integration

This package is designed to work seamlessly with Laravel applications. For detailed Laravel integration instructions, see LARAVEL_INTEGRATION.md.

Quick Laravel Setup:

  1. Install the package: composer require taitech/azampay
  2. Publish configuration: php artisan vendor:publish --tag=azampay-config
  3. Add environment variables to your .env file
  4. Include routes: require base_path('routes/azampay.php');

Laravel Usage Examples:

// Using the Facade
use Taitech\Azampay\Facades\Azampay;
$response = Azampay::checkout($payload);

// Using the Service Container
$azampay = app('azampay');
$response = $azampay->checkout($payload);

// Using Dependency Injection
public function __construct(private AzampayService $azampay) {}
$response = $this->azampay->checkout($payload);

Quick Start

use Taitech\Azampay\AzampayService;

// Initialize the service
$azampay = new AzampayService(
    appName: 'YOUR_APP_NAME',
    clientId: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET',
    env: 'SANDBOX', // or 'PRODUCTION'
    service: 'MNO' // or 'BANK' or 'MERCHANT'
);

Configuration

The package requires the following configuration parameters:

  • appName: Your application name registered with Azampay
  • clientId: Your Azampay client ID
  • secret: Your Azampay client secret
  • env: Environment setting (SANDBOX or PRODUCTION)
  • service: Service type (MNO, BANK, or MERCHANT)

Service Types

Mobile Money (MNO)

$azampay = new AzampayService(
    appName: 'YOUR_APP_NAME',
    clientId: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET',
    env: 'SANDBOX', // or 'PRODUCTION'
    service: 'MNO'
);

Bank Payments

$azampay = new AzampayService(
    appName: 'YOUR_APP_NAME',
    clientId: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET',
    env: 'SANDBOX', // or 'PRODUCTION'
    service: 'BANK'
);

Merchant Services

$azampay = new AzampayService(
    appName: 'YOUR_APP_NAME',
    clientId: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET',
    env: 'SANDBOX', // or 'PRODUCTION'
    service: 'MERCHANT'
);

Usage

Initiate Payment and Checkout

// vendor credentials
$app = 'YOUR_APP_NAME';
$clientId = 'YOUR_CLIENT_ID';
$secret = 'YOUR_SECRET_KEY';


try {
     $azampay = new AzampayService(
        $app, // App Name
        $clientId, // Client ID
        $secret, // Client Secret
        'SANDBOX', // Environment (SANDBOX/PRODUCTION)
        'MNO', // Service (MNO, BANK, MERCHANT)
    );

    // Example of calling the checkout method for MNO [MOBILE PAYMENT]
    $payload = [
        'amount' => 5000,
        'accountNumber' => '0713 295 803',      // normalised to 255713295803
        'provider' => 'Mpesa',                  // Airtel, Tigo, Halopesa, Azampesa or Mpesa
        'externalId' => 'ORDER-1001-PAY-1',     // YOUR reference (≤ 128 chars) — returned as utilityref in the callback
        'additionalProperties' => [
                'order' => 'ORDER-1001',
            ],
    ];

    $response = $azampay->checkout($payload);
    // ['transactionId' => '...', 'message' => '...', 'success' => true, 'externalId' => 'ORDER-1001-PAY-1']

} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}

Handling the callback (since 1.2)

AzamPay calls your callback URL when the customer approves or declines the prompt. Each callback carries an RSA signature over {utilityref}{externalreference}{transactionstatus}{operator}; verify it before trusting the result, then match the payment by utilityref (your externalId).

use Taitech\Azampay\Modules\CallbackModule;

// Laravel: app('azampay.callback') is pre-configured (fetches AzamPay's
// public key automatically, or uses AZAMPAY_PUBLIC_KEY).
$callback = app('azampay.callback');

// Plain PHP: give it the PEM key, or a closure that fetches it.
$callback = new CallbackModule(null, fn () => $azampay->getPublicKey());

$data = json_decode($request->getContent(), true);

if (! $callback->verify($data)) {
    return response()->json(['success' => false], 401);
}

$result = $callback->parse($data);
// $result['reference']  → your externalId
// $result['successful'] → true when transactionstatus is "success"
// $result['amount'], $result['operator'], $result['mno_reference'], ...

Always check that amount equals the amount you requested before marking an order paid, and ignore repeat callbacks for a payment that is already final.

HTTP options

new AzampayService($app, $clientId, $secret, 'SANDBOX', 'MNO', [
    'timeout' => 30,        // seconds
    'force_ipv4' => true,   // resolve AzamPay hosts over IPv4 only
]);

In Laravel: AZAMPAY_TIMEOUT, AZAMPAY_FORCE_IPV4, AZAMPAY_PUBLIC_KEY.

Bank checkout — CRDB, NMB (since 1.3)

The customer links their account and gets an OTP from their bank first (CRDB: *150*03# → 7 → 5 → Link Azampay Account; NMB: *150*66# → 8 → 5 → 1).

$bank = $azampay->for('BANK');   // or new AzampayService(..., 'BANK')

$response = $bank->checkout([
    'provider' => 'CRDB',                  // CRDB or NMB
    'amount' => 25000,
    'merchantAccountNumber' => '0150123456789',
    'merchantMobileNumber' => '0712345678',
    'otp' => '123456',
    'referenceId' => 'ORDER-1001-PAY-2',   // yours; generated if omitted and returned
]);

Disbursements — payouts, name lookup, status (since 1.3)

$payouts = $azampay->for('DISBURSEMENT');

$payouts->nameLookup('tigo', '255712345678');   // confirm the recipient's name first

$response = $payouts->disburse([
    'source' => ['countryCode' => 'TZ', 'fullName' => 'My Shop Ltd', 'bankName' => 'azampesa', 'accountNumber' => '2557…', 'currency' => 'TZS'],
    'destination' => ['countryCode' => 'TZ', 'fullName' => 'Asha Juma', 'bankName' => 'tigo', 'accountNumber' => '255712345678', 'currency' => 'TZS'],
    'transferDetails' => ['amount' => 15000],    // type defaults to MNO, dateInEpoch to now
    'externalReferenceId' => 'REFUND-42',        // yours; generated if omitted and returned
    'remarks' => 'Refund for order 42',
    // 'checksum' => '...',                      // if AzamPay requires it for your account
]);

$payouts->transactionStatus($response['pgReferenceId'], 'tigo');

Wallets: tigo, airtel, azampesa. Payout callbacks: app('azampay.callback')->parseDisbursement($data) (AzamPay documents no signature for these — confirm with transactionStatus() before acting).

Payment partners and hosted checkout page (since 1.3)

$merchant = $azampay->for('MERCHANT');

$partners = $merchant->paymentPartners();   // names, logos, provider ids

$page = $merchant->checkoutPage([
    'amount' => 15000,
    'vendorId' => 'your-vendor-id',
    'vendorName' => 'My Shop',
    'requestOrigin' => 'https://shop.example',
    'redirectSuccessURL' => 'https://shop.example/paid',
    'redirectFailURL' => 'https://shop.example/failed',
    'cart' => ['items' => [['name' => 'Router']]],
]);

return redirect($page['url']);   // $page['externalId'] is your reference

Testing

composer install
composer test

Faking AzamPay in your Laravel tests (since 1.3)

The service provider sends requests through Laravel's HTTP client, so Http::fake() works:

Http::fake([
    'authenticator-sandbox.azampay.co.tz/*' => Http::response(['data' => ['accessToken' => 'test']]),
    'sandbox.azampay.co.tz/azampay/mno/checkout' => Http::response(['transactionId' => 'T1', 'success' => true]),
]);

app('azampay')->checkout([...]);

Http::assertSent(fn ($request) => $request['externalId'] === 'ORDER-1001-PAY-1');

Outside the container: $azampay->setHttpFactory(app(\Illuminate\Http\Client\Factory::class)), or setHttpClient($guzzle) for a Guzzle client/mock.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/your-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/your-feature)
  5. Create a new Pull Request
  6. Ensure your code adheres to the project's coding standards
  7. Provide clear and concise commit messages

For more details, please refer to the CONTRIBUTING file.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For any issues or questions, please open an issue on the GitHub repository.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.3.0] - 2026-10-23

Bank checkout, disbursements, payment partners, hosted checkout page, and Http::fake() support. See CHANGELOG.md.

[1.2.0] - 2026-10-23

Fixes externalId handling and callback verification (RSA). See CHANGELOG.md.

[1.1.0] - 2024-12-19

Added

  • Laravel 12.x compatibility and support
  • Enhanced version compatibility across Laravel 9.x, 10.x, 11.x, and 12.x

[1.0.0] - 2024-01-01

Added

  • Initial release
  • Support for Mobile Money (MNO) payments
  • Support for Bank payments
  • Support for Merchant services
  • Environment switching capability
  • Flexible service selection