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.
Package info
github.com/JoseModi97/yii2-ecitizen-gateway
Type:yii2-extension
pkg:composer/josemodi97/yii2-ecitizen-gateway
Requires
- php: >=7.4
Requires (Dev)
- phpunit/phpunit: ^9.6 || ^10.0
Suggests
- illuminate/support: Required for Laravel integration: ServiceProvider and Facade auto-discovery.
- yiisoft/yii2: Needed for Yii2 integration: Gii generators, EcitizenPaymentTrait, EcitizenInvoiceInterface, or registering EcitizenGateway as an application component.
- yiisoft/yii2-gii: Required if using the eCitizen Gii code generators to generate EcitizenClient and PaymentController.
Provides
None
Conflicts
None
Replaces
None
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
EcitizenServiceProviderandEcitizenFacade withphp artisan vendor:publishsupport. - 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:
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:
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:
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:
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.



