> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sitestorepro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Building and Extending Site Store Pro Payment Plugins

> Extend Site Store Pro's built-in Stripe, Paddle, and PayPal processors or build a fully custom payment gateway by implementing PaymentProcessorInterface.

Site Store Pro ships with four payment processors — a test gateway and production-ready integrations for Stripe, Paddle Billing, and PayPal. You can customize the behavior of any built-in processor without touching core files by extending its class, or build an entirely new gateway by implementing the `PaymentProcessorInterface` contract and registering it in the platform.

## Built-in Processors

| ID | Name           | Driver                | SDK                       |
| -- | -------------- | --------------------- | ------------------------- |
| 0  | Test Gateway   | `TestProcessor.php`   | None                      |
| 1  | Stripe         | `StripeProcessor.php` | `stripe/stripe-php`       |
| 2  | Paddle Billing | `PaddleProcessor.php` | `paddlehq/paddle-php-sdk` |
| 3  | PayPal         | `PayPalProcessor.php` | Built-in HTTP client      |

<Note>
  Processor IDs 0–99 are reserved for built-in processors. When you register a custom gateway, you must use an ID of **100 or higher**.
</Note>

## The PaymentProcessorInterface Contract

Every processor — built-in or custom — must satisfy this interface:

```php theme={null}
// app/Services/Payments/Contracts/PaymentProcessorInterface.php

interface PaymentProcessorInterface
{
    public function charge(float $amount, string $currency, array $payload): PaymentResult;
    public function isSandbox(): bool;
    public function getName(): string;
}
```

`charge()` returns a `PaymentResult` value object that carries the authorization code, transaction ID, success flag, and any error message back to the order processing pipeline.

## Extending Built-in Processors

The cleanest way to customize a built-in processor is to extend its class. Place your extension file inside the `payment-processors/` directory at the project root — the platform auto-detects extension files in that directory on boot, so you don't need to modify any core service provider.

<Tabs>
  <Tab title="Stripe">
    Create `payment-processors/stripe/StripeProcessorExtension.php`:

    ```php theme={null}
    <?php
    namespace PaymentProcessors\Stripe;

    use App\Services\Payments\Processors\StripeProcessor as BaseStripeProcessor;

    class StripeProcessorExtension extends BaseStripeProcessor
    {
        public function getName(): string
        {
            return 'Customized Stripe' . ($this->isSandbox() ? ' (Sandbox)' : '');
        }

        public function createPaymentIntent(float $amount, string $currency = 'usd'): array
        {
            $result = parent::createPaymentIntent($amount, $currency);
            // Add custom logging or inject metadata here
            return $result;
        }
    }
    ```
  </Tab>

  <Tab title="Paddle">
    Create `payment-processors/paddle/PaddleProcessorExtension.php`:

    ```php theme={null}
    <?php
    namespace PaymentProcessors\Paddle;

    use App\Services\Payments\Processors\PaddleProcessor as BasePaddleProcessor;

    class PaddleProcessorExtension extends BasePaddleProcessor
    {
        public function createTransaction(
            float $amount,
            string $currency = 'USD',
            array $meta = []
        ): array {
            $meta['custom_store_ref'] = config('app.name');
            return parent::createTransaction($amount, $currency, $meta);
        }
    }
    ```
  </Tab>

  <Tab title="PayPal">
    Create `payment-processors/paypal/PayPalProcessorExtension.php`:

    ```php theme={null}
    <?php
    namespace PaymentProcessors\PayPal;

    use App\Services\Payments\Processors\PayPalProcessor as BasePayPalProcessor;
    use App\Services\Payments\PaymentResult;

    class PayPalProcessorExtension extends BasePayPalProcessor
    {
        public function charge(
            float $amount,
            string $currency,
            array $payload
        ): PaymentResult {
            // Pre- or post-process the charge here
            return parent::charge($amount, $currency, $payload);
        }
    }
    ```
  </Tab>
</Tabs>

<Note>
  The platform auto-detects extension files inside `payment-processors/` on boot. You don't need to register them manually — just follow the namespace and directory naming convention shown above and the manager will pick them up.
</Note>

## Building a Custom Payment Gateway

Follow these nine steps to integrate a brand-new payment provider from scratch.

<Warning>
  Custom processor IDs must be **100 or higher**. IDs 0–99 are reserved for Site Store Pro's built-in processors. Using a reserved ID will cause conflicts that are difficult to debug.
</Warning>

