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.

Maintainers

Package info

github.com/numanrki/bank-alfalah-payment-gateway

pkg:composer/numanrki/bank-alfalah

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-04-06 11:23 UTC

This package is auto-updated.

Last update: 2026-04-06 11:30:30 UTC


README

Latest Version on Packagist Total Downloads License: MIT PHP Version

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 responsesHandshakeResponse, IpnResponse with 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:

  1. Replace composer require with numanrki/bank-alfalah
  2. Update env variables from ALFAPAY_* to BANK_ALFALAH_*
  3. Use the Transaction builder instead of setter chains
  4. SSO redirect is built-in (no manual form building needed)
  5. IPN verification with retry is built-in
  6. All 7 payment types work out of the box

Credits

License

MIT License. See LICENSE for details.