raiyansarker / sslcommerz-sdk
A modern, typesafe PHP SDK for SSLCommerz Payment Gateway using Saloon PHP.
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.8
- saloonphp/saloon: ^4.0
Requires (Dev)
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2026-04-14 08:53:25 UTC
README
⚠️ Experimental This package is under active development. The implementation is based on the official v4 documentation but has not been exhaustively tested against the live API. Breaking changes may occur between releases. Test thoroughly in sandbox mode before deploying to production.
A modern, typesafe PHP SDK for SSLCommerz Payment Gateway, built on top of Saloon PHP.
Features
- Modern PHP: Requires PHP 8.3 or higher.
- Typesafe: Uses DTOs and custom response classes.
- Saloon PHP: Leverages the power of Saloon v4 for API requests.
- Testing: Built-in support for mocking and testing with Pest v4.
- PHPStan: Analyzed at
maxlevel for maximum reliability.
Installation
composer require raiyansarker/sslcommerz-sdk
Usage
Initialize Connector
use RaiyanSarker\SSLCommerz\SSLCommerzConnector; $connector = new SSLCommerzConnector( storeId: 'your_store_id', storePassword: 'your_store_password', isSandbox: true // Set to false for live );
Initialize Payment
use RaiyanSarker\SSLCommerz\Data\PaymentData; $paymentData = new PaymentData( totalAmount: 100.00, currency: 'BDT', transactionId: 'TRANS_123456', successUrl: 'https://your-domain.com/success', failUrl: 'https://your-domain.com/fail', cancelUrl: 'https://your-domain.com/cancel', customerName: 'John Doe', customerEmail: 'john@example.com', customerAddress1: 'Dhaka', customerCity: 'Dhaka', customerCountry: 'Bangladesh', customerPhone: '01700000000', customerPostcode: '1234', productName: 'Test Product', productCategory: 'Electronics' ); $response = $connector->initializePayment($paymentData); if ($response->isSuccess()) { $gatewayUrl = $response->getGatewayPageURL(); // Redirect user to $gatewayUrl } else { echo "Error: " . $response->getFailedReason(); }
Validate Payment
$valId = $_POST['val_id']; // From SSLCommerz callback // ⚠️ Never trust the raw POST data from SSLCommerz callbacks directly. // Always validate the val_id against the SSLCommerz Validation API. $response = $connector->validatePayment($valId); if ($response->isValid()) { // ⚠️ Always verify the amount and currency match your order records. $transactionId = $response->getTransactionId(); $amount = $response->getAmount(); $currency = $response->getCurrency(); // ⚠️ Check risk level before fulfilling. 0 = safe, 1 = risky. if ($response->getRiskLevel() === 1) { // Hold the order and review manually } // Update your database }
Query Transaction
$response = $connector->queryTransaction('TRANS_123456'); if ($response->isSuccess()) { $transaction = $response->getFirstTransaction(); // Handle transaction details }
Refund Transaction
$response = $connector->refundTransaction( refundAmount: 50.00, bankTransactionId: 'BANK_TRAN_ID', refundTransactionId: 'REFUND_TRAN_' . uniqid(), referenceTransactionId: 'TRANS_123456', refundRemarks: 'Customer requested refund' ); if ($response->isSuccess()) { echo "Refund initiated: " . $response->getRefundReferenceId(); }
Laravel Integration
The SSLCommerz PHP SDK provides seamless integration with Laravel through a built-in Service Provider and Facade.
1. Installation & Auto-Discovery
The SDK uses Laravel's package auto-discovery. Once you install the package via Composer, the Service Provider and SSLCommerz Facade will be registered automatically.
2. Configuration
Publish the default configuration file to config/sslcommerz.php:
php artisan vendor:publish --tag="sslcommerz-config"
Environment Variables
Configure your SSLCommerz credentials in your .env file:
SSLCOMMERZ_STORE_ID=your_store_id SSLCOMMERZ_STORE_PASSWORD=your_store_password SSLCOMMERZ_SANDBOX=true
Configuration Options
The published config/sslcommerz.php file allows you to manage:
| Option | Environment Variable | Default | Description |
|---|---|---|---|
store_id |
SSLCOMMERZ_STORE_ID |
null |
Your SSLCommerz Store ID. |
store_password |
SSLCOMMERZ_STORE_PASSWORD |
null |
Your SSLCommerz Store Password. |
sandbox |
SSLCOMMERZ_SANDBOX |
true |
Set to false for production (live) mode. |
3. Usage
You can interact with the SDK using either the SSLCommerz Facade or by injecting the SSLCommerzConnector.
Using the Facade
The Facade provides a static interface to all connector methods:
use RaiyanSarker\SSLCommerz\Laravel\Facades\SSLCommerz; use RaiyanSarker\SSLCommerz\Data\PaymentData; public function initiatePayment() { $paymentData = new PaymentData( totalAmount: 100.00, currency: 'BDT', transactionId: 'TXN_' . uniqid(), successUrl: route('payment.success'), failUrl: route('payment.fail'), cancelUrl: route('payment.cancel'), customerName: 'John Doe', customerEmail: 'john@example.com', customerAddress1: 'Dhaka', customerCity: 'Dhaka', customerCountry: 'Bangladesh', customerPhone: '01700000000', customerPostcode: '1207', productName: 'Test Product', productCategory: 'General', ); $response = SSLCommerz::initializePayment($paymentData); if ($response->isSuccess()) { return redirect()->away($response->getGatewayPageURL()); } return back()->with('error', $response->getFailedReason()); }
Dependency Injection
The SSLCommerzConnector is bound as a singleton in the Laravel service container:
use RaiyanSarker\SSLCommerz\SSLCommerzConnector; use RaiyanSarker\SSLCommerz\Data\PaymentData; public function handleCallback(Request $request, SSLCommerzConnector $connector) { $response = $connector->validatePayment($request->input('val_id')); if ($response->isValid()) { // Handle successful payment } }
4. Handling Callbacks & CSRF
SSLCommerz requires three mandatory callback URLs (Success, Fail, and Cancel) and an optional IPN (Instant Payment Notification) URL.
Define Routes
SSLCommerz sends a POST request to these URLs. Define them in your routes/web.php:
use App\Http\Controllers\PaymentController; use Illuminate\Support\Facades\Route; Route::post('/payment/success', [PaymentController::class, 'success']); Route::post('/payment/fail', [PaymentController::class, 'fail']); Route::post('/payment/cancel', [PaymentController::class, 'cancel']); Route::post('/payment/ipn', [PaymentController::class, 'ipn']);
Exclude from CSRF Protection
Since SSLCommerz sends external POST requests, you must exclude these routes from Laravel's CSRF protection.
For Laravel 11+ (in bootstrap/app.php):
->withMiddleware(function (Middleware $middleware) { $middleware->validateCsrfTokens(except: [ '/payment/success', '/payment/fail', '/payment/cancel', '/payment/ipn', ]); })
For older Laravel versions (in app/Http/Middleware/VerifyCsrfToken.php):
protected $except = [ '/payment/success', '/payment/fail', '/payment/cancel', '/payment/ipn', ];
Handling the Callback in a Controller
namespace App\Http\Controllers; use Illuminate\Http\Request; use RaiyanSarker\SSLCommerz\Laravel\Facades\SSLCommerz; class PaymentController extends Controller { public function success(Request $request) { $validationId = $request->input('val_id'); $response = SSLCommerz::validatePayment($validationId); if ($response->isValid()) { // ⚠️ Always verify amount and currency match your order records. // ⚠️ Check risk level before fulfilling. 0 = safe, 1 = risky. if ($response->getRiskLevel() === 1) { // Hold the order and review manually return redirect()->route('checkout')->with('error', 'Payment is under review.'); } $transactionId = $response->getTransactionId(); // Update your database, mark order as paid return redirect()->route('order.complete', ['id' => $transactionId]); } return redirect()->route('checkout')->with('error', 'Payment validation failed.'); } public function fail(Request $request) { return redirect()->route('checkout')->with('error', 'Payment failed.'); } public function cancel(Request $request) { return redirect()->route('checkout')->with('info', 'Payment cancelled.'); } public function ipn(Request $request) { // IPN is sent in the background by SSLCommerz $validationId = $request->input('val_id'); $status = $request->input('status'); if ($status === 'VALID' || $status === 'VALIDATED') { $response = SSLCommerz::validatePayment($validationId); if ($response->isValid()) { // Update your database using $response->getTransactionId() // It is recommended to check if the order is already marked as paid } } return response()->json(['status' => 'OK']); } }
5. Testing & Mocking in Laravel
When writing tests for your Laravel application, you can use the SSLCommerz Facade's built-in mocking capabilities or Saloon's mocking features.
Mocking with Facade
use RaiyanSarker\SSLCommerz\Laravel\Facades\SSLCommerz; use RaiyanSarker\SSLCommerz\Responses\PaymentInitializationResponse; test('it redirects to gateway on successful initialization', function () { SSLCommerz::shouldReceive('initializePayment') ->once() ->andReturn(/* mock response object */); $response = $this->post('/checkout'); $response->assertRedirect(); });
Mocking with Saloon
Since the SDK is built on Saloon, you can use Saloon::fake() for more granular control:
use Saloon\Http\Faking\MockResponse; use Saloon\Laravel\Facades\Saloon; use RaiyanSarker\SSLCommerz\Requests\InitializePaymentRequest; Saloon::fake([ InitializePaymentRequest::class => MockResponse::make(['status' => 'SUCCESS', 'GatewayPageURL' => 'https://sandbox.sslcommerz.com/gw'], 200), ]);
Testing
The project uses Pest for testing. Tests are divided into:
tests/Unit: For isolated logic (e.g., DTO transformations).tests/Feature: For integration tests and API request/response validation.
Run tests with Pest:
composer test
Run static analysis with PHPStan:
composer stan
Run both:
composer all
Resources
- Postman Collection: A comprehensive Postman collection is available in
resources/postman/SSLCommerz.postman_collection.jsonto help you explore and test the API endpoints.
Credits
- Special thanks to sumoncse19 for providing the original Postman collection for the SSLCommerz API.
License
MIT