PHPackages                             timeax/paykit-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. timeax/paykit-sdk

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

timeax/paykit-sdk
=================

PayKit Gateway SDK (contracts + typed payloads) for integrating multiple payment providers through strict contracts and DTO payloads.

v0.1.0(3mo ago)042MITPHPPHP ^8.1

Since Jan 1Pushed 1mo agoCompare

[ Source](https://github.com/timeax/payment-gateway-sdk)[ Packagist](https://packagist.org/packages/timeax/paykit-sdk)[ RSS](/packages/timeax-paykit-sdk/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (3)Versions (11)Used By (0)

📦 PayKit Gateway SDK (Contracts + Payloads)
===========================================

[](#-paykit-gateway-sdk-contracts--payloads)

A **business‑agnostic Payment Gateway SDK** that lets a host application integrate multiple payment providers through strict contracts and typed payloads.

✅ What we ship:

- **Contracts (interfaces):** define what a gateway driver can do (payments, refunds, saved cards, virtual accounts, payouts, etc.)
- **Payloads (DTOs):** strict request/response/event shapes (no magic arrays)
- **Abstract driver base(s):** safe defaults + reusable helpers
- **Manager/Registry:** resolve drivers by `driver_key`
- **Frontend primitives:** scripts + UI module manifests (framework‑agnostic)

❌ What we do **not** ship:

- Checkout/cart/invoice semantics (the SDK never cares what the payment is for)
- Host database schema or migrations (hosts persist manifests/capabilities however they want)
- A UI framework implementation (React/Vue/etc. are host concerns)

> Source of truth: only `src/` is the SDK public surface.

---

0) Installation
---------------

[](#0-installation)

### Composer

[](#composer)

```
composer require timeax/paykit-sdk
```

### Namespace / Autoloading

[](#namespace--autoloading)

This SDK is PSR-4 autoloaded under the `PayKit\` namespace (your package `composer.json` should use):

```
"autoload": {
  "psr-4": {
    "PayKit\\": "src/"
  }
}
```

---

0.1) Laravel integration (manual, copy-paste)
---------------------------------------------

[](#01-laravel-integration-manual-copy-paste)

PayKit ships as a plain Composer library (framework-agnostic). In Laravel, you can bind the manager as a singleton in your **host app**.

### Option A — Bind only `GatewayManager` (simplest host DX)

[](#option-a--bind-only-gatewaymanager-simplest-host-dx)

```
// app/Providers/AppServiceProvider.php

use PayKit\Manager\GatewayManager;

public function register(): void
{
    $this->app->singleton(GatewayManager::class, fn () => new GatewayManager());
}
```

### Option B — Bind the full manager stack (explicit singletons)

[](#option-b--bind-the-full-manager-stack-explicit-singletons)

```
// app/Providers/AppServiceProvider.php

use PayKit\Manager\GatewayManager;
use PayKit\Manager\GatewayRegistry;
use PayKit\Manager\DriverResolver;

public function register(): void
{
    $this->app->singleton(GatewayRegistry::class, fn () => new GatewayRegistry());

    $this->app->singleton(DriverResolver::class, fn ($app) =>
        new DriverResolver($app->make(GatewayRegistry::class))
    );

    $this->app->singleton(GatewayManager::class, fn ($app) =>
        new GatewayManager(
            $app->make(GatewayRegistry::class),
            $app->make(DriverResolver::class),
        )
    );
}
```

> If your constructor signatures differ, adjust the bindings accordingly — the intent is “one registry + one resolver + one manager per app”.

---

1) Core philosophy
------------------

[](#1-core-philosophy)

### 1. Model‑First (Host DB is Source of Truth)

[](#1-modelfirst-host-db-is-source-of-truth)

Drivers may discover capabilities (currencies/countries/features) **only during manifest sync**, not at checkout runtime.

### 2. Host‑Controlled Business Effects

[](#2-hostcontrolled-business-effects)

- Drivers only talk to providers.
- Hosts decide what “paid” means (credit wallet, place orders, grant access, etc.).

### 3. Strict Contracts + Payloads

[](#3-strict-contracts--payloads)

Everything is typed. No random associative arrays for core flows.

### 4. Capability‑by‑Contract

[](#4-capabilitybycontract)

New features are optional contracts, not scattered flags.

---

2) What this SDK solves
-----------------------

[](#2-what-this-sdk-solves)

- A single integration surface across many providers.
- Normalized statuses (canonical sets).
- A manifest/capability engine so checkout can be DB‑filtered without calling provider APIs.
- Optional frontend integration metadata (scripts + UI module keys).

---

3) Glossary
-----------

[](#3-glossary)

- **Driver:** Provider implementation (Stripe, Paystack, Flutterwave, etc.).
- **Manifest:** Driver’s support/capability snapshot (currencies, countries, features).
- **Support Matrix:** What the driver supports (where/what currencies/countries).
- **Feature Set:** What the driver can do (refunds, saved cards, virtual accounts, payouts, etc.).
- **UI Manifest:** Framework‑agnostic frontend module descriptors.

---

4) Config injection (Hybrid)
----------------------------

[](#4-config-injection-hybrid)

PayKit uses a **hybrid config injection** pattern:

- Drivers may receive a **default config** in the constructor (standard “manager” flow).
- Contract methods accept an **optional override config**: `?ConfigBag $config = null`.
- Resolution rule: **method override &gt; constructor default &gt; error**.

This keeps host code clean in normal flows, while allowing stateless/batch use when needed (e.g., iterating through multiple merchant accounts).

**Rule:** Wherever a method takes both a config and other parameters, the config **must be the last parameter**.

Config is carried in `Timeax\ConfigSchema\Support\ConfigBag` (from `timeax/ui-config-schema`), which holds `options`and `secrets` and provides `option()` / `secret()` helpers.

---

5) `src/` folder structure (reference)
--------------------------------------

[](#5-src-folder-structure-reference)

> This is the canonical `src/` layout.

```
src/
  index.php
  Pay.php

  Contracts/
    # --- base / discovery ---
    PaymentGatewayDriverContract.php
    PaymentGatewayManifestProviderContract.php
    PaymentGatewayAvailabilityContract.php
    PaymentGatewayRequirementsContract.php
    ProvidesGatewayConfigContract.php

    # --- core payments ---
    PaymentGatewayPaymentsContract.php
    PaymentGatewayPaymentStatusMapperContract.php
    PaymentGatewayVerificationContract.php

    # --- webhooks / events ---
    PaymentGatewayWebhooksContract.php

    # --- frontend integration ---
    PaymentGatewayScriptsContract.php
    PaymentGatewayUiContract.php
    PaymentGatewayFrontendConfigContract.php

    # --- saved methods + card tokenization ---
    PaymentGatewaySavedMethodsContract.php
    PaymentGatewayCardTokenizationContract.php

    # --- refunds + disputes ---
    PaymentGatewayRefundsContract.php
    PaymentGatewayDisputesContract.php

    # --- virtual accounts ---
    PaymentGatewayVirtualAccountsContract.php
    PaymentGatewayVirtualAccountLedgerContract.php
    PaymentGatewayVirtualAccountWithdrawalsContract.php
    PaymentGatewayVirtualAccountWebhookWatcherContract.php
    PaymentGatewayVirtualAccountPollingWatcherContract.php
    PaymentGatewayVirtualAccountReconcileContract.php

    # --- payouts ---
    PaymentGatewayPayoutsContract.php
    PaymentGatewayBeneficiariesContract.php

    # --- virtual cards ---
    PaymentGatewayVirtualCardsContract.php

    # --- reconciliation + diagnostics ---
    PaymentGatewayReconcileContract.php
    PaymentGatewayDiagnosticsContract.php

  Payload/
    Common/
      # primitives
      Money.php
      Amount.php
      Currency.php
      Country.php
      Reference.php
      ProviderRef.php
      Metadata.php
      CanonicalPaymentStatus.php
      CanonicalPayoutStatus.php
      CanonicalRefundStatus.php

      # config + schema (from timeax/ui-config-schema)
      # ConfigBag, ConfigSchema, ConfigField, ConfigValidationResult (external)
      HealthCheckResult.php

      # manifest + capabilities
      GatewayManifest.php
      GatewayFeatureSet.php
      GatewaySupportMatrix.php
      SupportedCurrency.php
      SupportedCountry.php
      GatewayRequirements.php
      UiManifest.php
      UiModuleDescriptor.php
      UiEntryDescriptor.php

      # scripts
      GatewayScript.php
      ScriptLocation.php

      # saved methods/cards
      SavedMethod.php
      CardSummary.php
      CardBrand.php
      CardFingerprint.php

      # virtual accounts/cards
      VirtualAccount.php
      VirtualAccountBank.php
      VirtualCardRecord.php

      # payouts
      PayoutDestination.php
      PayoutMethod.php
      Beneficiary.php

    Requests/
      # payments
      PaymentInitiateRequest.php
      PaymentVerifyRequest.php

      # webhooks
      WebhookRequest.php

      # refunds/disputes
      RefundRequest.php
      RefundVerifyRequest.php
      DisputeQuery.php

      # saved methods/cards
      ListMethodsRequest.php
      GetMethodRequest.php
      AttachMethodRequest.php
      DetachMethodRequest.php
      SetDefaultMethodRequest.php
      CardTokenizeRequest.php

      # virtual accounts
      VirtualAccountCreateRequest.php
      VirtualAccountAssignRequest.php
      VirtualAccountGetRequest.php
      ListVirtualAccountsRequest.php
      DeactivateVirtualAccountRequest.php
      VirtualAccountLedgerQuery.php
      VirtualAccountLedgerEntryQuery.php
      VirtualAccountWithdrawalRequest.php
      VirtualAccountWithdrawalVerifyRequest.php

      # watchers / polling
      PollVirtualAccountEventsQuery.php
      ReconcileQuery.php

      # payouts
      PayoutRequest.php
      PayoutVerifyRequest.php
      BeneficiaryCreateRequest.php
      BeneficiaryUpdateRequest.php

      # frontend config
      FrontendConfigRequest.php

    Responses/
      # payments + next action
      PaymentInitiateResult.php
      NextAction.php
      RedirectAction.php
      InlineAction.php
      PopupAction.php
      QrCodeAction.php
      InstructionsAction.php

      PaymentVerifyResult.php

      # webhooks
      WebhookVerifyResult.php

      # refunds/disputes
      RefundResult.php
      RefundStatusResult.php
      DisputeSnapshot.php

      # saved methods/cards
      SavedMethodList.php
      CardTokenizeResult.php

      # virtual accounts
      VirtualAccountRecord.php
      VirtualAccountList.php
      VirtualAccountLedgerEntry.php
      VirtualAccountLedgerPage.php
      VirtualAccountWithdrawalResult.php
      VirtualAccountWithdrawalStatusResult.php

      # watchers / reconcile
      PollSpec.php
      ReconcileResult.php

      # payouts
      PayoutResult.php
      PayoutStatusResult.php
      BeneficiaryList.php

      # frontend config
      FrontendConfigResult.php

    Events/
      # generic webhook normalization
      WebhookEvent.php
      WebhookHandleResult.php

      # virtual account events (deposits/withdrawals/transfers)
      VirtualAccountEvent.php
      VirtualAccountEventBatch.php

      # optional payout/refund normalization
      PayoutEvent.php
      RefundEvent.php

  Drivers/
    AbstractPaymentGatewayDriver.php
    Concerns/
      HasConfigSchema.php
      ResolvesConfig.php
      MapsStatuses.php
      RedactsSecrets.php
      BuildsManifest.php

  Manager/
    GatewayRegistry.php
    DriverResolver.php
    GatewayManager.php

  Support/
    Assert.php
    Clock.php
    Redactor.php
    Signature.php
    Idempotency.php
    Pagination.php
    DedupeScripts.php

  Exceptions/
    GatewayDriverNotFoundException.php
    GatewayConfigException.php
    GatewayCapabilityException.php
    GatewayRuntimeException.php

  Types/
    Dict.php

```

---

6) Canonical statuses and normalization
---------------------------------------

[](#6-canonical-statuses-and-normalization)

### CanonicalPaymentStatus

[](#canonicalpaymentstatus)

Your driver must normalize provider statuses to a closed set.

---

7) Contracts (Interfaces)
-------------------------

[](#7-contracts-interfaces)

### 7.1 Base driver contract (Required)

[](#71-base-driver-contract-required)

Defines the minimum every driver must support: identity, config schema, validation, health check, and redaction.

### 7.2 Manifest provider contract (Strongly recommended)

[](#72-manifest-provider-contract-strongly-recommended)

Allows hosts to sync a snapshot of gateway capabilities for DB‑first checkout filtering.

### 7.3 Availability contract (Optional)

[](#73-availability-contract-optional)

Allow hosts to cheaply hide gateways based on lightweight context (country, user type, etc.).

---

8) Frontend integration
-----------------------

[](#8-frontend-integration)

### Scripts

[](#scripts)

Drivers may provide `GatewayScript[]` (tags, URLs, placement) for the host to inject.

### UI manifest

[](#ui-manifest)

Drivers may provide module keys (host maps keys → actual components).

---

9) Saved methods and card tokenization
--------------------------------------

[](#9-saved-methods-and-card-tokenization)

Saved methods and tokenization are optional capability contracts.

---

10) Virtual accounts (correctness requirement)
----------------------------------------------

[](#10-virtual-accounts-correctness-requirement)

If a driver supports virtual accounts (`PaymentGatewayVirtualAccountsContract`), it must implement at least one watcher:

- `PaymentGatewayVirtualAccountWebhookWatcherContract` **or**
- `PaymentGatewayVirtualAccountPollingWatcherContract`

Optionally implement reconciliation.

---

11) Payouts (general withdrawals)
---------------------------------

[](#11-payouts-general-withdrawals)

Optional contracts: payouts + beneficiaries.

---

12) Virtual cards (Optional)
----------------------------

[](#12-virtual-cards-optional)

Optional contract.

---

13) Generic reconciliation &amp; diagnostics
--------------------------------------------

[](#13-generic-reconciliation--diagnostics)

Optional contracts.

---

14) The Manager (host-side resolver)
------------------------------------

[](#14-the-manager-host-side-resolver)

The host uses a registry to map `driver_key` (from the DB model) to a driver class.

### Responsibilities

[](#responsibilities)

- Register installed drivers
- Resolve a driver instance using a `ConfigBag`
- Optionally trigger manifest sync

```
namespace PayKit\Manager;

use PayKit\Contracts\PaymentGatewayDriverContract;
use Timeax\ConfigSchema\Support\ConfigBag;

...
```

> Your host app can wrap `make()` with DB resolution (PaymentGateway row → driverKey + config).

### Optional: `Pay` entrypoint (SDK facade)

[](#optional-pay-entrypoint-sdk-facade)

If you prefer **one import** (and static access) instead of injecting `GatewayManager` everywhere, the SDK includes a small entrypoint: `PayKit\Pay`.

It wraps a singleton `GatewayManager`/`GatewayRegistry` and provides:

- `Pay::register($driverKey, DriverClass::class, ...)`
- `Pay::registerGateway(GatewayRegistration $reg)`
- `Pay::setProvider($providerClass)`
- `Pay::driver($driverKey, ConfigBag $config)`
- `Pay::via($source)`
- `Pay::list($filter)`

#### Host adapter contract

[](#host-adapter-contract)

```
namespace PayKit\Contracts;

use Timeax\ConfigSchema\Support\ConfigBag;

interface ProvidesGatewayConfigContract
{
    public function gatewayDriverKey(): string;

    public function gatewayConfig(): ConfigBag;
}
```

#### Usage (host)

[](#usage-host)

**Register drivers once** (Laravel ServiceProvider, bootstrap file, etc.):

```
use PayKit\Pay;

// 1. Basic registration
Pay::register('stripe', \App\Payments\Drivers\StripeDriver::class);

// 2. Register with a concrete gateway ID (enables Pay::via(12))
Pay::register(
    'paystack',
    \App\Payments\Drivers\PaystackDriver::class,
    gatewayId: 12,
    providerClass: \App\Models\PaymentGateway::class
);

// 3. Set a default provider class for all gateway IDs
Pay::setProvider(\App\Models\PaymentGateway::class);
Pay::register('flutterwave', \App\Payments\Drivers\FlutterwaveDriver::class, gatewayId: 'fw_001');

// 4. Register a gateway entry manually (if driver is already registered)
use PayKit\Payload\Common\GatewayRegistration;

Pay::registerGateway(new GatewayRegistration(
    gatewayId: 'fw_002',
    driverKey: 'flutterwave',
    providerClass: \App\Models\PaymentGateway::class
));
```

**Resolve driver** (recommended DX):

```
use PayKit\Pay;

// A) From a DB model (implements ProvidesGatewayConfigContract)
// Returns PaymentGatewayPayDriverContract (asserts pay capability)
$driver = Pay::via($gatewayModel);

// B) From a gateway ID (requires registration with gatewayId)
$driver = Pay::via(12);
$driver = Pay::via('fw_001');

// C) From driver key + config
$driver = Pay::via('stripe', $config);

// Use the driver
$result = $driver->initiatePayment($payload);
```

**List available gateways**:

```
use PayKit\Pay;
use PayKit\Payload\Requests\GatewayListFilter;
use PayKit\Payload\Common\Currency;
use PayKit\Payload\Common\Country;

// Filter by currency, country, or features
$list = Pay::list(new GatewayListFilter(
    currency: new Currency('USD'),
    country: new Country('US')
), includeDriversWithoutGateways: false);
```

**Manual resolution** (tests / scripts):

```
use PayKit\Pay;
use Timeax\ConfigSchema\Support\ConfigBag;

// Returns PaymentGatewayDriverContract (no capability assertion)
$driver = Pay::driver('stripe', new ConfigBag(
    secrets: ['secret_key' => '...'],
    options: ['environment' => 'test'],
));
```

---

15) Host flows (how it works end‑to‑end)
----------------------------------------

[](#15-host-flows-how-it-works-endtoend)

### Flow A — Install/Enable gateway (manifest sync)

[](#flow-a--installenable-gateway-manifest-sync)

1. Host resolves driver and loads `ConfigBag` from DB.
2. Host calls `getManifest()`.
3. Host persists:

- supported currencies/countries
- feature flags (by contract)
- any UI metadata

### Flow B — Render checkout page (scripts/UI)

[](#flow-b--render-checkout-page-scriptsui)

1. Host filters gateways using the stored manifest.
2. Host injects scripts (if any).
3. Host maps `UiManifest` keys to frontend components.

### Flow C — Initiate payment

[](#flow-c--initiate-payment)

1. Host creates `PaymentInitiateRequest`.
2. Host calls `initiatePayment()`.
3. Host renders the `NextAction`.

### Flow D — Verify / Webhook

[](#flow-d--verify--webhook)

1. Host calls `verifyPayment()` (pull) **or** validates and parses webhook (push).
2. Host applies business effect (credit wallet, create orders, etc.).

---

16) Implementation samples (Stripe)
-----------------------------------

[](#16-implementation-samples-stripe)

This section shows a practical implementation pattern using a `StripeDriver` as the subject driver.

### 16.1 Minimal host adapter (DB model)

[](#161-minimal-host-adapter-db-model)

Your host gateway record should implement `ProvidesGatewayConfigContract`.

```
namespace App\Models;

use PayKit\Contracts\ProvidesGatewayConfigContract;
use Timeax\ConfigSchema\Support\ConfigBag;

final class PaymentGateway /* extends Model */ implements ProvidesGatewayConfigContract
{
    public string $driver_key;

    /** @var array */
    public array $secrets = [];

    /** @var array */
    public array $options = [];

    public function gatewayDriverKey(): string
    {
        return $this->driver_key;
    }

    public function gatewayConfig(): ConfigBag
    {
        // secrets should come from encrypted storage in the real host.
        return new ConfigBag(secrets: $this->secrets, options: $this->options);
    }
}
```

### 16.2 Stripe config schema

[](#162-stripe-config-schema)

A typical Stripe gateway needs:

- `secret_key` (required)
- `publishable_key` (required)
- `webhook_secret` (optional unless webhooks are enabled)

```
namespace App\Payments\Drivers;

use PayKit\Drivers\AbstractPaymentGatewayDriver;
use PayKit\Drivers\Concerns\HasConfigSchema;
use PayKit\Drivers\Concerns\ResolvesConfig;
use Timeax\ConfigSchema\Schema\ConfigField;
use Timeax\ConfigSchema\Support\ConfigBag;
use Timeax\ConfigSchema\Support\ConfigValidationResult;

final class StripeDriver extends AbstractPaymentGatewayDriver
{
    use ResolvesConfig;
    use HasConfigSchema;

    public function driverKey(): string
    {
        return 'stripe';
    }

    /** @return array */
    protected function configFields(): array
    {
        return [
            new ConfigField(
                name: 'secret_key',
                label: 'Stripe Secret Key',
                required: true,
                secret: true,
            ),
            new ConfigField(
                name: 'publishable_key',
                label: 'Stripe Publishable Key',
                required: true,
            ),
            new ConfigField(
                name: 'webhook_secret',
                label: 'Webhook Signing Secret',
                required: false,
                secret: true,
            ),
        ];
    }

    public function validateConfig(?ConfigBag $config = null): ConfigValidationResult
    {
        $cfg = $this->resolveConfig($config);
        return $this->configSchema()->validate($cfg);
    }
}
```

> Notes:
>
> - `HasConfigSchema::validateConfig(...)` uses `resolveConfig(...)` if available and validates required fields.
> - Config values live in `ConfigBag` `secrets` / `options` — the driver reads from the resolved config.

### 16.3 Stripe health check (required)

[](#163-stripe-health-check-required)

PayKit requires drivers to implement their own health check (no default stub).

```
use Timeax\ConfigSchema\Support\ConfigBag;
use PayKit\Payload\Common\HealthCheckResult;

final class StripeDriver extends AbstractPaymentGatewayDriver
{
    // ... config schema above

    public function healthCheck(?ConfigBag $config = null): HealthCheckResult
    {
        $cfg = $this->resolveConfig($config);

        // Pseudo-check: confirm we have the required keys.
        // Real driver might do a lightweight API call (e.g. retrieve account / balance).
        $secret = $cfg->secret('secret_key');

        if (!$secret) {
            return HealthCheckResult::fail('Missing secret_key');
        }

        return HealthCheckResult::ok('Stripe config looks valid');
    }
}
```

### 16.4 Initiate payment (NextAction)

[](#164-initiate-payment-nextaction)

Most Stripe flows return an inline action using a client secret.

```
namespace App\Payments\Drivers;

use PayKit\Contracts\PaymentGatewayPaymentsContract;
use Timeax\ConfigSchema\Support\ConfigBag;
use PayKit\Payload\Common\CanonicalPaymentStatus;
use PayKit\Payload\Requests\PaymentInitiateRequest;
use PayKit\Payload\Responses\InlineAction;
use PayKit\Payload\Responses\NextAction;
use PayKit\Payload\Responses\PaymentInitiateResult;

final class StripeDriver extends AbstractPaymentGatewayDriver implements PaymentGatewayPaymentsContract
{
    // ... traits + config schema + healthCheck

    public function initiatePayment(PaymentInitiateRequest $request, ?ConfigBag $config = null): PaymentInitiateResult
    {
        $cfg = $this->resolveConfig($config);

        // Pseudo: create a PaymentIntent and return client secret.
        // $clientSecret = $this->stripe($cfg)->paymentIntents->create([...])->client_secret;
        $clientSecret = 'pi_xxx_secret_yyy';

        $action = new InlineAction(
            entry: 'stripe.checkout',
            props: [
                'publishableKey' => $cfg->secret('publishable_key'),
                'clientSecret' => $clientSecret,
                'reference' => (string) $request->reference,
            ],
        );

        return new PaymentInitiateResult(
            reference: $request->reference,
            providerRef: null,
            action: new NextAction($action),
            raw: null,
        );
    }

    public function mapStatus(mixed $rawPayload): CanonicalPaymentStatus
    {
        // Pseudo mapping. A real driver maps provider status fields to canonical enum.
        return CanonicalPaymentStatus::processing;
    }
}
```

### 16.5 Scripts + UI manifest

[](#165-scripts--ui-manifest)

Stripe typically needs `https://js.stripe.com/v3/` on checkout pages.

```
use PayKit\Contracts\PaymentGatewayScriptsContract;
use Timeax\ConfigSchema\Support\ConfigBag;
use PayKit\Payload\Common\GatewayScript;
use PayKit\Payload\Common\ScriptLocation;

final class StripeDriver extends AbstractPaymentGatewayDriver implements PaymentGatewayScriptsContract
{
    public function getScripts(?ConfigBag $config = null): array
    {
        return [
            new GatewayScript(
                src: 'https://js.stripe.com/v3/',
                location: ScriptLocation::head,
                async: true,
            ),
        ];
    }
}
```

UI modules are host-mapped keys (the SDK never ships React/Vue components).

```
use PayKit\Contracts\PaymentGatewayUiContract;
use PayKit\Payload\Common\UiEntryDescriptor;
use PayKit\Payload\Common\UiManifest;
use PayKit\Payload\Common\UiModuleDescriptor;

final class StripeDriver extends AbstractPaymentGatewayDriver implements PaymentGatewayUiContract
{
    public function uiManifest(): UiManifest
    {
        return new UiManifest([
            new UiModuleDescriptor(
                id: 'stripe.settings',
                entry: new UiEntryDescriptor(key: 'stripe.settings'),
            ),
            new UiModuleDescriptor(
                id: 'stripe.checkout',
                entry: new UiEntryDescriptor(key: 'stripe.checkout'),
            ),
        ]);
    }
}
```

### 16.6 Webhook verification + parsing

[](#166-webhook-verification--parsing)

If implementing `PaymentGatewayWebhooksContract`, the driver must verify signature and parse to a normalized event payload.

```
use PayKit\Contracts\PaymentGatewayWebhooksContract;
use Timeax\ConfigSchema\Support\ConfigBag;
use PayKit\Payload\Events\WebhookEvent;
use PayKit\Payload\Requests\WebhookRequest;
use PayKit\Payload\Responses\WebhookVerifyResult;

final class StripeDriver extends AbstractPaymentGatewayDriver implements PaymentGatewayWebhooksContract
{
    public function verifyWebhook(WebhookRequest $request, ?ConfigBag $config = null): WebhookVerifyResult
    {
        $cfg = $this->resolveConfig($config);

        // Pseudo signature validation. Real driver uses Stripe signature header + webhook secret.
        $secret = $cfg->secret('webhook_secret');
        if (!$secret) {
            return WebhookVerifyResult::fail('Missing webhook secret');
        }

        return WebhookVerifyResult::ok();
    }

    public function parseWebhook(WebhookRequest $request, ?ConfigBag $config = null): WebhookEvent
    {
        // Pseudo parse. Real driver decodes JSON and normalizes event type + provider refs.
        return new WebhookEvent(
            type: 'payment.succeeded',
            reference: null,
            providerRef: null,
            status: null,
            raw: $request->payload,
        );
    }
}
```

### 16.7 Stripe manifest (sync-time discovery)

[](#167-stripe-manifest-sync-time-discovery)

The host calls this during install/enable/config update and persists the result.

```
use PayKit\Contracts\PaymentGatewayManifestProviderContract;
use Timeax\ConfigSchema\Support\ConfigBag;
use PayKit\Payload\Common\GatewayFeatureSet;
use PayKit\Payload\Common\GatewayManifest;
use PayKit\Payload\Common\GatewaySupportMatrix;

final class StripeDriver extends AbstractPaymentGatewayDriver implements PaymentGatewayManifestProviderContract
{
    public function getManifest(?ConfigBag $config = null): GatewayManifest
    {
        // Allowed to call provider APIs here if needed (sync-time only).
        // For sample: hard-coded.

        $features = new GatewayFeatureSet(
            payments: true,
            refunds: true,
            savedMethods: true,
            webhooks: true,
            payouts: false,
            virtualAccounts: false,
        );

        $support = GatewaySupportMatrix::fromSimple(
            countries: ['US', 'GB', 'NG'],
            currencies: ['USD', 'GBP', 'NGN'],
        );

        return new GatewayManifest(
            driverKey: $this->driverKey(),
            features: $features,
            support: $support,
            ui: $this->uiManifest(),
            scripts: $this->getScripts(),
        );
    }
}
```

---

17) Watchouts (implementation risks)
------------------------------------

[](#17-watchouts-implementation-risks)

### A) Payload serialization

[](#a-payload-serialization)

- No closures, resources, SDK client objects, or open streams.
- Prefer scalar/array/payload-only fields.
- Keep raw provider payloads as `array|string|null`.

### B) UI manifest complexity

[](#b-ui-manifest-complexity)

- Backend returns module/component keys (+ optional props/schema)
- Host frontend owns a mapping registry (`"stripe.settings" -> `)
- Avoid backend-driven UI layout instructions.

### C) Versioning payloads

[](#c-versioning-payloads)

- Adding optional fields: **minor**.
- Renaming/removing/changing meaning: **major**.
- Prefer nullable additions to preserve driver compatibility.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance87

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

10

Last Release

106d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/32748fea7a55e27b70fdc139376954e97b78d1e2511ee30d88688398a5a73f03?d=identicon)[timeax](/maintainers/timeax)

---

Top Contributors

[![timeax](https://avatars.githubusercontent.com/u/105345335?v=4)](https://github.com/timeax "timeax (43 commits)")

---

Tags

contractssdkpaymentsdtopayment gateway

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/timeax-paykit-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/timeax-paykit-sdk/health.svg)](https://phpackages.com/packages/timeax-paykit-sdk)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
