Search by

josemodi97 / yii2-ecitizen-gateway

Beginner-friendly eCitizen payment gateway for Laravel, Yii2, and standalone PHP: build signed checkout payloads, render pay buttons, and verify callbacks with plain-English fields. Plain PHP, no required framework dependencies.

Maintainers

Package info

github.com/JoseModi97/yii2-ecitizen-gateway

Type:yii2-extension

pkg:composer/josemodi97/yii2-ecitizen-gateway

Transparency log

Statistics

Installs: 40

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.6.2 2026-09-04 08:47 UTC

README

A beginner-friendly Kenya eCitizen / PesaFlow payment gateway for Laravel, Yii2, and Standalone PHP. Build signed checkout payloads, render payment buttons, and verify webhook callbacks with plain-English fields.

  • Framework Agnostic: Pure PHP with zero forced dependencies (php: >=7.4). Works seamlessly in Laravel, Yii2, Yii3, WordPress, or vanilla PHP.
  • Laravel Auto-Discovery: Includes native EcitizenServiceProvider and Ecitizen Facade with php artisan vendor:publish support.
  • Yii2 Gii Tools: Includes eCitizen Client Generator and eCitizen Controller Generator in Gii with zero view-template dependencies.
  • Zero Database Obligation: Accepts and verifies payments out of the box without requiring any database tables or migrations. Bring your own database models when ready.
  • Instant Payment Button: Render ready-to-use, HMAC-signed payment forms in one line of code (payButton()).
  • Safaricom M-Pesa STK Push: Automatic Kenyan phone normalization (PhoneHelper) to trigger instant PIN prompts on customer phones.
  • Cryptographic Signature Verification: Validate server-to-server IPN notifications with HMAC-SHA256 (verify(), isPaid()).

Compatibility

  • Requires PHP 7.4 or newer (fully tested on PHP 8.0, 8.1, 8.2, 8.3, and 8.4+).
  • Laravel: Fully compatible with Laravel 6.x, 7.x, 8.x, 9.x, 10.x, and 11.x+.
  • Yii2: Fully compatible with Yii 2.0.x and Gii code generation.
  • Standalone PHP: Core classes (EcitizenClient, EcitizenGateway, PhoneHelper) have zero third-party dependencies.

Installation

Install via Composer:

composer require josemodi97/yii2-ecitizen-gateway

Quickstart: Laravel

The package registers its Service Provider and Ecitizen Facade automatically via Laravel Package Discovery.

1. Publish Configuration

Run the Artisan publish command to create config/ecitizen.php:

php artisan vendor:publish --tag=ecitizen-config

2. Configure Environment Variables

Add your merchant credentials to your .env file:

ECITIZEN_CLIENT_ID=your_api_client_id
ECITIZEN_API_KEY=your_api_key
ECITIZEN_SECRET=your_merchant_secret
ECITIZEN_SERVICE_ID=your_service_id
ECITIZEN_GATEWAY_URL=https://payments.ecitizen.go.ke/PaymentAPI/iframev2.1.php
ECITIZEN_CURRENCY=KES

3. Create a Payment Controller

Use the Ecitizen Facade in your controller:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Ecitizen; // or: use odhis\ecitizen\adapters\laravel\Facades\Ecitizen;

class PaymentController extends Controller
{
    /**
     * Display the payment summary card and pay button.
     */
    public function pay()
    {
        $payButtonHtml = Ecitizen::payButton([
            'amount'      => 1500,
            'reference'   => 'INV-1002',
            'description' => 'Land Rates Clearance',
            'name'        => 'John Doe',
            'idNumber'    => '28374619',
            'phone'       => '0712345678', // Automatically triggers Safaricom M-Pesa STK push!
            'callbackUrl' => route('payment.success'),
            'notifyUrl'   => route('payment.notify'),
        ], 'Proceed to eCitizen', ['class' => 'btn btn-success btn-lg']);

        return view('payment.pay', compact('payButtonHtml'));
    }

    /**
     * Webhook endpoint: receives server-to-server IPN from eCitizen.
     */
    public function notify(Request $request)
    {
        $result = Ecitizen::verify($request->all());

        if ($result['success']) {
            // Payment verified and settled!
            $reference  = $result['reference'];
            $amountPaid = $result['amountPaid'];

            // Optional: update your database record here
            // Order::where('reference', $reference)->update(['status' => 'paid']);

            return response()->json(['status' => 'ok']);
        }

        return response()->json(['status' => 'error', 'message' => 'Invalid signature'], 400);
    }

