Home / Docs / Integration guide
BDPayGate integration
Install
composer require byteminus/laravel-bdpaygate
php artisan vendor:publish --tag=bdpaygate-config
php artisan migrate
Schedule the settlement command:
// routes/console.php
Schedule::command('bdpaygate:sync')->everyTenMinutes();
.env per gateway
| Gateway | Variables |
|---|---|
| bKash | BKASH_APP_KEY, BKASH_APP_SECRET, BKASH_USERNAME, BKASH_PASSWORD, BKASH_SANDBOX, BKASH_VERSION (v2 or v1.2) |
| SSLCommerz | SSLCOMMERZ_STORE_ID, SSLCOMMERZ_STORE_PASSWORD, SSLCOMMERZ_SANDBOX |
| ShurjoPay | SHURJOPAY_USERNAME, SHURJOPAY_PASSWORD, SHURJOPAY_PREFIX, SHURJOPAY_SANDBOX |
| aamarPay | AAMARPAY_STORE_ID, AAMARPAY_SIGNATURE_KEY, AAMARPAY_SANDBOX |
| Nagad | NAGAD_MERCHANT_ID, NAGAD_MERCHANT_NUMBER, NAGAD_PUBLIC_KEY, NAGAD_PRIVATE_KEY, NAGAD_SANDBOX, NAGAD_BASE_URL, NAGAD_PROXY |
| Upay | UPAY_MERCHANT_ID, UPAY_MERCHANT_KEY, UPAY_MERCHANT_CODE, UPAY_MERCHANT_NAME, UPAY_MERCHANT_MOBILE, UPAY_SANDBOX, UPAY_PROXY |
| PortPos | PORTPOS_APP_KEY, PORTPOS_SECRET_KEY, PORTPOS_SANDBOX |
| PayStation | PAYSTATION_MERCHANT_ID, PAYSTATION_PASSWORD, PAYSTATION_SANDBOX |
| UddoktaPay | UDDOKTAPAY_BASE_URL, UDDOKTAPAY_API_KEY |
| Moneybag | MONEYBAG_API_KEY, MONEYBAG_SANDBOX |
| EPS | EPS_USERNAME, EPS_PASSWORD, EPS_HASH_KEY, EPS_MERCHANT_ID, EPS_STORE_ID, EPS_SANDBOX |
| DGePay | DGEPAY_CLIENT_ID, DGEPAY_CLIENT_SECRET, DGEPAY_API_KEY, DGEPAY_SANDBOX |
Global: BDPAYGATE_GATEWAY (default gateway), BDPAYGATE_REDIRECT_SUCCESS|FAILED|CANCELLED|PENDING (route name or URL), BDPAYGATE_PROXY (fixed-IP egress for gateways that whitelist server IPs).
Capabilities
| Gateway | Refunds | Status sync |
|---|---|---|
| bKash | Full and partial (up to 10) | Yes |
| SSLCommerz | Full and partial (asynchronous) | Yes |
| PortPos | Full and partial (asynchronous) | Yes |
| Upay | Full only | Yes |
| ShurjoPay, aamarPay, PayStation, UddoktaPay, Nagad, EPS, DGePay | Merchant panel only | Yes |
| Moneybag | Merchant panel only | No (use verifyTransaction($payment, $transactionId)) |
Check in code with instanceof ByteMinus\BdPayGate\Contracts\SupportsRefunds, SupportsPartialRefunds, SupportsSync.
Payment lifecycle
charge()creates a pendingPaymentrow (one per attempt, with a freshgateway_reference) and returnscheckout_url.- The gateway calls
/bdpaygate/{gateway}/.... The package ignores what the callback claims and asks the gateway's API. - Confirmed paid and amount matches:
Completed,PaymentCompletedfires once. Confirmed declined:FailedorCancelled(these can still becomeCompletedif the gateway later confirms payment). No answer: staysPendinguntilbdpaygate:sync.
Payment fields to use: status (PaymentStatus enum), reference, amount, gateway_transaction_id, failure_reason, requires_review, refunded_amount, meta, paid_at. $payment->attempts() lists every attempt for the same reference.
Several stores of one gateway
// config/bdpaygate.php
'gateways' => [
'sslcommerz_retail' => ['driver' => 'sslcommerz', 'store_id' => env('RETAIL_STORE_ID'), 'store_password' => env('RETAIL_STORE_PASSWORD')],
'sslcommerz_b2b' => ['driver' => 'sslcommerz', 'store_id' => env('B2B_STORE_ID'), 'store_password' => env('B2B_STORE_PASSWORD')],
],
Troubleshooting
- Payment stuck in pending: the gateway did not answer when the callback arrived. Run
php artisan bdpaygate:sync, orBdPayGate::sync($payment). - Customer logged out after paying: the app added
webmiddleware to the package routes. Keepbdpaygate.routes.middlewareempty. - "Amount mismatch": the gateway reported a different amount than was charged; the payment is not completed. Check for coupons or currency settings.
- Nagad or Upay rejects calls in production: they whitelist server IPs. Use a fixed-IP server or set
NAGAD_PROXY/UPAY_PROXY. - Testing UddoktaPay locally: its sandbox drops custom ports from the return URL; use a tunnel instead of
localhost:8000. - Writing a custom gateway: extend
ByteMinus\BdPayGate\Drivers\AbstractGateway, register withBdPayGate::extend('name', fn ($app, array $config, string $name) => new MyGateway($config, $name)).