Sitemap

Adapter Pattern in Laravel: A Practical Guide

4 min readSep 23, 2025
Press enter or click to view image in full size

When working on large Laravel applications, you’ll often face situations where two systems need to communicate but don’t share the same interface.

For example:

  • You start with Stripe as your payment provider.
  • Later, your business requires PayPal integration.
  • Each has completely different SDKs and methods.

Without structure, this leads to messy if/else logic scattered across your codebase. Enter the Adapter Pattern — a clean way to make different systems “speak the same language.”

🔹 What is the Adapter Pattern?

The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a “translator” between two classes that otherwise can’t directly interact.

Think of it like a power adapter:

  • Your laptop has a USB-C port.
  • The projector has an HDMI cable.
  • An adapter allows them to connect seamlessly.

In Laravel, this is particularly useful when you want to:

  • Swap third-party services (payment, SMS, storage, etc.).
  • Isolate vendor-specific logic.
  • Write clean, testable, and maintainable code.

🔹 Advantages of Using Adapter Pattern in Laravel

  1. Decoupling — Your application code doesn’t need to know about the third-party API details.
  2. Flexibility — Swap implementations without changing your business logic.
  3. Testability — You can easily mock adapters in tests.
  4. Maintainability — Vendor-specific code stays in one place.
  5. Scalability — Easily add new integrations following the same interface.

🔹 Real-World Example

Imagine you are integrating two payment gateways in your Laravel app: Stripe and PayPal. Each has a different API, but your application only wants to call a generic method like charge($amount).

Without adapters, you’d end up with messy if/else or duplicated logic. With the Adapter Pattern, you can abstract this.

🔹 Step-by-Step Implementation in Laravel

  1. Define a Common Interface{
<?php

namespace App\Contracts;

interface PaymentGatewayInterface
{
public function charge(float $amount): string;
}

2. Create Adapters for Each Gateway:

Stripe Adapter

<?php

namespace App\Adapters;

use App\Contracts\PaymentGatewayInterface;
use Stripe\StripeClient;

class StripeAdapter implements PaymentGatewayInterface
{
protected StripeClient $stripe;

public function __construct()
{
$this->stripe = new StripeClient(env('STRIPE_SECRET'));
}

public function charge(float $amount): string
{
// Example: Create a payment intent
$payment = $this->stripe->paymentIntents->create([
'amount' => $amount * 100,
'currency' => 'usd',
'payment_method_types' => ['card'],
]);

return "Stripe payment successful: " . $payment->id;
}
}

PayPal Adapter

<?php

namespace App\Adapters;

use App\Contracts\PaymentGatewayInterface;
use PayPal\Api\Payment;

class PaypalAdapter implements PaymentGatewayInterface
{
public function charge(float $amount): string
{
// Example PayPal logic
return "PayPal payment successful: " . uniqid('paypal_');
}
}

3. Use Adapters in a Service

<?php

namespace App\Services;

use App\Contracts\PaymentGatewayInterface;

class PaymentService
{
protected PaymentGatewayInterface $gateway;

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

public function process(float $amount): string
{
return $this->gateway->charge($amount);
}
}

4. Bind the Adapter in a Service Provider

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\PaymentGatewayInterface;
use App\Adapters\StripeAdapter;
use App\Adapters\PaypalAdapter;

class PaymentServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(PaymentGatewayInterface::class, function ($app) {
return match (config('payment.driver')) {
'stripe' => new StripeAdapter(),
'paypal' => new PaypalAdapter(),
default => new StripeAdapter(),
};
});
}
}

5. Use in Controller

<?php

namespace App\Http\Controllers;

use App\Services\PaymentService;
use Illuminate\Http\Request;

class PaymentController extends Controller
{
public function pay(Request $request, PaymentService $paymentService)
{
$amount = $request->input('amount', 100);
$result = $paymentService->process($amount);

return response()->json(['message' => $result]);
}
}

6. Configurable Driver

Create a config file config/payment.php:

<?php

return [
'driver' => env('PAYMENT_DRIVER', 'stripe'),
];

In .env:

PAYMENT_DRIVER=stripe // or paypal

🔹 Visual Representation

Here’s a simple way to visualize it:

[ Controller ] → [ PaymentService ] → [ PaymentGatewayInterface ]
↙ ↘
[ StripeAdapter ] [ PaypalAdapter ]

The controller doesn’t care which adapter is used — it only talks to the interface.

Where Can You Apply Adapter Pattern in Laravel?

Besides payments, you can use the Adapter Pattern in many real-world Laravel cases:

  • SMS Services → Twilio, Nexmo, MSG91.
  • File Storage → AWS S3, Google Cloud, Local.
  • Notifications → Slack, Email, WhatsApp APIs.
  • Search Engines → Elasticsearch, Algolia, Typesense.
  • Geocoding APIs → Google Maps, OpenStreetMap.

🔹 Best Practices When Using Adapter Pattern

  • Always start with a contract (interface) — this guarantees consistency.
  • Keep adapters thin — they should only translate, not contain business logic.
  • Use Service Providers to bind adapters dynamically.
  • Combine with Laravel’s config system for flexibility.
  • Write unit tests with mocks instead of hitting real APIs.

🔹 Conclusion

The Adapter Pattern in Laravel is like a universal translator for your application. It allows you to integrate multiple third-party services without creating messy conditional logic or vendor lock-in.

With this approach, your app becomes:

  • More maintainable
  • Easier to test
  • Highly flexible when business requirements change

If you’re building scalable Laravel applications, the Adapter Pattern should definitely be part of your design toolkit.

Pro tip: Pair the Adapter Pattern with Strategy Pattern for even more powerful abstractions (e.g., dynamically choosing gateways at runtime).

🙏 Thanks for reading! If you found this helpful, please like, share, and follow me on LinkedIn for more Laravel and software design insights.

--

--

Responses (2)