romanalisoy/pashabank-laravel-sdk

Laravel 12/13 SDK for PASHA Bank CardSuite ECOMM (3D-Secure acquiring) integration.

Maintainers

Package info

github.com/romanalisoy/pashabank-laravel-sdk

pkg:composer/romanalisoy/pashabank-laravel-sdk

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

1.0.0 2026-04-22 13:42 UTC

This package is not auto-updated.

Last update: 2026-04-22 13:45:38 UTC


README

Packagist Version Laravel PHP License

A modern, type-safe Laravel SDK for the PASHA Bank CardSuite ECOMM (3D-Secure acquiring) integration. Supports SMS / DMS payments, reversal, refund, and the full recurring payments lifecycle.

Features

  • First-class Laravel 12 & 13 support (PHP 8.2+)
  • Fluent builders for every bank command — no magic strings
  • mTLS / PKCS#12 handshake handled for you
  • Eloquent persistence with overridable table names and model classes
  • Events for every significant lifecycle transition
  • Ready-made callback route or helper-only mode, your choice
  • Multi-merchant out of the box (single-merchant stays simple)
  • Testable: PashaBank::fake() + Pest / PHPUnit friendly

Installation

composer require romanalisoy/pashabank-laravel-sdk

Publish config and migrations:

php artisan vendor:publish --tag=pashabank-config
php artisan vendor:publish --tag=pashabank-migrations
php artisan migrate

Configuration

Add to your .env:

PASHABANK_MERCHANT_ID=0001234
PASHABANK_TERMINAL_ID=TRM001

# Certificate issued by the bank (PKCS#12):
PASHABANK_CERT_TYPE=pkcs12
PASHABANK_CERT_PATH=/secure/certs/imakstore.0001234.p12
PASHABANK_CERT_PASSWORD=your-keystore-password
PASHABANK_CA_PATH=/secure/certs/PSroot.pem

# Callback behaviour — either 'redirect' (server-rendered app) or 'json'
# (SPA / mobile backend):
PASHABANK_CALLBACK_RESPONSE=redirect
PASHABANK_SUCCESS_URL=/payment/success
PASHABANK_FAILURE_URL=/payment/failure

Converting JKS → PKCS#12 (if the bank issued a .jks)

keytool -importkeystore \
  -srckeystore imakstore.jks -destkeystore imakstore.p12 \
  -deststoretype PKCS12 -srcalias ima \
  -deststorepass yourpassword -destkeypass yourpassword

Custom table names

If your company policy disallows third-party prefixes, override them in config/pashabank.php:

'persistence' => [
    'tables' => [
        'transactions' => 'payments',
        'recurring' => 'payment_subscriptions',
    ],
    'models' => [
        'transaction' => App\Models\Payment::class,
        'recurring' => App\Models\Subscription::class,
    ],
],

Your custom models must extend the SDK's base models (or re-implement the same interface — the SDK only uses public methods).

Usage

Simple SMS payment

use Romanalisoy\PashaBank\Facades\PashaBank;

public function checkout(Order $order)
{
    $registration = PashaBank::sms()
        ->amount($order->total)          // 19.80 decimal
        ->currency('AZN')                // or '944'
        ->description("Order #{$order->id}")
        ->language(app()->getLocale())
        ->for($order)                    // polymorphic link
        ->register();

    return redirect($registration->redirectUrl);
}

After 3DS and card entry the bank POSTs back to /pashabank/callback, which runs command=c, updates the transaction, fires PaymentCompleted / PaymentFailed, and redirects the browser.

Listening for completion

use Romanalisoy\PashaBank\Events\PaymentCompleted;

Event::listen(PaymentCompleted::class, function (PaymentCompleted $e) {
    $order = $e->transaction->payable;
    $order->markPaid();
});

DMS (auth + capture)

$auth = PashaBank::dms()
    ->amount($order->total)
    ->currency('AZN')
    ->for($order)
    ->authorize();

return redirect($auth->redirectUrl);

