🧠 Mastering The Singleton Pattern In Laravel For Payment Gateways (Stripe, SSLCommerz, Bkash)

Singleton Laravel Payment Gateway
Profile Picture Joton Sutradharβ€’ πŸ“– 6 min read β€’ πŸ“… 24th June 2025

Heads up!

Check my blogs on Dev.to and Medium!

Integrating payment gateways like Stripe, SSLCommerz, or Bkash is a common need in web development. But if not done right, it can lead to messy, repeated code and poor resource management.

In this blog, we’ll explore how to implement a clean, scalable, and reusable payment system using the Singleton Pattern, Strategy Pattern, and Dependency Injection in Laravel.

πŸ“Œ What You’ll Learn

βœ… What is the Singleton Pattern
βœ… Why and when to use it in Laravel
βœ… How to integrate multiple payment gateways (Stripe, SSLCommerz, Bkash)
βœ… How to make your architecture SOLID, testable, and extensible
βœ… Full code example with folder structure

πŸ” What is the Singleton Pattern?

The Singleton Pattern ensures a class has only one instance throughout the application and provides a global access point to it.

Instead of creating a new object every time, we use one shared instance.

βœ… Why Use Singleton for Payment Gateways?

  • Avoids repeated SDK initialization

  • Saves memory and ensures consistent behavior

  • Centralizes and simplifies gateway logic

  • Works perfectly with Laravel’s Service Container

🧱 Recommended Folder Structure

To keep things modular and clean:

app/
β”œβ”€β”€ Gateways/
β”‚   β”œβ”€β”€ Contracts/
β”‚   β”‚   └── PaymentGatewayInterface.php
β”‚   β”œβ”€β”€ StripeGateway.php
β”‚   β”œβ”€β”€ SSLCommerzGateway.php
β”‚   └── BkashGateway.php
β”œβ”€β”€ Services/
β”‚   └── PaymentGatewayService.php

🧩 Step 1: Define the Payment Gateway Interface

app/Gateways/Contracts/PaymentGatewayInterface.php

namespace App\Gateways\Contracts;

interface PaymentGatewayInterface
{
    public function charge(float $amount, string $currency): array;
    public function refund(string $transactionId): array;
}

This defines a contract that all gateways (Stripe, SSLCommerz, Bkash) must follow.

πŸ’³ Step 2: Create Gateway Implementations

Each gateway implements the PaymentGatewayInterface.

πŸ”Ή Stripe Gateway

app/Gateways/StripeGateway.php

namespace App\Gateways;

use App\Gateways\Contracts\PaymentGatewayInterface;

class StripeGateway implements PaymentGatewayInterface
{
    protected $config;

    public function __construct()
    {
        $this->config = config('services.stripe');
    }

    public function charge(float $amount, string $currency): array
    {
        return [
            'gateway' => 'stripe',
            'status' => 'success',
            'transaction_id' => uniqid('stripe_txn_'),
            'amount' => $amount,
            'currency' => $currency,
        ];
    }

    public function refund(string $transactionId): array
    {
        return [
            'gateway' => 'stripe',
            'status' => 'refunded',
            'transaction_id' => $transactionId,
        ];
    }
}

πŸ”Ή SSLCommerz Gateway

app/Gateways/SSLCommerzGateway.php

namespace App\Gateways;

use App\Gateways\Contracts\PaymentGatewayInterface;

class SSLCommerzGateway implements PaymentGatewayInterface
{
    protected $config;

    public function __construct()
    {
        $this->config = config('services.sslcommerz');
    }

    public function charge(float $amount, string $currency): array
    {
        return [
            'gateway' => 'sslcommerz',
            'status' => 'success',
            'transaction_id' => uniqid('ssl_txn_'),
            'amount' => $amount,
            'currency' => $currency,
        ];
    }

    public function refund(string $transactionId): array
    {
        return [
            'gateway' => 'sslcommerz',
            'status' => 'refunded',
            'transaction_id' => $transactionId,
        ];
    }
}

πŸ”Ή Bkash Gateway

app/Gateways/BkashGateway.php

namespace App\Gateways;

use App\Gateways\Contracts\PaymentGatewayInterface;

class BkashGateway implements PaymentGatewayInterface
{
    protected $config;

    public function __construct()
    {
        $this->config = config('services.bkash');
    }

    public function charge(float $amount, string $currency): array
    {
        return [
            'gateway' => 'bkash',
            'status' => 'success',
            'transaction_id' => uniqid('bkash_txn_'),
            'amount' => $amount,
            'currency' => $currency,
        ];
    }

