PHPackages                             amolood/zakhir-laravel - 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. [Payment Processing](/categories/payments)
4. /
5. amolood/zakhir-laravel

ActiveLibrary[Payment Processing](/categories/payments)

amolood/zakhir-laravel
======================

Official Laravel package for the Zakhir payment gateway

v1.0.0(3w ago)01MITPHPPHP ^8.2

Since May 14Pushed 3w agoCompare

[ Source](https://github.com/amolood/Zakhir-Laravel)[ Packagist](https://packagist.org/packages/amolood/zakhir-laravel)[ Docs](https://github.com/amolood/Zakhir-Laravel)[ RSS](/packages/amolood-zakhir-laravel/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (7)Versions (2)Used By (0)

 [![Zakhir](https://raw.githubusercontent.com/amolood/Zakhir-Laravel/main/art/logo.png)](https://raw.githubusercontent.com/amolood/Zakhir-Laravel/main/art/logo.png)Laravel Zakhir
==============

[](#laravel-zakhir)

 Official Laravel package for integrating the **Zakhir** payment gateway.
 Create payments, poll status, handle webhooks — with full audit logging and polymorphic model support.

 [![Latest Version](https://camo.githubusercontent.com/9be3f37b2cb141467deba95169d9019ca76220a65f1f586418da5522e0ad0210/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616d6f6c6f6f642f7a616b6869722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/amolood/zakhir-laravel) [![PHP Version](https://camo.githubusercontent.com/7cf6efd9426be7940437b7614c00697e0402b6dc9c2339173aec2249fca3280a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616d6f6c6f6f642f7a616b6869722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/amolood/zakhir-laravel) [![License](https://camo.githubusercontent.com/a77d51b95246e511d9d0949c11105e088bb6ddc4530a11700d3f4b533cca5831/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616d6f6c6f6f642f7a616b6869722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/amolood/zakhir-laravel) [![Total Downloads](https://camo.githubusercontent.com/15ec02d57604773387909af5e59d510ada0146d834cb09d3998849d56dde9785/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616d6f6c6f6f642f7a616b6869722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/amolood/zakhir-laravel)

 Built by [Digitalize Lab](https://digitalize.sd) · Maintained by [Abdalrahman Molood](https://amolood.com)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
    - [Options Reference](#options-reference)
    - [Staging Environment](#staging-environment)
- [Usage](#usage)
    - [Create a Payment](#create-a-payment)
    - [Poll Payment Status](#poll-payment-status)
    - [Cancel a Payment](#cancel-a-payment)
    - [Using the Facade](#using-the-facade)
    - [Dependency Injection](#dependency-injection)
- [Webhook Handling](#webhook-handling)
    - [Registering a Payment](#registering-a-payment)
    - [Listening to Events](#listening-to-events)
- [Database](#database)
    - [Migrations](#migrations)
    - [ZakhirPayment Model](#zakhirpayment-model)
    - [ZakhirLog Model](#zakhirlog-model)
- [Events Reference](#events-reference)
- [Exception Handling](#exception-handling)
- [Architecture Overview](#architecture-overview)
- [Testing](#testing)
- [Changelog](#changelog)
- [Credits](#credits)
- [License](#license)

---

Requirements
------------

[](#requirements)

DependencyVersionPHP`^8.2`Laravel`^10.0 | ^11.0 | ^12.0`---

Installation
------------

[](#installation)

Install via Composer:

```
composer require amolood/zakhir-laravel
```

Laravel's auto-discovery will register the service provider and `Zakhir` facade automatically. No manual registration needed.

Publish the configuration file:

```
php artisan vendor:publish --tag=zakhir-config
```

Run the migrations:

```
php artisan migrate
```

> If you prefer to publish migrations instead of letting the package load them automatically:
>
> ```
> php artisan vendor:publish --tag=zakhir-migrations
> ```

---

Configuration
-------------

[](#configuration)

All configuration lives in `config/zakhir.php`. After publishing, open that file and fill in your values directly — no `.env` entries are required.

```
php artisan vendor:publish --tag=zakhir-config
```

```
// config/zakhir.php

return [

    // "production" or "staging"
    'environment' => 'production',

    // Production credentials — from your Zakhir merchant dashboard
    'base_url' => 'https://zakhir.net/api/',
    'tenant'   => 'your_tenant_id',
    'profile'  => 'your_profile_id',
    'api_key'  => 'your_api_key',

    // Staging credentials — used when environment is "staging"
    'staging_base_url' => '',
    'staging_tenant'   => '',
    'staging_profile'  => '',
    'staging_api_key'  => '',

    // Where Zakhir POSTs payment status notifications
    'webhook_url' => 'https://yourdomain.com/api/zakhir/webhook',

    // Where customers are redirected after checkout
    'return_url' => 'https://yourdomain.com/orders/return',

    // HTTP timeout in seconds
    'timeout' => 15,

    // Log every API request and response to the zakhir_logs table
    'logging' => true,

    'routes' => [
        'enabled'    => true,
        'prefix'     => 'api/zakhir',    // webhook available at POST /api/zakhir/webhook
        'middleware' => ['api'],
    ],

];
```

### Options Reference

[](#options-reference)

KeyTypeDescription`environment``string``"production"` or `"staging"``base_url``string`Production API base URL`tenant``string`Merchant tenant ID`profile``string`Merchant profile ID`api_key``string`API key for request authentication`staging_base_url``string`Staging API base URL`staging_tenant``string`Staging tenant ID`staging_profile``string`Staging profile ID`staging_api_key``string`Staging API key`webhook_url``string`Public URL where Zakhir sends status callbacks`return_url``string`URL customers land on after checkout`timeout``int`HTTP request timeout in seconds`logging``bool`Write every API call to `zakhir_logs` table`routes.enabled``bool`Auto-register the built-in webhook route`routes.prefix``string`URL prefix for the webhook route`routes.middleware``array`Middleware applied to the webhook route### Staging Environment

[](#staging-environment)

Set `environment` to `"staging"` and fill in the staging credentials block. The package selects the correct set of credentials automatically — no other changes needed.

```
'environment'    => 'staging',
'staging_base_url' => 'https://staging.zakhir.net/api/',
'staging_tenant'   => 'staging_tenant_id',
'staging_profile'  => 'staging_profile_id',
'staging_api_key'  => 'staging_api_key',
```

---

Usage
-----

[](#usage)

### Create a Payment

[](#create-a-payment)

```
use Zakhir\LaravelZakhir\ZakhirPaymentService;
use Zakhir\LaravelZakhir\Data\PaymentResponse;

$zakhir = app(ZakhirPaymentService::class);

$response = $zakhir->createPayment(
    amount: 250.00,            // in SDG (or your configured currency)
    currency: 'SDG',
    note: 'Order #1024',
    returnUrl: 'https://yourdomain.com/orders/1024', // optional, falls back to config
    notifyUrl: 'https://yourdomain.com/api/zakhir/webhook', // optional, falls back to config
    referenceId: null,         // optional — a UUID is auto-generated if omitted
);

// Redirect the customer to the Zakhir checkout page
return redirect($response->checkoutUrl);
```

The returned `PaymentResponse` object exposes:

PropertyTypeDescription`$id``string`Zakhir's internal payment ID`$referenceId``string`The UUID sent in the request (store this to poll/cancel later)`$status``string``PENDING`, `COMPLETED`, etc.`$checkoutUrl``string|null`Hosted checkout page URL — redirect your customer here`$mobileAppUrl``string|null`Deep link for mobile Zakhir app`$paymentToken``string|null`Short-lived payment token`$paymentTokenExpiresAt``string|null`ISO 8601 expiry timestamp`$raw``array`Full raw API response```
$response->isPending();    // true
$response->isCompleted();  // false
```

### Poll Payment Status

[](#poll-payment-status)

Use this to check the current state of a payment without waiting for a webhook:

```
$status = $zakhir->getPaymentStatus($referenceId);

if ($status->isCompleted()) {
    // Mark your order as paid
}

if ($status->isRejected()) {
    // Notify the customer
}
```

`PaymentStatusResponse` properties:

PropertyTypeDescription`$referenceId``string`Your original referenceId`$status``string``PENDING` / `COMPLETED` / `REJECTED``$id``string`Zakhir's payment ID`$raw``array`Full raw API response```
$status->isPending();    // bool
$status->isCompleted();  // bool
$status->isRejected();   // bool
```

### Cancel a Payment

[](#cancel-a-payment)

Cancel a `PENDING` payment that has no transaction attached yet:

```
$result = $zakhir->cancelPayment($referenceId);
```

Returns the raw response array from the Zakhir API.

### Using the Facade

[](#using-the-facade)

All methods are also available via the `Zakhir` facade:

```
use Zakhir\LaravelZakhir\Facades\Zakhir;

$response = Zakhir::createPayment(250.00, 'SDG', 'Order #1024');

$status = Zakhir::getPaymentStatus($referenceId);

Zakhir::cancelPayment($referenceId);
```

### Dependency Injection

[](#dependency-injection)

Inject `ZakhirPaymentService` directly into your controllers or services:

```
use Zakhir\LaravelZakhir\ZakhirPaymentService;

class CheckoutController extends Controller
{
    public function __construct(
        private readonly ZakhirPaymentService $zakhir,
    ) {}

    public function pay(Order $order)
    {
        $response = $this->zakhir->createPayment(
            amount: $order->total,
            currency: 'SDG',
            note: "Order #{$order->id}",
        );

        // Store the referenceId so you can look it up later
        $order->update(['zakhir_reference_id' => $response->referenceId]);

        return redirect($response->checkoutUrl);
    }
}
```

---

Webhook Handling
----------------

[](#webhook-handling)

The package registers a webhook endpoint automatically at:

```
POST /api/zakhir/webhook

```

The route prefix is configurable via `ZAKHIR_ROUTE_PREFIX`. To disable the built-in route entirely and register your own, set:

```
ZAKHIR_ROUTES_ENABLED=false
```

Then point to your own controller that resolves `ZakhirWebhookController` or implements the same logic.

### Registering a Payment

[](#registering-a-payment)

Before Zakhir's webhook can update a payment, you must create a `ZakhirPayment` record **after** calling `createPayment`. This record is the package's local representation of the payment:

```
use Zakhir\LaravelZakhir\Models\ZakhirPayment;

$response = Zakhir::createPayment(250.00, 'SDG', "Order #{$order->id}");

ZakhirPayment::create([
    'transaction_id'    => 'zakhir-pending-' . $response->referenceId,
    'reference_id'      => $response->referenceId,
    'gateway_reference' => $response->id,
    'payable_id'        => $order->id,
    'payable_type'      => Order::class,
    'amount'            => 25000,     // store in piasters (SDG × 100)
    'currency'          => 'SDG',
    'status'            => 'PENDING',
]);
```

When Zakhir sends a `COMPLETED` webhook, the controller updates the record atomically (row-level lock, idempotency guard) and dispatches `ZakhirPaymentCompleted`.

### Listening to Events

[](#listening-to-events)

Register listeners in your `EventServiceProvider` (or using `#[AsEventListener]`):

```
use Zakhir\LaravelZakhir\Events\ZakhirPaymentCompleted;
use Zakhir\LaravelZakhir\Events\ZakhirPaymentFailed;
use Zakhir\LaravelZakhir\Events\ZakhirWebhookReceived;

// AppServiceProvider or EventServiceProvider
Event::listen(ZakhirPaymentCompleted::class, function ($event) {
    $payment = $event->payment; // ZakhirPayment model (already COMPLETED)

    $order = Order::find($payment->payable_id);
    $order->markAsPaid();
    $order->customer->notify(new OrderConfirmed($order));
});

Event::listen(ZakhirPaymentFailed::class, function ($event) {
    // $event->payload      → WebhookPayload DTO
    // $event->localPayableId   → your model's ID
    // $event->localPayableType → your model's class
});

Event::listen(ZakhirWebhookReceived::class, function ($event) {
    // Fired for every webhook regardless of status — useful for raw auditing
    // $event->payload → WebhookPayload DTO
});
```

Or use a dedicated listener class:

```
class HandleZakhirPayment
{
    public function handle(ZakhirPaymentCompleted $event): void
    {
        $payment = $event->payment;
        // ...
    }
}
```

---

Database
--------

[](#database)

### Migrations

[](#migrations)

Two tables are created:

TablePurpose`zakhir_payments`One row per payment attempt; tracks status, amount, and raw payload`zakhir_logs`Append-only audit log of every outgoing API request/responseMigrations are loaded automatically. To publish them instead:

```
php artisan vendor:publish --tag=zakhir-migrations
```

### ZakhirPayment Model

[](#zakhirpayment-model)

`Zakhir\LaravelZakhir\Models\ZakhirPayment`

ColumnTypeDescription`id``bigint`Auto-increment primary key`transaction_id``string`Unique internal ID — format `zakhir-{seed}``gateway_reference``string|null`Zakhir's own payment ID`reference_id``string`UUID sent as `referenceId` in the API request`payable_id``int`ID of the related local model`payable_type``string`Class of the related local model`amount``bigint`Amount in smallest unit (piasters for SDG)`currency``string(3)`ISO currency code, e.g. `SDG``status``string``PENDING` / `COMPLETED` / `FAILED``raw_payload``json|null`Full webhook or API response payload`paid_at``timestamp|null`When the payment completed**Polymorphic relation** — attach payments to any Eloquent model:

```
// On your Invoice / Order model
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Zakhir\LaravelZakhir\Models\ZakhirPayment;

public function zakhirPayments(): MorphMany
{
    return $this->morphMany(ZakhirPayment::class, 'payable');
}
```

```
$completedPayments = $order->zakhirPayments()->where('status', 'COMPLETED')->get();
```

### ZakhirLog Model

[](#zakhirlog-model)

`Zakhir\LaravelZakhir\Models\ZakhirLog`

Every outgoing API call is recorded automatically when `ZAKHIR_LOGGING=true`:

ColumnTypeDescription`id``bigint`Auto-increment primary key`direction``string``outgoing` or `incoming``method``string`HTTP verb`url``string`Full endpoint URL`ip``string|null`Client IP (incoming only)`status_code``smallint`HTTP status code`request_body``json|null`Request payload`response_body``json|null`Response payload`duration_ms``int`Round-trip time in milliseconds`created_at``timestamp`Log timestamp> Logging failures are silently swallowed — a broken log table will never block a payment.

---

Events Reference
----------------

[](#events-reference)

EventWhenPayload`ZakhirWebhookReceived`Every incoming webhook`WebhookPayload $payload``ZakhirPaymentCompleted`Webhook `status=COMPLETED`, after DB update`ZakhirPayment $payment``ZakhirPaymentFailed`Webhook `status=REJECTED`, after DB update`WebhookPayload $payload`, `int $localPayableId`, `string $localPayableType`### `WebhookPayload` DTO

[](#webhookpayload-dto)

```
$payload->id;            // string — Zakhir's payment ID
$payload->referenceId;   // string — your original referenceId
$payload->status;        // PaymentStatus enum
$payload->raw;           // array — full raw webhook body
```

### `PaymentStatus` Enum

[](#paymentstatus-enum)

```
use Zakhir\LaravelZakhir\Enums\PaymentStatus;

PaymentStatus::Pending;    // 'PENDING'
PaymentStatus::Completed;  // 'COMPLETED'
PaymentStatus::Rejected;   // 'REJECTED'
PaymentStatus::Unknown;    // 'UNKNOWN'

$status->isTerminal();     // true for Completed and Rejected
```

---

Exception Handling
------------------

[](#exception-handling)

All package exceptions extend `ZakhirException` (which extends `RuntimeException`):

ExceptionThrown when`ZakhirException`Base class — gateway disabled, missing config, invalid response`ZakhirApiException`Zakhir API returns a non-2xx HTTP response`ZakhirWebhookException`Webhook payload is missing a required field or fails signature check```
use Zakhir\LaravelZakhir\Exceptions\ZakhirException;
use Zakhir\LaravelZakhir\Exceptions\ZakhirApiException;

try {
    $response = Zakhir::createPayment(250.00, 'SDG', 'Order #1024');
} catch (ZakhirApiException $e) {
    // HTTP-level error from the Zakhir API
    logger()->error('Zakhir API error', [
        'status'   => $e->statusCode,
        'body'     => $e->responseBody,
        'message'  => $e->getMessage(),
    ]);
} catch (ZakhirException $e) {
    // Configuration issue or invalid response
    logger()->error('Zakhir error: ' . $e->getMessage());
}
```

`ZakhirApiException` exposes two read-only properties:

```
$e->statusCode;    // int  — HTTP status code (401, 422, 500, …)
$e->responseBody;  // array — decoded JSON response body
```

---

Architecture Overview
---------------------

[](#architecture-overview)

```
src/
├── ZakhirServiceProvider.php          Auto-discovery, DI bindings, routes, migrations
├── ZakhirPaymentService.php           Public API — createPayment / getPaymentStatus / cancelPayment
│
├── Contracts/
│   └── ZakhirClientInterface.php      Interface for the HTTP client (swap or mock in tests)
│
├── Http/
│   ├── ZakhirConfig.php               Reads config; handles prod/staging switching
│   ├── ZakhirClient.php               HTTP layer — all Zakhir API calls + logging
│   ├── Controllers/
│   │   └── ZakhirWebhookController.php  Processes COMPLETED / REJECTED webhooks
│   └── Middleware/
│       └── VerifyZakhirWebhookSignature.php  Passthrough middleware (no signature required)
│
├── Data/                              Typed DTOs — no raw arrays leaking across boundaries
│   ├── CreatePaymentData.php
│   ├── PaymentResponse.php
│   ├── PaymentStatusResponse.php
│   └── WebhookPayload.php
│
├── Enums/
│   └── PaymentStatus.php              PENDING / COMPLETED / REJECTED / UNKNOWN
│
├── Events/
│   ├── ZakhirWebhookReceived.php
│   ├── ZakhirPaymentCompleted.php
│   └── ZakhirPaymentFailed.php
│
├── Exceptions/
│   ├── ZakhirException.php
│   ├── ZakhirApiException.php
│   └── ZakhirWebhookException.php
│
├── Facades/
│   └── Zakhir.php
│
├── Models/
│   ├── ZakhirPayment.php              Polymorphic payment record
│   └── ZakhirLog.php                 Append-only API audit log
│
└── Support/
    └── ZakhirLogger.php               Writes to zakhir_logs; silently skips on DB failure

```

**Key design decisions:**

- **Idempotent webhooks** — every status update runs inside a `DB::transaction()` with `lockForUpdate()`, so replayed or concurrent webhooks are safe.
- **Polymorphic `ZakhirPayment`** — attach payments to any Eloquent model (Order, Invoice, Subscription…) without modifying the package.
- **Events over tight coupling** — the package fires events; your application decides what to do.
- **Logging never crashes** — `ZakhirLogger` catches all exceptions internally so a broken `zakhir_logs` table can never block a live payment.
- **Interface-bound client** — `ZakhirClientInterface` lets you swap or mock the HTTP client cleanly in tests.

---

Testing
-------

[](#testing)

The package ships with a full PHPUnit suite using [Orchestra Testbench](https://github.com/orchestral/testbench).

```
composer install
./vendor/bin/phpunit
```

In your own application, use Laravel's `Http::fake()` to mock Zakhir API calls without hitting the real gateway:

```
use Illuminate\Support\Facades\Http;
use Zakhir\LaravelZakhir\Facades\Zakhir;

Http::fake([
    '*/payments' => Http::response([
        'id'          => 'zakhir-id-001',
        'referenceId' => 'test-uuid',
        'status'      => 'PENDING',
        'checkoutPage' => [
            'url' => 'https://zakhir.net/pay/test',
        ],
    ], 200),
]);

$response = Zakhir::createPayment(100.00, 'SDG', 'Test payment');

$this->assertEquals('PENDING', $response->status);
$this->assertNotEmpty($response->checkoutUrl);
```

To test webhook handling, use `ZakhirPaymentCompleted` with `Event::fake()`:

```
use Illuminate\Support\Facades\Event;
use Zakhir\LaravelZakhir\Events\ZakhirPaymentCompleted;
use Zakhir\LaravelZakhir\Models\ZakhirPayment;

Event::fake();

ZakhirPayment::create([
    'transaction_id' => 'zakhir-pending-ref-001',
    'reference_id'   => 'ref-001',
    'payable_id'     => 1,
    'payable_type'   => Order::class,
    'amount'         => 10000,
    'currency'       => 'SDG',
    'status'         => 'PENDING',
]);

$this->postJson('/api/zakhir/webhook', [
    'id'          => 'gw-id-001',
    'referenceId' => 'ref-001',
    'status'      => 'COMPLETED',
])->assertOk();

Event::assertDispatched(ZakhirPaymentCompleted::class);

$this->assertDatabaseHas('zakhir_payments', [
    'reference_id' => 'ref-001',
    'status'       => 'COMPLETED',
]);
```

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a full history of releases and changes.

---

Credits
-------

[](#credits)

**Package Author**[Abdalrahman Molood](https://amolood.com)**Company**[Digitalize Lab](https://digitalize.sd)**Payment Gateway**[Zakhir](https://zakhir.net/)Contributions, issues, and pull requests are welcome.

---

License
-------

[](#license)

This package is open-source software licensed under the [MIT License](LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance94

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 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

Unknown

Total

1

Last Release

26d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8304924?v=4)[ABDALRAHMAN MOLOOD](/maintainers/amolood)[@amolood](https://github.com/amolood)

---

Top Contributors

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

---

Tags

laravelpaymentgatewaySudanSDGzakhirdigitalize

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/amolood-zakhir-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/amolood-zakhir-laravel/health.svg)](https://phpackages.com/packages/amolood-zakhir-laravel)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[api-platform/laravel

API Platform support for Laravel

59156.3k10](/packages/api-platform-laravel)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45344.0k1](/packages/pressbooks-pressbooks)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)[calebdw/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

15104.9k4](/packages/calebdw-larastan)

PHPackages © 2026

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