raiyansarker/sslcommerz-sdk

A modern, typesafe PHP SDK for SSLCommerz Payment Gateway using Saloon PHP.

Maintainers

Package info

github.com/raiyansarker/sslcommerz-sdk

pkg:composer/raiyansarker/sslcommerz-sdk

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-04-14 08:52 UTC

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 max level 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.json to 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