mainul / pay-station-disbursement
Laravel package for the PayStation bKash/Nagad/Rocket disbursement API.
Package info
github.com/Mainul12501/pay-station-disbursement
pkg:composer/mainul/pay-station-disbursement
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/validation: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.5
This package is auto-updated.
Last update: 2026-05-01 19:41:48 UTC
README
mainul/pay-station-disbursement is a Laravel package for the PayStation bKash disbursement API.
It currently implements the two documented endpoints:
| Method | Endpoint | Description |
|---|---|---|
POST |
/merchant/disbursement/request |
Disburse money to a bKash account |
POST |
/merchant/disbursement/status |
Check the status of a disbursement transaction |
The package is designed for Laravel 11, 12, and 13, uses Laravel's HTTP client, validates request payloads before sending them, and throws explicit exceptions for configuration, validation, transport, and API-level failures.
Features
- Laravel auto-discovery support (zero-config registration)
- Publishable configuration file with environment variable support
- Typed request payload and response objects (DTOs)
- Clear exception hierarchy for operational failures
- Runtime credential overrides for multi-merchant use cases
- Built-in request validation before HTTP calls
- Configurable timeout and retry logic
- Testbench test coverage for the documented endpoints
Requirements
- PHP
8.2+ - Laravel
11.x,12.x, or13.x
Installation
Install the package with Composer:
composer require mainul/pay-station-disbursement
The service provider and facade are registered automatically via Laravel's package auto-discovery.
Publish the config file:
php artisan vendor:publish --tag=bkash-disbursement-config
This publishes config/bkash-disbursement.php to your application's config directory.
Configuration
Add these values to your .env file:
BKASH_DISBURSEMENT_BASE_URL=https://api.paystation.com.bd BKASH_DISBURSEMENT_MERCHANT_ID=your_merchant_id BKASH_DISBURSEMENT_PASSWORD=your_hashed_password BKASH_DISBURSEMENT_PORTAL_KEY=your_portal_key
Optional configuration (with defaults):
BKASH_DISBURSEMENT_TIMEOUT=30 BKASH_DISBURSEMENT_CONNECT_TIMEOUT=10 BKASH_DISBURSEMENT_RETRY_TIMES=1 BKASH_DISBURSEMENT_RETRY_SLEEP=200
| Variable | Required | Default | Description |
|---|---|---|---|
BKASH_DISBURSEMENT_BASE_URL |
No | https://api.paystation.com.bd |
API base URL |
BKASH_DISBURSEMENT_MERCHANT_ID |
Yes | - | Your merchant/store ID |
BKASH_DISBURSEMENT_PASSWORD |
Yes | - | Pre-hashed password |
BKASH_DISBURSEMENT_PORTAL_KEY |
Yes | - | Portal authentication key |
BKASH_DISBURSEMENT_TIMEOUT |
No | 30 |
Request timeout (seconds) |
BKASH_DISBURSEMENT_CONNECT_TIMEOUT |
No | 10 |
Connection timeout (seconds) |
BKASH_DISBURSEMENT_RETRY_TIMES |
No | 1 |
Number of retry attempts |
BKASH_DISBURSEMENT_RETRY_SLEEP |
No | 200 |
Delay between retries (milliseconds) |
Note: The API requires the
passwordto be sent in hashed form. The package does not hash it for you -- store the pre-hashed value in your environment.
Usage
Disbursement request
Using the DisbursementPayload DTO:
use Mainul12501\BkashDisbursement\Data\DisbursementPayload; use Mainul12501\BkashDisbursement\Facades\BkashDisbursement; $response = BkashDisbursement::disburse(new DisbursementPayload( invoiceNumber: '1213456789199973', bankType: 1, bankId: 2, accountNumber: '01726315133', amount: '20.00', )); $response->transactionId; // 73KIO6OC $response->invoiceNumber; // 1213456789199973 $response->amount; // 20 $response->charge; // 0.14 $response->transactionTime; // 2026-01-15 12:30:00 $response->message; // The amount has been disbursed successfully
You can also pass a plain array:
$response = BkashDisbursement::disburse([ 'invoice_number' => '1213456789199973', 'bank_type' => 1, 'bank_id' => 2, 'acc_no' => '01726315133', 'amount' => '20.00', ]);
Transaction status
use Mainul12501\BkashDisbursement\Facades\BkashDisbursement; $status = BkashDisbursement::status('178881732008099'); if ($status->isSuccessful()) { // Disbursement completed successfully. $status->transactionId; $status->amount; } if ($status->isNotFound()) { // No transaction matched the invoice number. } $status->message;
Dependency injection
use Mainul12501\BkashDisbursement\Contracts\BkashDisbursementClient; class CheckDisbursementStatusAction { public function __construct( private readonly BkashDisbursementClient $client, ) { } public function handle(string $invoiceNumber): string { return $this->client->status($invoiceNumber)->message; } }
Runtime credential overrides
Useful when your application serves multiple merchants:
use Mainul12501\BkashDisbursement\Contracts\BkashDisbursementClient; $client = app(BkashDisbursementClient::class) ->withCredentials( merchantId: 'another-merchant-id', password: 'another-hashed-password', requestFromMerchantPortalKey: 'another-portal-key', ); $response = $client->disburse([ 'invoice_number' => 'INV-2026-0001', 'bank_type' => 1, 'bank_id' => 2, 'acc_no' => '01726315133', 'amount' => '20.00', ]);
Error handling
The package throws specific exceptions so callers can handle failures precisely:
use Mainul12501\BkashDisbursement\Exceptions\ApiException; use Mainul12501\BkashDisbursement\Exceptions\HttpRequestException; use Mainul12501\BkashDisbursement\Exceptions\InvalidConfigurationException; use Mainul12501\BkashDisbursement\Exceptions\RequestValidationException; use Mainul12501\BkashDisbursement\Exceptions\UnexpectedResponseException; use Mainul12501\BkashDisbursement\Facades\BkashDisbursement; try { $response = BkashDisbursement::disburse([ 'invoice_number' => 'INV-2026-0001', 'bank_type' => 1, 'bank_id' => 2, 'acc_no' => '01726315133', 'amount' => '20.00', ]); } catch (RequestValidationException $e) { // Local payload validation failed before the HTTP request was sent. $errors = $e->errors; // array keyed by field name } catch (InvalidConfigurationException $e) { // Required credentials are missing from config or runtime overrides. $message = $e->getMessage(); } catch (HttpRequestException $e) { // The remote API returned a non-2xx response or could not be reached. $status = $e->httpStatus; // ?int $body = $e->responseBody; // ?string } catch (ApiException $e) { // The remote API returned an application-level failure (e.g. duplicate invoice). $apiCode = $e->apiStatusCode; // ?string $apiResponse = $e->response; // array } catch (UnexpectedResponseException $e) { // The remote API returned a malformed or non-JSON response. $body = $e->responseBody; // ?string }
Exception hierarchy
RuntimeException
└── BkashDisbursementException
├── RequestValidationException
├── InvalidConfigurationException
├── HttpRequestException
├── ApiException
└── UnexpectedResponseException
| Exception | When it's thrown | Key properties |
|---|---|---|
RequestValidationException |
Payload validation fails before the HTTP request | errors (array) |
InvalidConfigurationException |
Required credentials are missing | - |
HttpRequestException |
Non-2xx HTTP response or connection failure | httpStatus, responseBody |
ApiException |
API returns an application-level error payload | apiStatusCode, response |
UnexpectedResponseException |
API returns non-JSON or malformed response | responseBody |
Response objects
DisbursementResponse
Returned by the disburse() method.
| Property | Type | Description |
|---|---|---|
statusCode |
string |
API status code |
status |
string |
Status label (e.g. success) |
invoiceNumber |
string |
The invoice number sent in the request |
amount |
string |
Disbursed amount |
charge |
string |
Transaction charge |
transactionId |
string |
Unique transaction identifier |
transactionTime |
?string |
Timestamp of the transaction |
message |
string |
Human-readable status message |
DisbursementStatusResponse
Returned by the status() method.
| Property | Type | Description |
|---|---|---|
statusCode |
string |
API status code |
status |
string |
Status label |
transactionStatus |
string |
Transaction status (success or not_found) |
invoiceNumber |
?string |
The invoice number |
amount |
?string |
Disbursed amount |
transactionId |
?string |
Unique transaction identifier |
transactionTime |
?string |
Timestamp of the transaction |
message |
string |
Human-readable status message |
Helper methods:
| Method | Returns | Description |
|---|---|---|
isSuccessful() |
bool |
true if transactionStatus === 'success' |
isNotFound() |
bool |
true if transactionStatus === 'not_found' |
Validation rules
The DisbursementPayload is validated before each request:
| Field | Rules |
|---|---|
invoice_number |
required, string |
bank_type |
required, integer, must be 1 |
bank_id |
required, integer, min 1 |
acc_no |
required, string |
amount |
required, numeric, min 20 |
Testing
Run the package tests with:
composer test
API reference
The package was implemented from the PayStation bKash disbursement API documentation. The two supported endpoints are:
- Disbursement request:
POST https://api.paystation.com.bd/merchant/disbursement/request - Status check:
POST https://api.paystation.com.bd/merchant/disbursement/status
Request headers (sent automatically by the package):
| Header | Required for |
|---|---|
merchantId |
Both endpoints |
password |
Both endpoints |
requestFromMerchantPortalKey |
Disbursement request only |
Disbursement form fields: invoice_number, bank_type, bank_id, acc_no, amount
Status form fields: invoice_number
License
This package is open-sourced software licensed under the MIT license.