mcpField
L
laravel-services

laravel-services

github.com/leeovery开发1549 次安装2 周前更新
代码仓库

关于 laravel-services

laravel-services 是面向 Laravel 开发者的技能,用于组织外部服务集成。它介绍 Manager 模式和多驱动设计,并涵盖用于测试的 null driver 及基于 Saloon 的 HTTP 连接器。

主要功能

  1. 01Manager 模式结构
  2. 02多服务驱动
  3. 03驱动契约
  4. 04Saloon HTTP 连接器
  5. 05基于 Facade 的访问
  6. 06用于测试的 null driver

适用场景

  1. 01集成外部 API
  2. 02切换支付服务提供方
  3. 03按配置选择服务实现
  4. 04使用 null driver 进行测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/leeovery/agentic-skills --skill laravel-services

安装后在 Claude Code 中输入/laravel-services确认已加载。

详细介绍

Laravel 服务

外部服务使用带有多个驱动的 Laravel Manager 模式。

相关指南:

  • Actions - Action 使用服务
  • Packages - 使用 Saloon 作为 HTTP 客户端
  • Testing - 使用 null 驱动进行测试

适用场景

以下情况使用服务层:

  • 集成外部 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