PayBridge — Unified Payment Gateway for PHP & Laravel
A modular, extensible payment processing package that combines 16 payment gateways into one unified API system utilizing the Strategy Pattern. Works seamlessly across Raw PHP, WordPress, CodeIgniter, Symfony, and Laravel (with auto-discovery and interactive Admin Control Panel). Built with clean architecture, enterprise-ready features, and PSR-4 compatibility.
- Supported Gateways
- Installation Guide
- Quick Start — Raw PHP
- Quick Start — Laravel
- API Reference
- Admin Control Panel
- Customer Checkout Integration
- Adding a Custom Gateway
- Events
- Security Checklist
- Production Checklist
- Configuration Reference
- Contributing
- License
- Changelog
| Category | Gateway | Driver Code | Status |
|---|---|---|---|
| MFS | bKash Tokenized | bkash_tokenize |
✅ Full |
| MFS | bKash PWG / URL Checkout | bkash_pwg |
✅ Full |
| MFS | Nagad | nagad |
✅ Full |
| MFS | DBBL Rocket | rocket |
✅ Full |
| MFS | UCB Upay | upay |
✅ Full |
| QR | Bangla QR (National Interoperable) | bangla_qr |
✅ Full |
| Crypto | Binance Pay | binance_pay |
✅ Full |
| Crypto | NOWPayments (100+ Cryptos) | nowpayments |
✅ Full |
| Aggregator | SSLCommerz | sslcommerz |
✅ Full |
| Aggregator | AamarPay | aamarpay |
✅ Full |
| Aggregator | SurjoPay | surjopay |
✅ Full |
| Aggregator | PortPay | portpay |
✅ Full |
| Bank | Sonali Pay | sonalipay |
✅ Full |
| Bank | Easy Payment System (EPS) | eps |
✅ Full |
| International | Stripe | stripe |
✅ Full |
| International | PayPal | paypal |
✅ Full |
- PHP
>= 8.2 - PHP Extensions:
curl,openssl,json,bcmath(recommended) - Laravel 10, 11, or 12 (optional — works without Laravel)
composer require codepagol/pay-bridgeFor local/path-based development, add to your main project's composer.json:
"repositories": [
{
"type": "path",
"url": "path/to/PayBridge"
}
],
"require": {
"codepagol/pay-bridge": "@dev"
}Then run composer update codepagol/pay-bridge.
The service provider PayBridge\Payment\Providers\PaymentServiceProvider and facade alias PayBridge are auto-discovered by Laravel.
# Publish configuration file
php artisan vendor:publish --tag=payment-config
# Run database migration
php artisan migrate
# (Optional) Publish admin views for customization
php artisan vendor:publish --tag=payment-viewsphp artisan route:list | grep payment-gatewaysYou should see the /admin/payment-gateways routes registered.
require_once 'vendor/autoload.php';
use PayBridge\Payment\PayBridge;
// Initialize any of the 16 gateways with its credentials array:
$gateway = PayBridge::make('bkash_tokenize', [
'app_key' => 'your_bkash_app_key',
'app_secret' => 'your_bkash_app_secret',
'username' => 'your_bkash_username',
'password' => 'your_bkash_password',
'sandbox' => true,
'callback_url' => 'https://yourdomain.com/callback.php',
]);
// Initiate payment:
$response = $gateway->pay([
'amount' => 500.00,
'transaction_id' => 'INV_' . time(),
'currency' => 'BDT',
]);
if ($response['success']) {
header('Location: ' . $response['redirect_url']);
exit;
} else {
echo "Payment error: " . $response['message'];
}use PayBridge\Payment\Facades\PayBridge;
// Uses the default driver from PAY_BRIDGE_DRIVER env variable
$response = PayBridge::driver()->pay([
'amount' => 100,
'transaction_id' => uniqid(),
'customer_name' => 'John Doe',
'customer_email' => 'john@example.com',
'customer_phone' => '01711111111',
]);
if ($response['success']) {
return redirect()->away($response['redirect_url']);
}
return back()->with('error', $response['message']);Explicitly specify a gateway:
// bKash Tokenize
$response = PayBridge::driver('bkash_tokenize')->pay([...]);
// Crypto Payment with Binance Pay
$response = PayBridge::driver('binance_pay')->pay([
'amount' => 50.00,
'currency' => 'USDT',
'transaction_id' => uniqid('bp_'),
'product_name' => 'VIP Membership',
]);
// Bangla QR (Scannable by bKash, Nagad, Rocket, etc.)
$response = PayBridge::driver('bangla_qr')->pay([
'amount' => 1250.00,
'transaction_id' => uniqid('bqr_'),
'product_name' => 'Online Purchase',
]);
// Render QR: <img src="{{ $response['qr_image_url'] }}" alt="Bangla QR">Every gateway driver implements 4 standardized methods through the PaymentGatewayInterface:
$response = $gateway->pay([
'amount' => 1250.00,
'currency' => 'BDT', // or 'USD', 'USDT'
'transaction_id' => 'TXN_' . uniqid(),
'product_name' => 'Order #1001', // optional
'customer_name' => 'Jane Doe', // optional
'customer_email' => 'jane@test.com',// optional
'customer_phone' => '01700000000', // optional
]);Response:
[
'success' => true,
'message' => 'Payment initiated successfully',
'transaction_id' => 'TXN_abc123',
'redirect_url' => 'https://gateway.example.com/checkout/...',
'amount' => null,
'currency' => null,
'raw_response' => [...],
]Called in your callback handler after the customer returns from the gateway:
// Laravel
$result = PayBridge::driver('sslcommerz')->verify($request->all());
// Raw PHP
$result = $gateway->verify($_POST);
if ($result['success']) {
// Payment verified — safe to fulfill order
$verifiedAmount = $result['amount'];
$currency = $result['currency'];
$transactionId = $result['transaction_id'];
}$result = PayBridge::driver('sslcommerz')->refund('TXN_abc123');
if ($result['success']) {
// Refund processed
}Used to process server-to-server notifications from gateways:
// In your webhook controller
public function handleWebhook(Request $request, string $gateway)
{
$result = PayBridge::driver($gateway)->webhook($request->all());
if ($result['success']) {
// Webhook signature verified — process the notification
return response()->json(['status' => 'ok']);
}
return response()->json(['error' => $result['message']], 400);
}Important: Webhook routes must be excluded from CSRF verification. See Troubleshooting.
PayBridge includes a complete, browser-based Admin Settings UI so non-developers can manage gateways without touching code or .env:
- Visit
/admin/payment-gatewaysin your browser. - Toggle Gateways On/Off: One click to enable or disable any gateway.
- Configure API Keys & Passwords: Input Store IDs, App Keys, and Secrets directly. All credentials are automatically encrypted in the database.
- Sandbox / Live Toggle: Switch between testing and production per gateway.
- Set Primary Default: Mark your default gateway with a single click.
use PayBridge\Payment\Facades\PayBridge;
// Returns only gateways where is_active = true
$activeGateways = PayBridge::getActiveGateways();By default, PayBridge enforces ['web', 'auth'] middleware. Customize in config/payment.php:
'admin' => [
'enabled' => true, // Set false to disable admin UI entirely
'prefix' => env('PAY_BRIDGE_ADMIN_PREFIX', 'admin/payment-gateways'),
'middleware' => ['web', 'auth'],
],'middleware' => ['web', 'auth', 'role:admin|super-admin'],'middleware' => ['web', 'auth', 'can:manage-payments'],'middleware' => ['web', 'auth', 'is_admin'],'middleware' => ['web', 'auth:admin'],PAY_BRIDGE_ADMIN_PREFIX=dashboard/settings/paymentsFor headless/API-only projects:
PAY_BRIDGE_ADMIN_ENABLED=falseuse PayBridge\Payment\Facades\PayBridge;
class CheckoutController extends Controller
{
public function showCheckout()
{
$activeGateways = PayBridge::getActiveGateways();
return view('checkout', [
'gateways' => $activeGateways,
'orderTotal' => 1250.00,
]);
}
}<form action="{{ route('checkout.process') }}" method="POST">
@csrf
<h3>Select Payment Method:</h3>
@foreach($gateways as $gateway)
<label>
<input type="radio" name="payment_gateway"
value="{{ $gateway['code'] }}"
{{ $gateway['is_default'] ? 'checked' : '' }}>
<strong>{{ $gateway['name'] }}</strong>
<span>{{ $gateway['category'] }}</span>
</label>
@endforeach
<button type="submit">Proceed to Payment (৳1250)</button>
</form>public function processCheckout(Request $request)
{
$gateway = $request->input('payment_gateway', 'sslcommerz');
$response = PayBridge::driver($gateway)->pay([
'amount' => 1250.00,
'currency' => ($gateway === 'binance_pay') ? 'USDT' : 'BDT',
'transaction_id' => 'TXN_' . uniqid(),
'product_name' => 'Order #1001',
'customer_name' => auth()->user()->name ?? 'Customer',
'customer_email' => auth()->user()->email ?? 'customer@example.com',
'customer_phone' => '01711111111',
]);
if (!$response['success']) {
return back()->with('error', $response['message']);
}
// Bangla QR: Display QR code directly
if ($gateway === 'bangla_qr' && !empty($response['qr_image_url'])) {
return view('checkout.bangla-qr', [
'qrImageUrl' => $response['qr_image_url'],
'tranId' => $response['transaction_id'],
'amount' => 1250.00,
]);
}
// Standard: Redirect to hosted checkout
return redirect()->away($response['redirect_url']);
}- Create a class extending
PayBridge\Payment\Drivers\AbstractGatewayDriver:
namespace App\Gateway;
use PayBridge\Payment\Drivers\AbstractGatewayDriver;
class MyCustomDriver extends AbstractGatewayDriver
{
public function pay(array $data): array
{
// Your implementation
return $this->formatResponse(true, 'Payment initiated', $data['transaction_id'] ?? null, 'https://...');
}
public function verify(array $data): array { /* ... */ }
public function refund(string $transactionId): array { /* ... */ }
public function webhook(array $payload): array { /* ... */ }
}- Register in
config/payment.php:
'custom_gateway' => [
'class' => \App\Gateway\MyCustomDriver::class,
'api_key' => env('CUSTOM_GATEWAY_API_KEY', ''),
]- Use it:
$response = PayBridge::driver('custom_gateway')->pay([...]);PayBridge fires standard Laravel events during transactions:
| Event | Fired When |
|---|---|
PayBridge\Payment\Events\PaymentSuccess |
Payment succeeds (with transactionId & payload) |
PayBridge\Payment\Events\PaymentFailed |
Payment fails (with transactionId, error & payload) |
// In EventServiceProvider or Listener
use PayBridge\Payment\Events\PaymentSuccess;
use PayBridge\Payment\Events\PaymentFailed;
protected $listen = [
PaymentSuccess::class => [
\App\Listeners\LogSuccessfulPayment::class,
],
PaymentFailed::class => [
\App\Listeners\LogFailedPayment::class,
],
];// App\Listeners\LogSuccessfulPayment.php
class LogSuccessfulPayment
{
public function handle(PaymentSuccess $event): void
{
\Log::info("Payment success: {$event->transactionId}", $event->payload);
// Update order status, send receipt email, etc.
}
}Events are dispatched safely — they gracefully degrade in non-Laravel environments.
- CSRF Exclusion: Add callback/webhook URLs to the
$exceptarray inVerifyCsrfTokensince gateways send external POST requests. - Webhook Verification: Always verify webhook signatures via the
webhook()driver method to prevent spoofed notifications. - Price Tampering: Always verify the returned amount matches your database record. Never trust client-side amounts.
- HTTPS Only: Ensure all live endpoints use HTTPS. Most gateways reject non-HTTPS webhook URLs in production.
- Data Validation: Validate all incoming data before passing to
->pay(...). - APP_KEY: Back up your Laravel
APP_KEY— it's used to encrypt database credentials.
- Set
PAY_BRIDGE_DRIVERin.env. - Switch sandbox to
falsefor your active gateway (e.g.,SSLCZ_SANDBOX=false). - Verify real API Keys and Secret Keys are accurately configured.
- Ensure SSL verification is active (
'ssl_verify' => trueis the default). - Test end-to-end with a small real transaction.
- Protect the Admin Panel with role-based middleware for production.
- Set up idempotent webhook handlers with
lockForUpdate()to prevent double-processing.
All gateway credentials can be configured via:
- Admin Control Panel (database-first, highest priority)
.envfile (fallback when no database record exists)config/payment.php(hardcoded defaults)
# Default Payment Gateway
PAY_BRIDGE_DRIVER=sslcommerz
# Admin Panel
PAY_BRIDGE_ADMIN_PREFIX=admin/payment-gateways
PAY_BRIDGE_ADMIN_ENABLED=true
# Gateway Credentials (examples)
SSLCZ_STORE_ID=your_store_id
SSLCZ_STORE_PASSWORD=your_store_password
SSLCZ_SANDBOX=true
BKASH_TOKENIZE_APP_KEY=your_app_key
BKASH_TOKENIZE_APP_SECRET=your_app_secret
BKASH_TOKENIZE_USERNAME=your_username
BKASH_TOKENIZE_PASSWORD=your_password
BKASH_TOKENIZE_SANDBOX=true
STRIPE_PUBLIC_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
BINANCE_PAY_API_KEY=your_cert_sn
BINANCE_PAY_SECRET_KEY=your_secretSee config/payment.php for the complete list of all 16 gateway variables.
Contributions are welcome! Here's how to get started:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-new-gateway - Commit your changes:
git commit -m "Add MyGateway driver" - Push to the branch:
git push origin feature/my-new-gateway - Open a Pull Request
- Follow PSR-4 autoloading and PSR-12 coding standards
- All new gateway drivers must extend
AbstractGatewayDriverand implementPaymentGatewayInterface - Add gateway field definitions to
PaymentGatewaySetting::getFieldDefinitions() - Register the driver in both
PayBridge::$drivers(standalone) andPaymentManager(Laravel) - Write clear docblocks for all public methods
PayBridge is open-source software licensed under the MIT License.
- Bug Fix: Removed duplicate
surjopayconfig block inconfig/payment.php. - Dependency Optimization: Removed unused
ramsey/uuiddependency fromcomposer.jsonand updated lockfile. - Facade Alignment: Standardized Facade namespace and class as
PayBridge\Payment\Facades\PayBridge. - Admin Control: Added
PAY_BRIDGE_ADMIN_ENABLEDconfig flag to conditionally load admin routes and views. - Import Cleanup: Removed unused
Illuminate\Support\Strimports from driver classes. - Documentation: Completed full documentation suite (all 7 guide chapters + updated README).
- 16 payment gateways with unified
pay(),verify(),refund(),webhook()API - No-code Admin Control Panel with encrypted credential storage
- Support for Raw PHP, WordPress, CodeIgniter, Symfony, and Laravel
- Dynamic customer checkout with Bangla QR display
- Cryptocurrency payments via Binance Pay and NOWPayments
- Bank-grade security: encrypted credentials, constant-time signature verification, SSL enforcement
- Configurable admin panel with role-based access control
For detailed guides on each gateway, security best practices, and troubleshooting: