romanalisoy / pashabank-laravel-sdk
Laravel 12/13 SDK for PASHA Bank CardSuite ECOMM (3D-Secure acquiring) integration.
Package info
github.com/romanalisoy/pashabank-laravel-sdk
pkg:composer/romanalisoy/pashabank-laravel-sdk
Requires
- php: ^8.2
- ext-openssl: *
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/database: ^12.0 || ^13.0
- illuminate/events: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/routing: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0 || ^11.0
- pestphp/pest: ^3.7
- pestphp/pest-plugin-laravel: ^3.1
This package is not auto-updated.
Last update: 2026-04-22 13:45:38 UTC
README
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 certConnectionException— TLS / DNS / timeout; retry-safeMerchantException— bank rejected the request structurallyValidationException— bad amount / currency / biller_client_id, etc.
Security
- Card numbers are masked before touching logs (
mask_card_numbersconfig) - 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
.p12certificate 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.