<Steps>
  <Step title="Copy the Example Gateway Template">
    Start from the bundled template to get the correct directory structure:

    ```bash theme={null}
    cp -r payment-processors/example-gateway payment-processors/my-gateway
    ```
  </Step>

  <Step title="Implement PaymentProcessorInterface">
    Create your processor class in `payment-processors/my-gateway/MyGatewayProcessor.php`:

    ```php theme={null}
    <?php
    namespace PaymentProcessors\MyGateway;

    use App\Services\Payments\Contracts\PaymentProcessorInterface;
    use App\Services\Payments\PaymentResult;

    class MyGatewayProcessor implements PaymentProcessorInterface
    {
        public function getName(): string
        {
            return 'My Gateway' . ($this->isSandbox() ? ' (Sandbox)' : '');
        }

        public function isSandbox(): bool
        {
            return config('services.my_gateway.sandbox', true);
        }

        public function charge(float $amount, string $currency, array $payload): PaymentResult
        {
            try {
                $transactionId = $payload['transaction_id'] ?? 'TXN_' . time();

                return new PaymentResult(
                    success: true,
                    authorizationCode: 'AUTH_' . rand(1000, 9999),
                    transactionId: $transactionId,
                    errorMessage: '',
                    processorName: $this->getName()
                );
            } catch (\Throwable $e) {
                return new PaymentResult(
                    success: false,
                    authorizationCode: '',
                    transactionId: '',
                    errorMessage: $e->getMessage(),
                    processorName: $this->getName()
                );
            }
        }
    }
    ```
  </Step>

  <Step title="Add Environment Credentials">
    Add your gateway's API keys to `.env`:

    ```ini theme={null}
    MY_GATEWAY_API_KEY=your_live_key
    MY_GATEWAY_SANDBOX_API_KEY=your_sandbox_key
    ```

    Reference these in `config/services.php` under a `my_gateway` key so they're accessible via `config('services.my_gateway.*')`.
  </Step>

  <Step title="Insert a Row into order_processors">
    Register your processor in the database. Use an ID of 100 or higher:

    ```sql theme={null}
    INSERT INTO order_processors (processor_id, processor_name, production, created_at, updated_at)
    VALUES (100, 'My Gateway', 0, NOW(), NOW());
    ```

    Set `production` to `1` when you're ready to accept live payments.
  </Step>

  <Step title="Register the Class in config/payment_processors.php">
    Load your processor file and map its ID to the class:

    ```php theme={null}
    require_once base_path('payment-processors/my-gateway/MyGatewayProcessor.php');

    $processors[100] = \PaymentProcessors\MyGateway\MyGatewayProcessor::class;
    ```
  </Step>

  <Step title="Add the Processor Type to PaymentProcessorManager">
    Open `app/Services/Payments/PaymentProcessorManager.php` and add your ID to the `activeProcessorType()` match expression:

    ```php theme={null}
    return match ($this->activeProcessorId()) {
        1       => 'stripe',
        2       => 'paddle',
        3       => 'paypal',
        100     => 'my-gateway',
        default => 'test',
    };
    ```
  </Step>

  <Step title="Handle Frontend Checkout Integration">
    Integrate your gateway's JavaScript SDK (if applicable) inside `OrderReview::preparePayment()`. This method is responsible for initialising the client-side checkout flow — tokenising card data, opening a hosted payment form, or triggering a redirect to your provider's checkout page.
  </Step>

  <Step title="Verify the Interface Contract">
    Double-check that your class satisfies all three methods required by `PaymentProcessorInterface`:

    ```php theme={null}
    interface PaymentProcessorInterface
    {
        public function charge(float $amount, string $currency, array $payload): PaymentResult;
        public function isSandbox(): bool;
        public function getName(): string;
    }
    ```

    PHP will throw a fatal error at runtime if any method is missing or has an incompatible signature.
  </Step>

  <Step title="Enable in the Admin Panel">
    Navigate to **Admin → Checkout → Processors**, find your new gateway in the list, and select it as the **Primary** processor. Toggle the sandbox flag off when you're ready to go live.
  </Step>
</Steps>

***

## Webhook Endpoints

Site Store Pro exposes dedicated webhook endpoints for each supported payment provider. Configure these URLs in your payment provider's dashboard so that payment events — confirmations, subscription updates, refunds, and disputes — are pushed to your Site Store Pro installation in real time.

| Method | Endpoint           | Provider       |
| ------ | ------------------ | -------------- |
| `POST` | `/webhooks/stripe` | Stripe         |
| `POST` | `/webhooks/paddle` | Paddle Billing |
| `POST` | `/webhooks/paypal` | PayPal         |

Each endpoint verifies the incoming request signature before processing the event payload:

* **Stripe** — validates using `STRIPE_WEBHOOK_SECRET` (`whsec_...`) via Stripe's `WebhookSignatureVerifier`.
* **Paddle** — validates using `PADDLE_WEBHOOK_SECRET` (`pwh_...`) via Paddle's notification verifier.
* **PayPal** — validates using `PAYPAL_CLIENT_ID` and `PAYPAL_CLIENT_SECRET` to verify webhook event authenticity.

<Warning>
  Webhook endpoints are CSRF-exempt by design — they receive POST requests from external payment providers. Ensure `STRIPE_WEBHOOK_SECRET`, `PADDLE_WEBHOOK_SECRET`, and PayPal credentials are set correctly in `.env` before exposing these URLs to production traffic. Missing or incorrect secrets will cause signature verification to fail and all incoming events to be rejected.
</Warning>

Register these URLs in your provider dashboards:

```text theme={null}
# Stripe Dashboard → Developers → Webhooks
https://yourdomain.com/webhooks/stripe

# Paddle Dashboard → Notifications
https://yourdomain.com/webhooks/paddle

# PayPal Developer Dashboard → Webhooks
https://yourdomain.com/webhooks/paypal
```

For local development and testing, use a tunnelling tool (such as the Stripe CLI or ngrok) to expose your local server and forward provider events to your machine before deploying to a publicly accessible URL.
