agentsclimarketplace

Laravel services

Skill noppu-labs/ai-toolkit/laravel/skills/laravel-services

Toolkit for agentic development of Laravel & React apps

Install
npx -y skills add noppu-labs/ai-toolkit --skill laravel-services

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Service layer for external API integration using manager pattern and Saloon. Use when integrating external APIs or third-party services.

SKILL.md

7.4 KB, as published. Nobody here has run it

Laravel Services

External services use Laravel's Manager pattern with multiple drivers.

Related guides:

  • Testing - 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

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

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

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;
    }
}

Usage

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 Driver for Testing

<?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',
        ]);
    }
}

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.