关于 laravel-services
laravel-services 是面向 Laravel 开发者的技能,用于组织外部服务集成。它介绍 Manager 模式和多驱动设计,并涵盖用于测试的 null driver 及基于 Saloon 的 HTTP 连接器。
主要功能
- 01Manager 模式结构
- 02多服务驱动
- 03驱动契约
- 04Saloon HTTP 连接器
- 05基于 Facade 的访问
- 06用于测试的 null driver
适用场景
- 01集成外部 API
- 02切换支付服务提供方
- 03按配置选择服务实现
- 04使用 null driver 进行测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ leeovery/ agentic-skills --skill laravel-services安装后在 Claude Code 中输入/laravel-services确认已加载。
详细介绍
Laravel 服务
外部服务使用带有多个驱动的 Laravel Manager 模式。
相关指南:
适用场景
以下情况使用服务层:
- 集成外部 API
- 同一服务有多个驱动(电子邮件、支付、SMS)
- 需要切换实现
- 希望在测试中使用 null 驱动
结构
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 类
<?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
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
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;
}
}
Saloon Connector
<?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
<?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
<?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;
}
}
用法
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'
);
// ...
}
}
用于测试的 Null 驱动
<?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',
]);
}
}
评论
支持 Markdown