Learn to decouple your Laravel application from external APIs using the Adapter pattern. Build resilient services that shield your domain from vendor churn.
Previously in this course, we discussed Database Deadlock Prevention to ensure high-concurrency stability. While database integrity is foundational, your application's external boundaries are equally fragile. Today, we focus on Managing Third-Party API Integrations by moving away from hard-coded vendor logic toward a clean, adapter-based architecture.
When you sprinkle API calls (using Http::get or Guzzle) directly into your controllers or actions, you create a "vendor lock-in" trap. If the third-party API changes its response structure or authentication method, you’re forced to hunt through your codebase to refactor. Instead, we’ll treat external APIs as internal dependencies that must conform to our domain interfaces.
The Adapter pattern allows incompatible interfaces to work together. In the context of Laravel, it means your domain logic speaks one language (your defined Interface), while an Adapter handles the translation to the vendor’s proprietary API.
By wrapping third-party APIs in services, we achieve three things:
Http::fake() for every single integration test.Imagine we need to integrate a payment provider. We don't want our ProcessPayment action to know if we are using Stripe, Braintree, or a custom internal gateway.
First, define a contract that describes what your application needs, not how the API works.
PHPnamespace App\Contracts; interface PaymentGateway { public function charge(int $amount, string $currency, string $token): bool; }
Now, build an implementation for a specific provider. This class encapsulates all vendor-specific headers, URL structures, and error handling.
PHPnamespace App\Services\Payments; use App\Contracts\PaymentGateway; use Illuminate\Support\Facades\Http; class StripeAdapter implements PaymentGateway { public function __construct(private string $apiKey) {} public function charge(int $amount, string $currency, string $token): bool { $response = Http::withToken($this->apiKey) ->post('https:#6A9955">//api.stripe.com/v1/charges', [ 'amount' => $amount, 'currency' => $currency, 'source' => $token, ]); return $response->successful(); } }
Register the implementation in your AppServiceProvider or a dedicated PaymentServiceProvider.
PHPpublic function register() { $this->app->bind(PaymentGateway::class, function ($app) { return new StripeAdapter(config('services.stripe.key')); }); }
Your business logic now depends on the interface, not the implementation.
PHPclass ProcessPayment { public function __construct(private PaymentGateway $gateway) {} public function execute(int $amount) { return $this->gateway->charge($amount, 'usd', 'tok_visa'); } }
Refactor an existing API integration in your project. If you are using a service like Mailgun or AWS SES, create a MailerInterface and a corresponding adapter.
src/Domain/Communication/Contracts/MailerInterface.php.Mail::send or HTTP-based mailing logic into a class implementing this interface.$this->mock(MailerInterface::class, fn($m) => $m->shouldReceive('send')->once());.RequestException inside the adapter and throw a custom PaymentFailedException that your domain logic understands.We've moved beyond simple Integrating Third-Party Services in Laravel by formalizing our external dependencies. By using the Adapter pattern, we ensure that our domain remains pure, our tests remain fast, and our architecture remains flexible enough to swap vendors without a massive refactor.
Up next: We will discuss strategies for API Rate Limiting and Circuit Breakers, ensuring your application stays responsive even when external vendors experience outages.
Master OAuth2 implementation in Laravel by building a secure Authorization Code flow. Learn to handle token issuance, validation, and architectural best practices.
Read moreLearn how to use Laravel Horizon for advanced queue monitoring, failure management, and performance tuning to keep your background jobs running smoothly.
Managing Third-Party API Integrations