PHPackages                             devmatchable/whop-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. [API Development](/categories/api)
4. /
5. devmatchable/whop-laravel

ActiveLibrary[API Development](/categories/api)

devmatchable/whop-laravel
=========================

Laravel package for the Whop PHP SDK — auto-wired client, signature-verifying middleware, webhook route + handler, artisan commands.

0.0.1(3w ago)00MITPHPPHP ^8.4CI passing

Since May 15Pushed 3w agoCompare

[ Source](https://github.com/devmatchable/whop-laravel)[ Packagist](https://packagist.org/packages/devmatchable/whop-laravel)[ Docs](https://github.com/devmatchable/whop-laravel)[ RSS](/packages/devmatchable-whop-laravel/feed)WikiDiscussions master Synced 1w ago

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

Whop Laravel
============

[](#whop-laravel)

[![CI](https://github.com/devmatchable/whop-laravel/actions/workflows/ci.yml/badge.svg)](https://github.com/devmatchable/whop-laravel/actions/workflows/ci.yml)[![PHP](https://camo.githubusercontent.com/270717987f5341772d79b57567226e54ed27b2d4199bbdc98a96e2edf24902fa/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e342532422d3737374242343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![Larastan](https://camo.githubusercontent.com/b820f56eb2d23924484069f0bebad037bd5caef855cbefe0054fd8f8084ec80b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6172617374616e2d6c6576656c2532306d61782d326136343936)](https://github.com/larastan/larastan)

Warning

**This package is in active development and is not yet ready for production use.**The public API may change at any time before version `1.0.0` is released. Please do not depend on it in production projects until a stable release is published.

Laravel package for the [Whop PHP SDK](https://github.com/devmatchable/whop-php-sdk) — auto-wires the `WhopApiClient` and `WebhookVerifier` from configuration, ships a signature-verifying middleware, an overridable webhook route + handler, and two artisan commands.

What this is
------------

[](#what-this-is)

A thin integration layer over the framework-agnostic [`devmatchable/whop-php-sdk`](https://github.com/devmatchable/whop-php-sdk). It does four things:

- Binds the SDK's `Matchable\Whop\WhopApiClient` and `Matchable\Whop\Webhook\WebhookVerifier` as container singletons, driven by the `whop` config.
- Registers a `whop.signature` middleware alias that verifies the Standard Webhooks signature on incoming requests and stashes the verified raw body on the request.
- Auto-mounts a webhook route at `whop.webhook_path` whose default handler dispatches a `WhopWebhookReceived` event — zero-config consumers only write a listener.
- Exposes `whop:check` and `whop:webhook:verify` artisan commands for setup validation and signature debugging.

The package owns *only* framework wiring. API logic, DTOs, and signature math live in the SDK and are not duplicated here.

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

[](#requirements)

- PHP 8.4+
- Laravel 11, 12, or 13
- `devmatchable/whop-php-sdk` ^0.0.1

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

[](#installation)

```
composer require devmatchable/whop-laravel
```

Laravel package auto-discovery registers `WhopServiceProvider` and the `Whop` facade alias automatically — no edits to `bootstrap/providers.php` or `config/app.php`.

Publish the config file:

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

Set the required environment variables:

```
WHOP_API_KEY=...
WHOP_WEBHOOK_SECRET=whsec_...
WHOP_BUSINESS_ID=biz_...
# Optional overrides
# WHOP_BASE_URL=https://sandbox-api.whop.com/api/v1
# WHOP_WEBHOOK_PATH=/_whop/webhook
# WHOP_HTTP_CLIENT=
# WHOP_REGISTER_ROUTES=true
```

Quick start
-----------

[](#quick-start)

Resolve the `WhopApiClient` and call any SDK resource. Constructor injection is the recommended path:

```
use Matchable\Whop\WhopApiClient;

final readonly class CompanyLookup
{
    public function __construct(
        private WhopApiClient $whop,
    ) {
    }

    public function fetch(string $companyId): void
    {
        $company = $this->whop->companies->get($companyId);
        // ...
    }
}
```

The `Whop` facade resolves the same singleton. SDK resources are exposed as public readonly properties on `WhopApiClient`, so reach them through the facade root:

```
use Matchable\Whop\Package\Facades\Whop;

$company = Whop::getFacadeRoot()->companies->get($companyId);
```

(The facade is most useful for IDE autocompletion via its `@mixin WhopApiClient`hint; in application code, prefer constructor injection.)

Configuration reference
-----------------------

[](#configuration-reference)

All keys live under `config/whop.php` and map 1:1 to environment variables:

KeyEnv varDefaultWhen to override`api_key``WHOP_API_KEY`*(required)*Always — the Bearer token the `WhopApiClient` authenticates with.`webhook_secret``WHOP_WEBHOOK_SECRET`*(required)*Always — the Standard Webhooks signing secret (`whsec_` prod / `ws_` sandbox).`base_url``WHOP_BASE_URL``https://api.whop.com/api/v1`Point at `https://sandbox-api.whop.com/api/v1` for non-production environments.`business_id``WHOP_BUSINESS_ID`*(required)*Always — every company-scoped Whop endpoint requires `company_id`; checked at boot, `app(WhopApiClient::class)` throws if unset.`http_client``WHOP_HTTP_CLIENT``null`Container id of a `Symfony\Component\HttpClient\Psr18Client` instance to inject.`webhook_path``WHOP_WEBHOOK_PATH``/_whop/webhook`Change the path the auto-route is mounted at; point Whop's webhook config at it.`register_routes``WHOP_REGISTER_ROUTES``true`Set to `false` when wiring your own route — see "custom route" section below.Receiving webhooks — the auto-route
-----------------------------------

[](#receiving-webhooks--the-auto-route)

By default the package mounts a `POST` route at `whop.webhook_path` with the `whop.signature` middleware applied and `WhopWebhookController` as the action. The controller verifies the signature, decodes the JSON body, and hands off to the bound `WhopWebhookHandlerInterface`. The default handler dispatches a `WhopWebhookReceived` event, so zero-config integration is just a listener:

```
use Matchable\Whop\Package\Events\WhopWebhookReceived;

final class HandleWhopWebhook
{
    public function handle(WhopWebhookReceived $event): void
    {
        // $event->payload    — decoded webhook JSON (array)
        // $event->rawPayload — the verified raw request body
    }
}
```

Register it in your `EventServiceProvider`:

```
protected $listen = [
    \Matchable\Whop\Package\Events\WhopWebhookReceived::class => [
        \App\Listeners\HandleWhopWebhook::class,
    ],
];
```

The controller responds with `204 No Content` on success, `401 Unauthorized` on an invalid signature, and `400 Bad Request` on a body that is not decodable JSON.

Receiving webhooks — custom route + middleware
----------------------------------------------

[](#receiving-webhooks--custom-route--middleware)

Disable the auto-route and wire your own action. The `whop.signature` middleware alias is registered unconditionally, so you keep signature verification:

```
WHOP_REGISTER_ROUTES=false
```

```
// routes/web.php (or routes/api.php)
use App\Http\Controllers\MyWhopWebhookController;
use Illuminate\Support\Facades\Route;

Route::post('/webhooks/whop', MyWhopWebhookController::class)
    ->middleware('whop.signature');
```

> **Laravel 12 CSRF note:** consumer-defined `POST` routes in the `web` group are CSRF-protected by default — Whop's request will be rejected with a 419 before `whop.signature` runs. To accept Whop webhooks at a `web`-group path, exclude it in `bootstrap/app.php`:
>
> ```
> ->withMiddleware(function (Middleware $middleware) {
>     $middleware->validateCsrfTokens(except: ['webhooks/whop']);
> })
> ```
>
>
>
> Or register the route under `routes/api.php` — the `api` group has no CSRF. The auto-mounted `/_whop/webhook` route is registered as a standalone route outside the `web` group, so this only applies to consumer-defined routes.

Read the verified body from the request attributes — the middleware stashes it under the `VerifyWhopSignature::VERIFIED_BODY_ATTRIBUTE` constant (`'whop.raw_body'`):

```
use Illuminate\Http\Request;
use Matchable\Whop\Package\Http\Middleware\VerifyWhopSignature;

final class MyWhopWebhookController
{
    public function __invoke(Request $request)
    {
        $rawBody = (string) $request->attributes->get(
            VerifyWhopSignature::VERIFIED_BODY_ATTRIBUTE,
        );
        $payload = json_decode($rawBody, associative: true);
        // ...
    }
}
```

Overriding the webhook handler
------------------------------

[](#overriding-the-webhook-handler)

The default handler is bound as `WhopWebhookHandlerInterface => EventDispatchingWebhookHandler`. Three documented override paths:

1. **Rebind the interface to your own implementation:**

    ```
    // app/Providers/AppServiceProvider.php
    use App\Whop\MyWebhookHandler;
    use Matchable\Whop\Package\Webhook\WhopWebhookHandlerInterface;

    public function register(): void
    {
        $this->app->bind(WhopWebhookHandlerInterface::class, MyWebhookHandler::class);
    }
    ```
2. **Extend `EventDispatchingWebhookHandler`** and override `handle()`:

    ```
    use Matchable\Whop\Package\Webhook\EventDispatchingWebhookHandler;

    final class MyWebhookHandler extends EventDispatchingWebhookHandler
    {
        public function handle(array $payload, string $rawPayload): void
        {
            // pre-processing
            parent::handle($payload, $rawPayload);
            // post-processing
        }
    }
    ```
3. **Decorate** the bound instance with the container's `extend`:

    ```
    use Matchable\Whop\Package\Webhook\WhopWebhookHandlerInterface;

    $this->app->extend(
        WhopWebhookHandlerInterface::class,
        fn (WhopWebhookHandlerInterface $inner) => new LoggingWebhookHandler($inner),
    );
    ```

Artisan commands
----------------

[](#artisan-commands)

```
# Verify config + perform a probe GET against the Whop API.
php artisan whop:check
```

`whop:check` exits with code 1 if any required key (`api_key`, `webhook_secret`, `business_id`, `base_url`) is missing. With config in place it issues a live probe against `/plans?company_id={WHOP_BUSINESS_ID}` to confirm the credentials reach Whop.

```
# Re-verify a captured webhook against the configured (or supplied) secret.
php artisan whop:webhook:verify \
    --payload-file=/tmp/whop-body.json \
    --id=msg_... \
    --timestamp=1714234567 \
    --signature='v1,...'
```

Quality gates
-------------

[](#quality-gates)

```
composer cs        # Pint dry-run
composer cs:fix    # Pint apply
composer stan      # Larastan at level max
composer test      # Pest (unit + integration)
composer mutate    # pest --mutate --covered-only --everything --min=97
```

CI (`.github/workflows/ci.yml`) runs the matrix `PHP {8.4, 8.5} × Laravel {11, 12} × {highest, lowest} dependencies`. Pint, Larastan, and mutation testing run on the canonical leg (PHP 8.4 / Laravel 12 / highest); Pest runs on every leg. Larastan runs at `level: max` with **no baseline** — fix the code, do not weaken the level.

Laravel 13 is supported by the package's `require` constraints, but the CI matrix does not yet include a Laravel 13 leg — adding one requires bumping the `pestphp/pest`/`pestphp/pest-plugin-laravel` dev dependencies to `^4.0` (Pest v3 caps at Laravel 12). Track that as its own change.

Versioning
----------

[](#versioning)

Pre-1.0. The public API may change between minor releases until `1.0.0`. Pin a specific version in production rather than a range until then.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance95

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity41

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

25d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/99ca2da4eb72149536aad21d87b92c24240bfffea7d702c01bd705b138fa47ad?d=identicon)[ToluTourialai](/maintainers/ToluTourialai)

![](https://www.gravatar.com/avatar/046ec11941ea4d874be32d3e03e97c5c13a9ce951594fd02dfc67792d0742a84?d=identicon)[JeroenMoonen](/maintainers/JeroenMoonen)

---

Top Contributors

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

---

Tags

api-clientlaravellaravel-packagepaymentsphppsr-18webhookswhopwhop-apilaravellaravel-packagewebhookspaymentswhopwhop-api

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/devmatchable-whop-laravel/health.svg)

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

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

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

Rapidly build MCP servers for your Laravel applications.

76318.2M110](/packages/laravel-mcp)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9732.3M121](/packages/roots-acorn)[larastan/larastan

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

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

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k14.1M120](/packages/laravel-pulse)[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)

PHPackages © 2026

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