    /**
     * Browser redirect landing page after payment.
     */
    public function success()
    {
        return view('payment.success');
    }
}

4. Configure Routes & Exempt CSRF

Because eCitizen servers send webhook notifications via POST, you must exempt the notification route from CSRF verification.

Routes (routes/web.php):

use App\Http\Controllers\PaymentController;

Route::get('/payment/pay', [PaymentController::class, 'pay'])->name('payment.pay');
Route::post('/payment/notify', [PaymentController::class, 'notify'])->name('payment.notify');
Route::get('/payment/success', [PaymentController::class, 'success'])->name('payment.success');

CSRF Exemption:

  • Laravel 11+ (bootstrap/app.php):

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'payment/notify',
        ]);
    })
  • Laravel 6.x – 10.x (app/Http/Middleware/VerifyCsrfToken.php):

    protected $except = [
        'payment/notify',
    ];

5. Render in Blade View (resources/views/payment/pay.blade.php)

<div class="card p-4 shadow-sm text-center">
    <h2>Invoice #INV-1002</h2>
    <p class="lead">Amount: <strong>KES 1,500</strong></p>
    <div class="mt-3">
        {!! $payButtonHtml !!}
    </div>
</div>

Quickstart: Yii2

Yii2 provides dedicated visual code generators in Gii with zero view-template dependencies.

Step 1: Client Generator (ecitizen-client)