// ...once the customer authorises and you are ready to capture:
PashaBank::dmsComplete($auth->transactionId)
    ->amount($order->total)
    ->currency('AZN')
    ->execute();

Reversal and refund

PashaBank::reversal($transId)->execute();                          // full reversal
PashaBank::reversal($transId)->amount(10.00)->execute();           // partial reversal
PashaBank::reversal($transId)->suspectedFraud()->execute();        // fraud flagged

PashaBank::refund($transId)->execute();                            // full refund
PashaBank::refund($transId)->amount(5.00)->execute();              // partial refund

Recurring payments

// 1. Register template + first charge (customer completes 3DS):
$registration = PashaBank::recurring()
    ->registerWithFirstPayment()
    ->amount(9.99)->currency('AZN')
    ->billerClientId('sub-'.$user->id)
    ->expiry('1231')               // MMYY
    ->description('Monthly subscription')
    ->for($user)
    ->register();

return redirect($registration->redirectUrl);

// 2. Charge later (no customer interaction needed):
PashaBank::recurring()
    ->execute('sub-'.$user->id)
    ->amount(9.99)->currency('AZN')
    ->charge();

// 3. Cancel:
PashaBank::recurring()->delete('sub-'.$user->id)->execute();

Multi-merchant

PashaBank::merchant('shop_two')->sms()->amount(50)->register();

Add further merchants to config/pashabank.php:

'merchants' => [
    'main'     => [ /* ... */ ],
    'shop_two' => [ /* ... */ ],
],

Manual completion (helper-only flow)

If you disable the built-in callback route, wire up your own endpoint:

// config/pashabank.php
'callback' => ['enabled' => false, /* ... */],
Route::post('/my/return', function (Request $request) {
    $status = PashaBank::completion($request->input('trans_id'))->get();

    return $status->isSuccessful()
        ? redirect('/thank-you')
        : redirect('/sorry');
});

Testing

The SDK ships with a test-friendly fake that keeps every operation functional (validation, events, persistence) while stubbing out the HTTP call to the bank.

use Romanalisoy\PashaBank\Facades\PashaBank;

it('charges the customer', function () {
    $fake = PashaBank::fake()->willReturnForCommand('v', [
        'TRANSACTION_ID' => 'test-trans-1',
    ]);

    $this->post('/checkout', [...]);

    $fake->assertCommandSent('v', fn ($p) => $p['amount'] === '19.80');
});

Simulate the bank callback:

PashaBank::fake()->fakeCallback('test-trans-1', [
    'RESULT' => 'OK',
    'RESULT_CODE' => '000',
]);

$this->post('/pashabank/callback', ['trans_id' => 'test-trans-1'])
     ->assertRedirect('/payment/success');

Events

Event Fired when
PaymentRegistered Bank accepts a new payment registration
PaymentCompleted command=c reports success
PaymentFailed command=c reports failure / decline / timeout
PaymentReversed Successful command=r
PaymentRefunded Successful command=k
RecurringRegistered Recurring template stored at bank
RecurringExecuted command=e completed (success or decline)
RecurringDeleted Recurring template removed from bank

Exceptions

All SDK exceptions extend Romanalisoy\PashaBank\Exceptions\PashaBankException:

  • ConfigurationException — missing merchant config, unreadable cert
  • ConnectionException — TLS / DNS / timeout; retry-safe
  • MerchantException — bank rejected the request structurally
  • ValidationException — bad amount / currency / biller_client_id, etc.

Security

  • Card numbers are masked before touching logs (mask_card_numbers config)
  • Sensitive fields (cvv2, pan, expiry) are scrubbed from log output
  • TLSv1.2 is enforced for every request to the bank
  • Callback route supports an IP allowlist (CIDR-aware)

Requirements

  • PHP 8.2+
  • Laravel 12.x or 13.x
  • PHP extensions: openssl, curl
  • A signed .p12 certificate issued by PASHA Bank

License

MIT. See LICENSE.

Not affiliated with PASHA Bank OJSC. Integration details are based on the bank's public integration specification.