    public function refund(string $transactionId): array
    {
        return [
            'gateway' => 'bkash',
            'status' => 'refunded',
            'transaction_id' => $transactionId,
        ];
    }
}

πŸ› οΈ Step 3: Create the PaymentGatewayService (Singleton-Friendly)

app/Services/PaymentGatewayService.php

namespace App\Services;

use App\Gateways\Contracts\PaymentGatewayInterface;

class PaymentGatewayService
{
    protected PaymentGatewayInterface $gateway;

    public function __construct(PaymentGatewayInterface $gateway)
    {
        $this->gateway = $gateway;
    }

    public function charge(float $amount, string $currency = 'BDT'): array
    {
        return $this->gateway->charge($amount, $currency);
    }

    public function refund(string $transactionId): array
    {
        return $this->gateway->refund($transactionId);
    }
}

Now, Laravel will inject the gateway using Dependency Injection, making it testable and extensible.

🧩 Step 4: Register Bindings in Service Container

app/Providers/AppServiceProvider.php

use App\Gateways\Contracts\PaymentGatewayInterface;
use App\Gateways\StripeGateway;

public function register()
{
    // Bind the default gateway
    $this->app->bind(PaymentGatewayInterface::class, function () {
        return new StripeGateway(); // You can switch to SSLCommerzGateway or BkashGateway
    });

    $this->app->singleton(\App\Services\PaymentGatewayService::class);
}

You can make this dynamic using a config or based on request data if needed.

πŸ“Ÿ Step 5: Use in Controller (With DI)

use App\Services\PaymentGatewayService;

class PaymentController extends Controller
{
    public function pay(PaymentGatewayService $paymentService)
    {
        $response = $paymentService->charge(1500, 'BDT');
        return response()->json($response);
    }

    public function refund(PaymentGatewayService $paymentService, string $transactionId)
    {
        $response = $paymentService->refund($transactionId);
        return response()->json($response);
    }

    // Optional: Dynamically switch gateway
    public function payWithBkash()
    {
        $bkash = new \App\Gateways\BkashGateway();
        $payment = new PaymentGatewayService($bkash);
        return response()->json($payment->charge(2000));
    }
}

🧠 Benefits of This Architecture

Benefit Description
βœ… Singleton-Friendly Only one instance of each gateway client is created
βœ… Strategy Pattern Easily switch between Stripe, SSLCommerz, and Bkash
βœ… Laravel-native Uses DI and Service Container
βœ… Extensible Add more gateways without changing core logic
βœ… Testable Easily mock PaymentGatewayInterface in tests

🧾 Summary

Concept Description
Singleton Reuses a single instance of the gateway service
Interface All gateways follow a shared contract
Strategy Gateways are interchangeable at runtime
DI Laravel resolves dependencies automatically
Gateways Stripe, SSLCommerz, Bkash supported out of the box
Related Blogs
Clean Code, Scalable Systems: The Abstract Service Advantage In Laravel
PHP Laravel Abstraction Abstract Service Joton Sutradhar

Alright, Laravel developers, let's talk shop. We love Laravel for its elegance, convention over configuration, and how quickly it lets us build. But as our applications grow, even in a framework as opinionated as Laravel, we can fall into traps: repeating the same logic across different "service" classes, inconsistent data handling, or struggling to manage complex workflows.

Profile Picture Joton Sutradhar β€’ πŸ“– 17 min read β€’ πŸ“… 20th June 2025
πŸš€ Laravel Horizon: A Step-by-Step Guide For Managing Queues Like A Pro
Queue Jobs Laravel Horizon

Queues are essential for building scalable and performant Laravel applications. Whether you're sending emails, processing files, or executing time-consuming tasks, queues offload work and keep your app fast. But how do you monitor and manage them effectively?

Profile Picture Joton Sutradhar β€’ πŸ“– 4 min read β€’ πŸ“… 2nd June 2025
Supercharge Your Laravel App With Queues – A Developer’s Experience With Background Email Sending
Laravel Jobs Queue Queue Jobs Php Joton Sutradhar

As Laravel developers, one of the critical lessons we eventually learn is: not everything should happen in real-time. Whether it's sending emails, processing images, syncing third-party data, or running analytics β€” pushing these resource-heavy or time-consuming tasks to the background is essential for a performant and responsive application.

Profile Picture Joton Sutradhar β€’ πŸ“– 6 min read β€’ πŸ“… 29th May 2025
Subscribe to my newsletter

Get recent projects & blog updates to your inbox.

I never share your email. Read our privacy policy.

© 2025 Joton Sutradhar. All rights reserved.