mainul/pay-station-disbursement

Laravel package for the PayStation bKash/Nagad/Rocket disbursement API.

Maintainers

Package info

github.com/Mainul12501/pay-station-disbursement

pkg:composer/mainul/pay-station-disbursement

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-01 19:37 UTC

This package is auto-updated.

Last update: 2026-05-01 19:41:48 UTC


README

License: MIT PHP Version Laravel

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, or 13.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 password to 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.