unipay / unipay-bd
Unified Bangladesh Payment Gateway & SaaS Billing Package for Laravel (bKash, Nagad, Rocket, Upay, CellFin, SSLCommerz, Shurjopay & aamarPay)
Requires
- php: ^8.1
- ext-json: *
- ext-openssl: *
- guzzlehttp/guzzle: ^7.5
- illuminate/contracts: ^10.0 || ^11.0
- illuminate/support: ^10.0 || ^11.0
Requires (Dev)
- orchestra/testbench: ^8.0 || ^9.0
- phpunit/phpunit: ^10.0 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-21 03:20:19 UTC
README
UniPay BD โ Unified Bangladesh Payment Gateway & SaaS Billing for Laravel
The ultimate unified Laravel payment gateway & SaaS recurring billing engine for Bangladeshi MFS & Card Gateways (bKash, Nagad, Rocket, Upay, CellFin, SSLCommerz, Shurjopay & aamarPay).
๐ Table of Contents
- Overview
- Supported Payment Gateways
- What's New in v2.0
- Requirements
- Installation
- Configuration
- Quick Start & Core Usage
- ๐ข SaaS & Multi-Tenancy Architecture
- ๐ Subscriptions & Recurring Billing (Cashier-Style)
- ๐ Hosted Payment Links
- ๐ Virtual Wallets & Credits
- ๐ฑ Currency Conversion & FX Buffering
- ๐ก๏ธ Anti-Fraud & Rate-Limiting (UniPayShield)
- ๐จ Drop-in Checkout Blade Component
- ๐ฉบ System Health Doctor CLI
- ๐งช Mock Gateway for Local Testing
- Extending with Custom Drivers
- Exception & Error Handling
- Testing
- Security
- License
๐ Overview
UniPay BD (unipay/unipay-bd) provides a seamless, developer-friendly driver-based payment integration and SaaS recurring subscription engine for Laravel applications operating in Bangladesh.
Inspired by Laravel's native driver pattern (FilesystemManager, CacheManager, Laravel Cashier), UniPay standardizes bKash, Nagad, Rocket, Upay, CellFin, SSLCommerz, Shurjopay, and aamarPay into an elegant, unified API syntax with complete support for Multi-Tenancy, Subscriptions, Wallets, FX Conversion, and Fraud Prevention.
๐ณ Supported Payment Gateways
UniPay BD natively supports 8 Bangladeshi payment providers + 1 offline mock driver:
| Gateway | Driver | Provider / Channel | Protocol & Security | Supported Operations |
|---|---|---|---|---|
| bKash | bkash |
bKash PGW | Tokenized REST API v1.2 with Bearer token caching | Single Payment, Agreement Recurring, Query, Refund |
| Nagad | nagad |
Nagad MFS | RSA OpenSSL Public Key Encryption & Private Key Signing | Create, Complete, Verify, Refund |
| Rocket | rocket |
Dutch-Bangla Bank (DBBL) | Merchant API v1 with Terminal ID | Create, Verify, Refund |
| Upay | upay |
UCB Fintech | Bearer Token REST API | Create, Verify, Refund |
| CellFin | cellfin |
Islami Bank (IBBL) | Reference ID Checkout API | Create, Verify, Refund |
| SSLCommerz | sslcommerz |
SSL Wireless | GWProcess v4 API & Validation API | Create, Validate, Refund |
| Shurjopay | shurjopay |
ShurjoMukhi | Shurjopay API v2 Tokenized | Create, Verify, Refund |
| aamarPay | aamarpay |
aamarPay PGW | Signature Key Secure JSON API | Create, Query, Refund |
| Mock Driver | mock |
Local Offline Test | Zero-credential sandbox simulation | Instant Create, Query, Refund |
๐ What's New in v2.0
- ๐ข Multi-Tenant SaaS Routing: Dynamically resolve tenant-specific merchant credentials using
Payment::forTenant($tenant)or runtimewithConfig(). Platform fee calculation (platform_fee,net_amount). - ๐ Cashier-Style Subscriptions: Built-in
Billabletrait on any Eloquent model (User,Team), bKash tokenized agreement billing, intervals (monthly,yearly), trial periods, and automated recurring billing worker (php artisan unipay:billing:process). - ๐ Hosted Payment Links: Generate shareable checkout URLs (
PaymentLink::create(...)) with custom expirations, viewable directly at/unipay/pay/{code}with drop-in checkout UI or JSON API. - ๐ Virtual Wallets: Integrated credit ledger (
WalletandWalletTransaction) for customer balance top-ups, deductions, and balance verification. - ๐ฑ Currency Conversion (FX): Automatic multi-currency conversion (
USD,EUR,GBPtoBDT) with configurable safety buffer markup. - ๐ก๏ธ UniPayShield Anti-Fraud: IP/Phone blacklist, velocity limiters, and rapid payment surge detection.
- ๐ณ aamarPay Gateway Driver: First-class support for aamarPay checkout, verification, and refunds.
- ๐งช Offline Mock Gateway: Effortless local development without internet access or sandbox credentials.
- ๐ฉบ Artisan Doctor: Diagnostics command
php artisan unipay:doctorchecking environment configs, migrations, PHP extensions, and gateway endpoints. - ๐จ Drop-in Blade UI:
<x-unipay-checkout />component with sleek styling for zero-friction front-end integration.
โ๏ธ Requirements
- PHP:
^8.1 || ^8.2 || ^8.3 - Laravel:
^10.0 || ^11.0 - PHP Extensions:
ext-json,ext-openssl,ext-curl
๐ฆ Installation
Install the package via Composer:
composer require unipay/unipay-bd
Run the Artisan installer to publish configuration and run database migrations:
php artisan unipay:install php artisan migrate
๐ง Configuration
Add your payment gateway credentials to your application's .env file:
# Default gateway (bkash, nagad, rocket, upay, cellfin, sslcommerz, shurjopay, aamarpay, mock) UNIPAY_DEFAULT_GATEWAY=bkash # bKash PGW BKASH_SANDBOX=true BKASH_APP_KEY=your_app_key BKASH_APP_SECRET=your_app_secret BKASH_USERNAME=your_username BKASH_PASSWORD=your_password # Nagad NAGAD_SANDBOX=true NAGAD_MERCHANT_ID=your_merchant_id NAGAD_MERCHANT_NUMBER=01700000000 NAGAD_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..." NAGAD_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..." # aamarPay AAMARPAY_SANDBOX=true AAMARPAY_STORE_ID=aamarpaytest AAMARPAY_SIGNATURE_KEY=dbb74894e82415a2f7ff0ec3a97e4183 # Currency FX (Optional for USD/EUR conversion) UNIPAY_CURRENCY_BUFFER_PERCENT=3.0
โก Quick Start & Core Usage
1. Fluent Payment Initiation
You can construct a payment using the fluent PaymentRequest::builder():
use Unipay\BD\Facades\Payment; use Unipay\BD\DTOs\PaymentRequest; $paymentRequest = PaymentRequest::builder() ->amount(1500.00, 'BDT') ->invoice('INV-' . time()) ->customer( mobile: '01712345678', email: 'customer@example.com', name: 'Rahim Uddin' ) ->coupon('PROMO20', 100.00) ->build(); // Initiate with default or specific gateway $response = Payment::driver('bkash')->createPayment($paymentRequest); if ($response->isSuccessful() || $response->isPending()) { // Redirect customer to payment portal return redirect()->away($response->redirectUrl); } return back()->withErrors(['message' => $response->message]);
2. Callbacks & Webhooks
UniPay automatically registers a unified callback route at /unipay/callback/{gateway}. When a customer finishes payment, the gateway redirects back to this endpoint, where UniPay verifies transaction status and dispatches events.
3. Listen to Payment Events
Decouple your order processing logic by listening to UniPay events in App\Providers\EventServiceProvider.php (or via Laravel 11 auto-discovery):
use Unipay\BD\Events\PaymentSucceeded; use Unipay\BD\Events\PaymentFailed; use Unipay\BD\Events\PaymentRefunded; protected $listen = [ PaymentSucceeded::class => [ \App\Listeners\MarkOrderAsPaid::class, ], PaymentFailed::class => [ \App\Listeners\HandleFailedPayment::class, ], ];
namespace App\Listeners; use Unipay\BD\Events\PaymentSucceeded; use App\Models\Order; class MarkOrderAsPaid { public function handle(PaymentSucceeded $event) { $response = $event->response; // PaymentResponse DTO $order = Order::where('invoice_id', $response->invoiceId)->first(); if ($order) { $order->update([ 'status' => 'paid', 'transaction_id' => $response->transactionId, 'gateway' => $response->gatewayName, ]); } } }
4. Process Refunds
Issue full or partial refunds directly:
use Unipay\BD\Facades\Payment; use Unipay\BD\DTOs\RefundRequest; $refundRequest = new RefundRequest( paymentId: 'PAY_12345678', transactionId: 'TRX_98765432', amount: 500.00, reason: 'Customer return request' ); $response = Payment::driver('bkash')->refund($refundRequest); if ($response->isSuccessful()) { // Refund complete $refundTrxId = $response->refundTransactionId; }
5. Query Payment Status
Query any transaction status explicitly at any time:
use Unipay\BD\Facades\Payment; $response = Payment::driver('nagad')->queryPayment('NAGAD_REF_1001'); if ($response->isSuccessful()) { $trxId = $response->transactionId; $amount = $response->amount; }
๐ข SaaS & Multi-Tenancy Architecture
UniPay BD v2.0 is built from the ground up to support multi-tenant SaaS applications where different tenants/vendors have their own merchant accounts or where platform fees must be retained.
Multi-Tenant Gateway Routing
Define a global tenant resolver in your AppServiceProvider:
use Unipay\BD\Facades\Payment; public function boot() { Payment::resolveTenantConfigUsing(function ($tenant, string $driver) { // Retrieve tenant's credentials from your database or encrypted settings return [ 'app_key' => $tenant->bkash_app_key, 'app_secret' => $tenant->bkash_app_secret, 'username' => $tenant->bkash_username, 'password' => $tenant->bkash_password, ]; }); }
Then initiate payments scoped to the tenant:
$tenant = Tenant::find(12); $response = Payment::forTenant($tenant) ->driver('bkash') ->createPayment($paymentRequest);
Runtime Gateway Config Overrides
You can also override credentials on the fly without using the tenant resolver:
$customBkash = Payment::driver('bkash')->withConfig([ 'app_key' => 'vendor_key_99', 'app_secret' => 'vendor_secret_99', 'username' => 'vendor_user', 'password' => 'vendor_pass', ]); $response = $customBkash->createPayment($paymentRequest);
๐ Subscriptions & Recurring Billing (Cashier-Style)
UniPay BD provides an expressive, Cashier-inspired recurring billing engine using bKash Tokenized Agreements or automatic wallet deductions.
Using the Billable Trait
Add the Billable trait to your User or Organization model:
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Unipay\BD\Traits\Billable; class User extends Authenticatable { use Billable; }
Create and Manage Subscriptions
$user = auth()->user(); // Create a monthly Pro subscription with a 7-day trial $subscription = $user->newSubscription('pro-plan', 999.00) ->monthly() ->trialDays(7) ->create(); // Check subscription status if ($user->subscribed('pro-plan')) { // User has an active or trialing subscription } // Check if user is currently in free trial if ($subscription->onTrial()) { $daysLeft = $subscription->trial_ends_at->diffInDays(now()); } // Cancel or pause subscription $subscription->cancel(); $subscription->pause(); $subscription->resume();
Automatic Billing Cron Worker
UniPay provides a console command to automatically charge all active subscriptions that are due for renewal:
php artisan unipay:billing:process
Schedule it in your routes/console.php (Laravel 11) or app/Console/Kernel.php (Laravel 10):
use Illuminate\Support\Facades\Schedule; Schedule::command('unipay:billing:process')->dailyAt('00:01');
๐ Hosted Payment Links
Create instant, shareable payment links for invoices, donations, or custom orders:
use Unipay\BD\Models\PaymentLink; $link = PaymentLink::create([ 'title' => 'Web Design Retainer - Invoice #1024', 'amount' => 15000.00, 'customer_name' => 'Tech Corp BD', 'customer_email' => 'finance@techcorp.com', 'expires_at' => now()->addDays(3), 'allowed_gateways' => ['bkash', 'nagad', 'aamarpay'], ]); // Send $link->url to the customer! // Example: https://yourapp.com/unipay/pay/Xy9K1z
When opened in a browser, customers see a clean checkout portal where they can pick a payment method. Alternatively, your frontend can fetch it via JSON:
curl https://yourapp.com/unipay/pay/Xy9K1z
๐ Virtual Wallets & Credits
Enable micro-transactions, customer top-ups, and in-app credit systems:
$wallet = $user->getWallet(); // Auto-creates wallet if not existing // Add balance $wallet->deposit(500.00, 'Top-up via bKash', ['payment_id' => 'PAY_888']); // Check balance if ($wallet->hasSufficientBalance(120.00)) { // Withdraw for purchase $wallet->withdraw(120.00, 'Purchased digital sticker pack'); } // Transaction ledger $history = $wallet->transactions()->latest()->get();
๐ฑ Currency Conversion & FX Buffering
If your SaaS charges in USD, EUR, or GBP, UniPay converts international currencies to BDT with an optional safety margin buffer:
use Unipay\BD\Services\CurrencyConverter; $converter = app(CurrencyConverter::class); // Converts 10.00 USD to BDT using current exchange rates + safety buffer $bdtAmount = $converter->convertToBdt(10.00, 'USD');
๐ก๏ธ Anti-Fraud & Rate-Limiting (UniPayShield)
Protect your platform from rapid bot carding and spam payments:
use Unipay\BD\Services\UniPayShield; $shield = app(UniPayShield::class); // Checks IP velocity, phone number fraud patterns, and blacklists $shield->protect( ip: request()->ip(), phone: '01712345678', amount: 50000.00 );
Throws \Unipay\BD\Exceptions\UnipayException if suspicious velocity or blacklisted identity is detected.
๐จ Drop-in Checkout Blade Component
Render a ready-to-use payment selector widget in any Blade template:
<x-unipay-checkout :amount="2500" currency="BDT" :gateways="['bkash', 'nagad', 'aamarpay', 'sslcommerz']" action="/checkout/process" />
๐ฉบ System Health Doctor CLI
Troubleshoot payment issues, verify migrations, validate SSL certificates, and check merchant credentials with a single command:
php artisan unipay:doctor
Sample output:
UniPay BD Diagnostic Report
---------------------------
PHP Version: 8.3.28 ........................................ [OK]
OpenSSL Extension: Enabled ................................ [OK]
JSON Extension: Enabled ................................... [OK]
Database Migrations: Up to date ........................... [OK]
bKash Gateway Config: Configured .......................... [OK]
Nagad Gateway Config: Keys Present ........................ [OK]
aamarPay Gateway Config: Configured ....................... [OK]
Doctor found 0 issues. Everything looks healthy!
๐งช Mock Gateway for Local Testing
Need to test your checkout flow in development or CI without active sandbox credentials?
Use the built-in mock driver:
UNIPAY_DEFAULT_GATEWAY=mock
Or programmatically:
$response = Payment::driver('mock')->createPayment($paymentRequest); // Immediately succeeds and provides mock redirect & transaction IDs $trxId = $response->transactionId;
๐ ๏ธ Extending with Custom Drivers
You can easily register custom gateway drivers using Payment::extend():
use Unipay\BD\Facades\Payment; Payment::extend('custom_mfs', function ($app) { return new CustomMfsGateway(config('unipay.gateways.custom_mfs')); });
โ ๏ธ Exception & Error Handling
UniPay provides a clean exception hierarchy:
Unipay\BD\Exceptions\UnipayException: Base package exception.Unipay\BD\Exceptions\InvalidGatewayException: Thrown when requesting an unsupported gateway.Unipay\BD\Exceptions\BkashApiException: Thrown when bKash returns API/credential errors.Unipay\BD\Exceptions\NagadEncryptionException: Thrown when OpenSSL RSA keys or signatures fail.Unipay\BD\Exceptions\PaymentVerificationFailedException: Thrown on failed checksums or invalid amounts.
๐งช Testing
UniPay BD includes a comprehensive test suite with 100% mocked HTTP and database coverage:
vendor/bin/phpunit
OK (60 tests, 237 assertions)
๐ Security
If you discover any security-related issues, please email atikhasan2090@gmail.com instead of using the public issue tracker.
๐ License
UniPay BD is open-sourced software licensed under the MIT License.
Crafted with โค๏ธ for the Bangladeshi Developer Community by Atik Hasan.