--- name: laravel-services description: Service layer for external API integration using manager pattern and Saloon. Use when working with external APIs, third-party services, or when user mentions services, external API, Saloon, API integration, manager pattern. --- # Laravel Services External services use Laravel's **Manager pattern** with multiple drivers. **Related guides:** - [Actions](../laravel-actions/SKILL.md) - Actions use services - [Packages](../laravel-packages/SKILL.md) - Saloon for HTTP clients - [Testing](../laravel-testing/SKILL.md) - Testing with null drivers ## When to Use **Use service layer when:** - Integrating external APIs - Multiple drivers for same service (email, payment, SMS) - Need to swap implementations - Want null driver for testing ## Structure ``` Services/ └── Payment/ ├── PaymentManager.php # Manager (extends Laravel Manager) ├── Connectors/ │ └── StripeConnector.php # Saloon HTTP connector ├── Contracts/ │ └── PaymentDriver.php # Driver interface ├── Drivers/ │ ├── StripeDriver.php # Stripe implementation │ ├── PayPalDriver.php # PayPal implementation │ └── NullDriver.php # For testing ├── Exceptions/ │ └── PaymentException.php ├── Facades/ │ └── Payment.php # Facade └── Requests/ └── Stripe/ ├── CreatePaymentIntentRequest.php └── RefundPaymentRequest.php ``` ## Manager Class ```php config->get('payment.default'); } public function createStripeDriver(): StripeDriver { return new StripeDriver( apiKey: $this->config->get('payment.drivers.stripe.api_key'), webhookSecret: $this->config->get('payment.drivers.stripe.webhook_secret'), ); } public function createNullDriver(): NullDriver { return new NullDriver; } } ``` ## Driver Contract ```php sendRequest( new CreatePaymentIntentRequest($amount, $currency) ); return PaymentIntentData::from($response->json()); } public function refundPayment(string $paymentIntentId, ?int $amount = null): bool { // Implementation... } private function sendRequest(Request $request): Response { $response = $this->getConnector()->send($request); if ($response->failed()) { throw PaymentException::failedRequest($response); } return $response; } private function getConnector(): StripeConnector { if (static::$connector === null) { static::$connector = new StripeConnector($this->apiKey); } return static::$connector; } } ``` ## Saloon Connector ```php "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', ]; } } ``` ## Saloon Request ```php $this->amount, 'currency' => $this->currency, ]; } } ``` ## Facade ```php id); // Use specific driver Payment::driver('stripe')->createPaymentIntent(10000, 'usd'); Payment::driver('paypal')->createPaymentIntent(10000, 'usd'); // Use in actions class ProcessPaymentAction { public function __invoke(Order $order, PaymentData $data): Payment { $paymentIntent = Payment::createPaymentIntent( amount: $order->total, currency: 'usd' ); // ... } } ``` ## Null Driver for Testing ```php 'pi_test_' . uniqid(), 'amount' => $amount, 'currency' => $currency, 'status' => 'succeeded', ]); } public function refundPayment(string $paymentIntentId, ?int $amount = null): bool { return true; } public function retrievePaymentIntent(string $paymentIntentId): PaymentIntentData { return PaymentIntentData::from([ 'id' => $paymentIntentId, 'status' => 'succeeded', ]); } } ``` ## Summary **Service layer provides:** - Manager pattern for multiple drivers - Saloon for HTTP requests - Null drivers for testing - Clean abstraction over external services - Swappable implementations **Use for external API integrations only.**