laravel-services
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel Services
Laravel 服务层
External services use Laravel's Manager pattern with multiple drivers.
Related guides:
- Actions - Actions use services
- Packages - Saloon for HTTP clients
- Testing - Testing with null drivers
外部服务采用Laravel的Manager模式,支持多驱动实现。
相关指南:
- Actions - 动作(Actions)基于服务层实现
- Packages - 基于Saloon实现HTTP客户端
- Testing - 基于Null驱动进行测试
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
在以下场景使用服务层:
- 集成外部API
- 同一服务需多驱动实现(如邮件、支付、短信)
- 需要切换实现方案
- 测试时需使用Null驱动
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.phpServices/
└── Payment/
├── PaymentManager.php # 管理器(继承Laravel Manager)
├── Connectors/
│ └── StripeConnector.php # Saloon HTTP连接器
├── Contracts/
│ └── PaymentDriver.php # 驱动接口
├── Drivers/
│ ├── StripeDriver.php # Stripe实现
│ ├── PayPalDriver.php # PayPal实现
│ └── NullDriver.php # 测试用驱动
├── Exceptions/
│ └── PaymentException.php
├── Facades/
│ └── Payment.php # 门面(Facade)
└── Requests/
└── Stripe/
├── CreatePaymentIntentRequest.php
└── RefundPaymentRequest.phpManager Class
管理器类
php
<?php
declare(strict_types=1);
namespace App\Services\Payment;
use App\Services\Payment\Drivers\NullDriver;
use App\Services\Payment\Drivers\StripeDriver;
use Illuminate\Support\Manager;
class PaymentManager extends Manager
{
public function getDefaultDriver(): string
{
return $this->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;
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment;
use App\Services\Payment\Drivers\NullDriver;
use App\Services\Payment\Drivers\StripeDriver;
use Illuminate\Support\Manager;
class PaymentManager extends Manager
{
public function getDefaultDriver(): string
{
return $this->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
<?php
declare(strict_types=1);
namespace App\Services\Payment\Contracts;
use App\Data\PaymentIntentData;
interface PaymentDriver
{
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData;
public function refundPayment(string $paymentIntentId, ?int $amount = null): bool;
public function retrievePaymentIntent(string $paymentIntentId): PaymentIntentData;
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Contracts;
use App\Data\PaymentIntentData;
interface PaymentDriver
{
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData;
public function refundPayment(string $paymentIntentId, ?int $amount = null): bool;
public function retrievePaymentIntent(string $paymentIntentId): PaymentIntentData;
}Driver Implementation
驱动实现
php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Drivers;
use App\Data\PaymentIntentData;
use App\Services\Payment\Connectors\StripeConnector;
use App\Services\Payment\Contracts\PaymentDriver;
use App\Services\Payment\Exceptions\PaymentException;
use App\Services\Payment\Requests\Stripe\CreatePaymentIntentRequest;
use Saloon\Http\Response;
class StripeDriver implements PaymentDriver
{
private static ?StripeConnector $connector = null;
public function __construct(
private readonly string $apiKey,
private readonly string $webhookSecret,
) {}
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData
{
$response = $this->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;
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Drivers;
use App\Data\PaymentIntentData;
use App\Services\Payment\Connectors\StripeConnector;
use App\Services\Payment\Contracts\PaymentDriver;
use App\Services\Payment\Exceptions\PaymentException;
use App\Services\Payment\Requests\Stripe\CreatePaymentIntentRequest;
use Saloon\Http\Response;
class StripeDriver implements PaymentDriver
{
private static ?StripeConnector $connector = null;
public function __construct(
private readonly string $apiKey,
private readonly string $webhookSecret,
) {}
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData
{
$response = $this->sendRequest(
new CreatePaymentIntentRequest($amount, $currency)
);
return PaymentIntentData::from($response->json());
}
public function refundPayment(string $paymentIntentId, ?int $amount = null): bool
{
// 实现逻辑...
}
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
Saloon 连接器
php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Connectors;
use Saloon\Http\Connector;
class StripeConnector extends Connector
{
public function __construct(private readonly string $apiKey) {}
public function resolveBaseUrl(): string
{
return 'https://api.stripe.com';
}
protected function defaultHeaders(): array
{
return [
'Authorization' => "Bearer {$this->apiKey}",
'Content-Type' => 'application/json',
];
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Connectors;
use Saloon\Http\Connector;
class StripeConnector extends Connector
{
public function __construct(private readonly string $apiKey) {}
public function resolveBaseUrl(): string
{
return 'https://api.stripe.com';
}
protected function defaultHeaders(): array
{
return [
'Authorization' => "Bearer {$this->apiKey}",
'Content-Type' => 'application/json',
];
}
}Saloon Request
Saloon 请求类
php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Requests\Stripe;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
class CreatePaymentIntentRequest extends Request implements HasBody
{
use HasJsonBody;
protected Method $method = Method::POST;
public function __construct(
private readonly int $amount,
private readonly string $currency,
) {}
public function resolveEndpoint(): string
{
return '/v1/payment_intents';
}
protected function defaultBody(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
];
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Requests\Stripe;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
class CreatePaymentIntentRequest extends Request implements HasBody
{
use HasJsonBody;
protected Method $method = Method::POST;
public function __construct(
private readonly int $amount,
private readonly string $currency,
) {}
public function resolveEndpoint(): string
{
return '/v1/payment_intents';
}
protected function defaultBody(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
];
}
}Facade
门面(Facade)
php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Facades;
use App\Data\PaymentIntentData;
use Illuminate\Support\Facades\Facade;
/**
* @method static PaymentIntentData createPaymentIntent(int $amount, string $currency)
* @method static bool refundPayment(string $paymentIntentId, ?int $amount = null)
* @method static PaymentIntentData retrievePaymentIntent(string $paymentIntentId)
*
* @see \App\Services\Payment\PaymentManager
*/
class Payment extends Facade
{
protected static function getFacadeAccessor(): string
{
return \App\Services\Payment\PaymentManager::class;
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Facades;
use App\Data\PaymentIntentData;
use Illuminate\Support\Facades\Facade;
/**
* @method static PaymentIntentData createPaymentIntent(int $amount, string $currency)
* @method static bool refundPayment(string $paymentIntentId, ?int $amount = null)
* @method static PaymentIntentData retrievePaymentIntent(string $paymentIntentId)
*
* @see \App\Services\Payment\PaymentManager
*/
class Payment extends Facade
{
protected static function getFacadeAccessor(): string
{
return \App\Services\Payment\PaymentManager::class;
}
}Usage
使用示例
php
use App\Services\Payment\Facades\Payment;
// Use default driver
$paymentIntent = Payment::createPaymentIntent(
amount: 10000, // $100.00 in cents
currency: 'usd'
);
// Refund payment
Payment::refundPayment($paymentIntent->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'
);
// ...
}
}php
use App\Services\Payment\Facades\Payment;
// 使用默认驱动
$paymentIntent = Payment::createPaymentIntent(
amount: 10000, // 100美元,单位为美分
currency: 'usd'
);
// 退款操作
Payment::refundPayment($paymentIntent->id);
// 指定驱动使用
Payment::driver('stripe')->createPaymentIntent(10000, 'usd');
Payment::driver('paypal')->createPaymentIntent(10000, 'usd');
// 在动作(Actions)中使用
class ProcessPaymentAction
{
public function __invoke(Order $order, PaymentData $data): Payment
{
$paymentIntent = Payment::createPaymentIntent(
amount: $order->total,
currency: 'usd'
);
// ...
}
}Null Driver for Testing
测试用Null驱动
php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Drivers;
use App\Data\PaymentIntentData;
use App\Services\Payment\Contracts\PaymentDriver;
class NullDriver implements PaymentDriver
{
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData
{
return PaymentIntentData::from([
'id' => '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',
]);
}
}php
<?php
declare(strict_types=1);
namespace App\Services\Payment\Drivers;
use App\Data\PaymentIntentData;
use App\Services\Payment\Contracts\PaymentDriver;
class NullDriver implements PaymentDriver
{
public function createPaymentIntent(int $amount, string $currency): PaymentIntentData
{
return PaymentIntentData::from([
'id' => '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.
服务层特性:
- 基于Manager模式实现多驱动管理
- 采用Saloon处理HTTP请求
- 提供Null驱动用于测试
- 对外部服务实现清晰的抽象
- 支持切换不同实现方案
仅适用于外部API集成场景。