Open Gii in your browser (http://localhost/index.php?r=gii), select eCitizen Client Generator, fill in your credentials, and click Generate:

eCitizen Client Generator in Gii

This writes @app/config/ecitizen.php:

use odhis\ecitizen\EcitizenClient;

$ecitizen = new EcitizenClient([
    'apiClientID' => 'YOUR_API_CLIENT_ID',
    'apiKey'      => 'YOUR_API_KEY',
    'secret'      => 'YOUR_SECRET',
    'serviceID'   => 'YOUR_SERVICE_ID',
]);

return $ecitizen;

Step 2: Controller Generator (ecitizen-controller)

Select eCitizen Controller Generator and click Generate:

eCitizen Controller Generator in Gii

This creates @app/controllers/PaymentController.php with self-contained rendering (no separate view files needed):

  • actionPay() — Displays payment summary and payment button.
  • actionNotify() — Receives and cryptographically verifies server-to-server webhook notifications.
  • actionSuccess() — Customer confirmation landing page.

Step 3: Making Payments in Yii2

Visit http://localhost/index.php?r=payment/pay:

eCitizen Payment Pay Screen

Customize parameters on the fly via query parameters:

http://localhost/index.php?r=payment/pay&amount=1200&description=Permit+Renewal&phone=0712345678

When payment completes, eCitizen redirects to your confirmation page:

eCitizen Payment Confirmation

Quickstart: Standalone PHP (No Framework)

You can use the gateway directly in any vanilla PHP script or microframework:

1. Initialize Client & Render Payment Button

require_once __DIR__ . '/vendor/autoload.php';

use odhis\ecitizen\EcitizenClient;

$ecitizen = new EcitizenClient([
    'apiClientID' => 'YOUR_API_CLIENT_ID',
    'apiKey'      => 'YOUR_API_KEY',
    'secret'      => 'YOUR_SECRET',
    'serviceID'   => 'YOUR_SERVICE_ID',
]);

echo $ecitizen->payButton([
    'amount'      => 500,
    'reference'   => 'INV-0001',
    'description' => 'School fees',
    'name'        => 'Jane Doe',
    'idNumber'    => '12345678',
    'phone'       => '0712345678', // Automatically formatted for M-Pesa STK push
    'callbackUrl' => 'https://example.com/payment/success',
    'notifyUrl'   => 'https://example.com/payment/notify',
]);

2. Verify Incoming Webhook (notify.php)

require_once __DIR__ . '/vendor/autoload.php';

use odhis\ecitizen\EcitizenClient;

$ecitizen = new EcitizenClient([
    'apiClientID' => 'YOUR_API_CLIENT_ID',
    'apiKey'      => 'YOUR_API_KEY',
    'secret'      => 'YOUR_SECRET',
    'serviceID'   => 'YOUR_SERVICE_ID',
]);

$result = $ecitizen->verify($_POST);

header('Content-Type: application/json');

if ($result['success']) {
    $ref    = $result['reference'];   // e.g. 'INV-0001'
    $paid   = $result['amountPaid'];  // e.g. 500.00
    $status = $result['status'];      // e.g. 'Settled'

    // Update your database / log settlement...

    echo json_encode(['status' => 'ok']);
    exit;
}

http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Signature verification failed']);

Core Features & Usage Details

1. Safaricom M-Pesa STK Push

When the customer provides a phone number in any common Kenyan format (0712345678, +254712345678, or 254712345678), the built-in PhoneHelper automatically normalizes it to international standard 2547XXXXXXXX and sets sendStkPush => true.

This causes eCitizen to immediately initiate an M-Pesa STK push PIN prompt on the customer's phone upon reaching the gateway.

2. Custom Checkout Form or Embedded iFrame (checkout())

If you want to render an embedded <iframe> on your page or submit via custom JavaScript instead of using payButton(), use checkout():

$checkout = $ecitizen->checkout([
    'amount'      => 2500,
    'reference'   => 'APP-4401',
    'description' => 'Building Permit',
    'name'        => 'Grace Mwangi',
    'idNumber'    => '19283746',
    'callbackUrl' => 'https://example.com/payment/success',
    'notifyUrl'   => 'https://example.com/payment/notify',
]);

// Returns:
// [
//     'url' => 'https://payments.ecitizen.go.ke/PaymentAPI/iframev2.1.php',
//     'payload' => [
//         'apiClientID'    => '...',
//         'serviceID'      => '...',
//         'billRefNumber'  => 'APP-4401',
//         'amountExpected' => '2500.00',
//         'currency'       => 'KES',
//         'secureHash'     => '...',
//         ...
//     ]
// ]

Embedded iFrame HTML Example:

<iframe
    src="<?= htmlspecialchars($checkout['url'] . '?' . http_build_query($checkout['payload'])) ?>"
    width="100%"
    height="700px"
    frameborder="0"
    allow="payment">
</iframe>

3. URLs Explained: Browser Redirect vs Server Webhook

eCitizen handles communication through two separate channels:

Parameter Initiated By Purpose Verification
callbackUrl (callBackURLOnSuccess) Customer's Browser Redirects the user back to your site after payment to show a confirmation page. Client-side redirect. Never use this alone to mark orders as paid.
notifyUrl (notificationURL) eCitizen's Server Asynchronous server-to-server webhook (IPN) carrying transaction status and cryptographic HMAC signature. Verified via verify() or isPaid(). Use this to confirm settlement in your database.

4. Bring Your Own Database (BYOD)

The library does not mandate any database tables, migrations, or schema definitions. You are free to persist transactions however your project requires:

// In your notification webhook handler:
if ($result['success']) {
    // Example with Eloquent (Laravel):
    Order::where('reference', $result['reference'])->update([
        'status'      => 'paid',
        'amount_paid' => $result['amountPaid'],
    ]);

    // Example with ActiveRecord (Yii2):
    // $order = Order::findOne(['reference' => $result['reference']]);
    // if ($order) { $order->status = 'paid'; $order->save(false); }

    // Example with PDO (Vanilla PHP):
    // $stmt = $pdo->prepare("UPDATE orders SET status = 'paid' WHERE reference = ?");
    // $stmt->execute([$result['reference']]);
}

File & Class Structure

Path Purpose
src/EcitizenClient.php Main public API (payButton(), checkout(), verify(), isPaid()).
src/EcitizenGateway.php Cryptographic HMAC-SHA256 signature generator and verifier.
src/helpers/PhoneHelper.php Normalizes Kenyan phone numbers for Safaricom M-Pesa STK push.
src/adapters/laravel/EcitizenServiceProvider.php Laravel Service Provider supporting config publishing and DI container binding.
src/adapters/laravel/Facades/Ecitizen.php Laravel Facade for static Ecitizen::... calls.
src/adapters/laravel/config/ecitizen.php Default Laravel configuration template.
src/Bootstrap.php Yii2 extension bootstrap registering Gii code generators.
src/generators/client/ Yii2 Gii Client Generator (ecitizen-client).
src/generators/controller/ Yii2 Gii Controller Generator (ecitizen-controller) with zero view dependencies.
src/interfaces/EcitizenInvoiceInterface.php Optional interface for existing invoice models.
src/controllers/EcitizenPaymentTrait.php Optional trait for webhook handling.

Security

  • Sensitive credentials (API Keys, Secrets, Passwords) should always be stored in environment variables (.env).
  • Never skip HMAC signature verification on inbound webhooks. Always use $ecitizen->verify($postData).
  • Always exempt the notification webhook URL from CSRF middleware so eCitizen's server can deliver payment updates.

License

MIT License. See LICENSE for details.