numanrki / bank-alfalah
Bank Alfalah (Alfa) Payment Gateway SDK for PHP & Laravel. Supports Credit/Debit Card, Alfa Wallet, JazzCash, Raast QR, BNPL, Bank Account, and Card on Delivery.
Requires
- php: >=8.0
- ext-curl: *
- ext-json: *
- ext-openssl: *
Requires (Dev)
- phpunit/phpunit: ^9.0|^10.0|^11.0
README
A modern, framework-agnostic PHP SDK for Bank Alfalah payment gateway with first-class Laravel support.
Supports all 7 payment methods: Credit/Debit Card, Alfa Wallet, Bank Alfalah Account, Alfa Islamic BNPL, Card on Delivery, JazzCash Wallet, and Raast QR.
Features
- Framework-agnostic — works with Laravel, Symfony, CodeIgniter, Yii, or plain PHP
- All 7 payment types supported out of the box
- SSO redirect flow — handshake → redirect → IPN verification
- Zero dependencies — only requires
ext-openssl,ext-json,ext-curl - Laravel auto-discovery — service provider, facade, and config publishing
- Typed responses —
HandshakeResponse,IpnResponsewith enum-based status - Modern PHP 8.0+ — enums, named arguments, strict types
- IPN retry — built-in retry mechanism for payment verification
- AES-128-CBC encryption — exact Bank Alfalah specification
Installation
composer require numanrki/bank-alfalah
Requirements
- PHP >= 8.0
- OpenSSL extension
- JSON extension
- cURL extension
Quick Start (Plain PHP)
use Numanrki\BankAlfalah\BankAlfalah; use Numanrki\BankAlfalah\Transaction; use Numanrki\BankAlfalah\Enums\PaymentType; // 1. Create gateway instance $gateway = BankAlfalah::make([ 'merchant_id' => 'YOUR_MERCHANT_ID', 'store_id' => 'YOUR_STORE_ID', 'merchant_hash' => 'YOUR_MERCHANT_HASH', 'merchant_username' => 'YOUR_USERNAME', 'merchant_password' => 'YOUR_PASSWORD', 'key1' => 'YOUR_KEY_1', 'key2' => 'YOUR_KEY_2', 'sandbox' => true, // false for production ]); // 2. Create a transaction $transaction = Transaction::create('ORDER-001', 1500.00) ->currency('PKR') ->paymentType(PaymentType::CreditDebitCard) ->returnUrl('https://yoursite.com/payment/callback') ->email('customer@example.com') ->phone('03001234567'); // 3. Perform handshake $handshake = $gateway->handshake($transaction); // 4. Redirect customer to Bank Alfalah echo $gateway->buildRedirectPage($handshake, $transaction); exit;
Verify Payment (on callback URL)
// After customer returns from Bank Alfalah portal $response = $gateway->checkStatus('ORDER-001'); if ($response->isPaid()) { // Payment successful — update your order echo "Payment confirmed!"; } elseif ($response->isFailed()) { // Payment failed echo "Payment failed."; } else { // Pending — check again later (use cron/scheduled job) echo "Payment pending, will verify shortly."; }
Verify with Retry (recommended for callback URL)
// Retries 5 times with 3-second intervals $response = $gateway->checkStatusWithRetry('ORDER-001', maxRetries: 5, delaySeconds: 3); if ($response->isPaid()) { // Confirmed }
Laravel Setup
1. Publish config file
php artisan vendor:publish --tag=bankalfalah-config
This creates config/bankalfalah.php.
2. Add to .env
BANK_ALFALAH_MERCHANT_ID=your_merchant_id BANK_ALFALAH_STORE_ID=your_store_id BANK_ALFALAH_MERCHANT_HASH=your_merchant_hash BANK_ALFALAH_MERCHANT_USERNAME=your_username BANK_ALFALAH_MERCHANT_PASSWORD=your_password BANK_ALFALAH_KEY1=your_key_1 BANK_ALFALAH_KEY2=your_key_2 BANK_ALFALAH_SANDBOX=true
3. Use in Controller
use Numanrki\BankAlfalah\BankAlfalah; use Numanrki\BankAlfalah\Transaction; use Numanrki\BankAlfalah\Enums\PaymentType; class PaymentController extends Controller { public function pay(BankAlfalah $gateway) { $transaction = Transaction::create('INV-' . time(), 2500.00) ->currency('PKR') ->paymentType(PaymentType::CreditDebitCard) ->returnUrl(route('payment.callback')) ->email('customer@example.com'); $handshake = $gateway->handshake($transaction); return response($gateway->buildRedirectPage($handshake, $transaction)); } public function callback(BankAlfalah $gateway, Request $request) { $response = $gateway->checkStatusWithRetry('INV-123456789'); if ($response->isPaid()) { return redirect('/thank-you'); } return redirect('/payment-failed'); } }
Using the Facade
use Numanrki\BankAlfalah\Laravel\Facades\BankAlfalah; use Numanrki\BankAlfalah\Transaction; use Numanrki\BankAlfalah\Enums\PaymentType; $transaction = Transaction::create('ORD-100', 5000) ->currency('PKR') ->paymentType(PaymentType::AlfaWallet) ->returnUrl(route('payment.callback')); $handshake = BankAlfalah::handshake($transaction); return response(BankAlfalah::buildRedirectPage($handshake, $transaction));
Payment Types
| Enum | Value | Method |
|---|---|---|
PaymentType::CreditDebitCard |
3 |
Visa / Mastercard |
PaymentType::AlfaWallet |
1 |
Alfa Wallet |
PaymentType::BankAccount |
2 |
Bank Alfalah Account |
PaymentType::BNPL |
5 |
Alfa Islamic BNPL |
PaymentType::CardOnDelivery |
6 |
Card on Delivery |
PaymentType::JazzCash |
11 |
JazzCash Wallet |
PaymentType::RaastQR |
12 |
Raast QR |
Get all types as an array:
$allTypes = PaymentType::all(); // ['1' => 'Alfa Wallet', '2' => 'Bank Alfalah Account', '3' => 'Credit/Debit Card', ...]
Payment Flow
┌──────────────┐ 1. Handshake ┌───────────────────┐
│ Your Server │ ───────────────────▶ │ Bank Alfalah API │
│ │ ◀──────────────────── │ /HS/HS/HS │
│ │ AuthToken └───────────────────┘
│ │
│ │ 2. SSO Redirect ┌───────────────────┐
│ │ ──── (HTML Form) ────▶ │ Bank Alfalah │
│ │ │ Payment Portal │
│ │ │ /SSO/SSO/SSO │
│ │ └───────────────────┘
│ │ │
│ │ 3. Customer returns │
│ │ ◀─────────────────────────────┘
│ │
│ │ 4. IPN Check ┌───────────────────┐
│ │ ───────────────────▶ │ Bank Alfalah IPN │
│ │ ◀──────────────────── │ /HS/api/IPN/... │
└──────────────┘ Paid / Failed └───────────────────┘
API Endpoints
| Endpoint | Sandbox | Production |
|---|---|---|
| Handshake | https://sandbox.bankalfalah.com/HS/HS/HS |
https://payments.bankalfalah.com/HS/HS/HS |
| SSO | https://sandbox.bankalfalah.com/SSO/SSO/SSO |
https://payments.bankalfalah.com/SSO/SSO/SSO |
| IPN | https://sandbox.bankalfalah.com/HS/api/IPN/OrderStatus/{MerchantId}/{StoreId}/{OrderRef} |
https://payments.bankalfalah.com/HS/api/IPN/OrderStatus/{MerchantId}/{StoreId}/{OrderRef} |
IPN Webhook / Cron Job
Bank Alfalah sends the customer back to your returnUrl, but the payment may still be processing. Use a cron job or scheduled task to verify pending payments:
Laravel Scheduled Command
// app/Console/Commands/VerifyPendingPayments.php use Numanrki\BankAlfalah\BankAlfalah; class VerifyPendingPayments extends Command { protected $signature = 'payments:verify'; public function handle(BankAlfalah $gateway) { $pendingOrders = Order::where('status', 'pending')->get(); foreach ($pendingOrders as $order) { $response = $gateway->checkStatus($order->transaction_ref); if ($response->isPaid()) { $order->update(['status' => 'paid']); } elseif ($response->isFailed()) { $order->update(['status' => 'failed']); } } } }
// app/Console/Kernel.php $schedule->command('payments:verify')->everyFiveMinutes();
Plain PHP Cron
// cron.php — run via crontab: */5 * * * * php /path/to/cron.php require 'vendor/autoload.php'; $gateway = \Numanrki\BankAlfalah\BankAlfalah::make([/* your config */]); $pendingOrders = get_pending_orders(); // your function foreach ($pendingOrders as $order) { $response = $gateway->checkStatus($order['transaction_ref']); if ($response->isPaid()) { mark_as_paid($order['id']); } }
Error Handling
use Numanrki\BankAlfalah\Exceptions\InvalidConfigException; use Numanrki\BankAlfalah\Exceptions\HandshakeException; use Numanrki\BankAlfalah\Exceptions\PaymentException; try { $handshake = $gateway->handshake($transaction); } catch (InvalidConfigException $e) { // Missing merchant credentials echo "Configuration error: " . $e->getMessage(); } catch (HandshakeException $e) { // Bank Alfalah rejected the handshake echo "Gateway error: " . $e->getMessage(); } catch (PaymentException $e) { // General payment/connection error echo "Error: " . $e->getMessage(); }
IPN Response Properties
$response = $gateway->checkStatus($transactionRef); $response->isPaid(); // true if TransactionStatus === 'Paid' $response->isFailed(); // true if TransactionStatus === 'Failed' $response->isSessionEnded(); // true if TransactionStatus === 'SessionEnded' $response->isDecided(); // true if paid OR failed (not pending) $response->getStatus(); // TransactionStatus enum or null $response->getTransactionRef();// Transaction reference number $response->getRawData(); // Full response array from Bank Alfalah
Configuration Reference
| Key | Description | Required |
|---|---|---|
merchant_id |
Unique merchant identifier from Bank Alfalah | Yes |
store_id |
Store/location identifier | Yes |
merchant_hash |
Authentication hash from Bank Alfalah | Yes |
merchant_username |
API username | Yes |
merchant_password |
API password | Yes |
key1 |
AES-128-CBC encryption key | Yes |
key2 |
AES-128-CBC initialization vector (IV) | Yes |
sandbox |
true for sandbox, false for production |
No (default: false) |
Migrating from Other Packages
From naeemz/alfapay or zfhassaan/alfa
This package provides a cleaner, more complete implementation:
- Replace
composer requirewithnumanrki/bank-alfalah - Update env variables from
ALFAPAY_*toBANK_ALFALAH_* - Use the
Transactionbuilder instead of setter chains - SSO redirect is built-in (no manual form building needed)
- IPN verification with retry is built-in
- All 7 payment types work out of the box
Credits
- Numan (@numanrki)
- Built on top of the Bank Alfalah payment gateway specification
License
MIT License. See LICENSE for details.