Skip to main content

Documentation Index

Fetch the complete documentation index at: https://danestvesllc-2b77d201.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks are how Polar.sh tells your application what has happened — a subscription was created, an order was placed, a checkout expired. Laravel Polar handles signature verification and payload parsing automatically, then dispatches typed Laravel events your listeners can react to. You don’t need to write any webhook verification code. This page explains the endpoint, the 21 events that are dispatched, and the two ways to register listeners.

How it works

When you install the package it registers a POST /polar/webhook route. Every incoming request is processed in this order:
  1. Verified against your POLAR_WEBHOOK_SECRET — invalid signatures are rejected with a 400 response before any event is dispatched.
  2. Parsed into a typed payload object using the Polar PHP SDK.
  3. Synced to your database — for example, creating or updating a Subscription record when a subscription.created event arrives.
  4. Dispatched as a typed Laravel event so your own listeners can run custom logic (send emails, provision access, update feature flags, etc.).
You must exempt polar/* from CSRF verification in bootstrap/app.php, otherwise Polar’s requests will be rejected with a 419 response before verification can run. See the installation guide for the exact snippet.

Webhook endpoint URL

With the default POLAR_PATH=polar config, your webhook URL is:
POST https://yourdomain.com/polar/webhook
Enter this URL when you create the webhook in the Polar dashboard under Settings → Webhooks.

Dispatched events

The package dispatches a unique Laravel event class for each Polar event type. All events carry a $payload property with the full typed Polar SDK payload. Some events also expose convenience properties ($billable, $order, $subscription) for direct access to your local Eloquent models — no manual lookups required.

Order events

Events with $billable and $order convenience properties.
Event classPolar eventConvenience properties
Danestves\LaravelPolar\Events\OrderCreatedorder.created$billable, $order, $payload
Danestves\LaravelPolar\Events\OrderUpdatedorder.updated$billable, $order, $payload
use Danestves\LaravelPolar\Events\OrderCreated;

public function handle(OrderCreated $event): void
{
    $order = $event->order;         // local Order model
    $user  = $event->billable;      // local billable (e.g. User)
    $raw   = $event->payload;       // WebhookOrderCreatedPayload
}

Subscription events

Events with $billable and $subscription convenience properties.
Event classPolar eventConvenience properties
Danestves\LaravelPolar\Events\SubscriptionCreatedsubscription.created$billable, $subscription, $payload
Danestves\LaravelPolar\Events\SubscriptionUpdatedsubscription.updated$billable, $subscription, $payload
Danestves\LaravelPolar\Events\SubscriptionActivesubscription.active$billable, $subscription, $payload
Danestves\LaravelPolar\Events\SubscriptionCanceledsubscription.canceled$billable, $subscription, $payload
Danestves\LaravelPolar\Events\SubscriptionRevokedsubscription.revoked$billable, $subscription, $payload
use Danestves\LaravelPolar\Events\SubscriptionCanceled;

public function handle(SubscriptionCanceled $event): void
{
    $subscription = $event->subscription; // local Subscription model
    $user         = $event->billable;
}

Benefit grant events

Events with a $billable convenience property.
Event classPolar eventConvenience properties
Danestves\LaravelPolar\Events\BenefitGrantCreatedbenefit_grant.created$billable, $payload
Danestves\LaravelPolar\Events\BenefitGrantUpdatedbenefit_grant.updated$billable, $payload
Danestves\LaravelPolar\Events\BenefitGrantRevokedbenefit_grant.revoked$billable, $payload
use Danestves\LaravelPolar\Events\BenefitGrantCreated;

public function handle(BenefitGrantCreated $event): void
{
    $user  = $event->billable;
    $grant = $event->payload->benefitGrant; // access via payload
}

Checkout events

These events carry only $payload — there is no local model to resolve.
Event classPolar eventPayload access
Danestves\LaravelPolar\Events\CheckoutCreatedcheckout.created$event->payload->checkout
Danestves\LaravelPolar\Events\CheckoutUpdatedcheckout.updated$event->payload->checkout
Danestves\LaravelPolar\Events\CheckoutExpiredcheckout.expired$event->payload->checkout
use Danestves\LaravelPolar\Events\CheckoutCreated;

public function handle(CheckoutCreated $event): void
{
    $checkout = $event->payload->checkout;
}

Customer events

Event classPolar eventPayload access
Danestves\LaravelPolar\Events\CustomerCreatedcustomer.created$event->payload->customer
Danestves\LaravelPolar\Events\CustomerUpdatedcustomer.updated$event->payload->customer
Danestves\LaravelPolar\Events\CustomerDeletedcustomer.deleted$event->payload->customer
Danestves\LaravelPolar\Events\CustomerStateChangedcustomer.state_changed$event->payload->customer

Product events

Event classPolar eventPayload access
Danestves\LaravelPolar\Events\ProductCreatedproduct.created$event->payload->product
Danestves\LaravelPolar\Events\ProductUpdatedproduct.updated$event->payload->product

Benefit events

Event classPolar eventPayload access
Danestves\LaravelPolar\Events\BenefitCreatedbenefit.created$event->payload->benefit
Danestves\LaravelPolar\Events\BenefitUpdatedbenefit.updated$event->payload->benefit

The WebhookHandled catch-all event

After the package finishes processing any webhook — regardless of type — it dispatches Danestves\LaravelPolar\Events\WebhookHandled with the raw payload array. Use this as a catch-all for logging, auditing, or handling event types you have not registered individual listeners for:
use Danestves\LaravelPolar\Events\WebhookHandled;

public function handle(WebhookHandled $event): void
{
    logger('Polar webhook received', ['type' => $event->payload['type']]);
}

Registering listeners

Option 1: individual listener classes

Create a listener class for each event you want to handle, then register them in your EventServiceProvider:
// app/Listeners/HandleOrderCreated.php

namespace App\Listeners;

use Danestves\LaravelPolar\Events\OrderCreated;

class HandleOrderCreated
{
    public function handle(OrderCreated $event): void
    {
        // provision access, send a receipt email, etc.
        $order = $event->order;
        $user  = $event->billable;
    }
}
// app/Providers/EventServiceProvider.php

namespace App\Providers;

use App\Listeners\HandleOrderCreated;
use Danestves\LaravelPolar\Events\OrderCreated;
use Danestves\LaravelPolar\Events\WebhookHandled;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        OrderCreated::class => [
            HandleOrderCreated::class,
        ],
        WebhookHandled::class => [
            // add any catch-all listeners here
        ],
    ];
}
Laravel 11 and 12 will auto-discover listeners that follow the standard naming convention, so the $listen array registration may be optional in those versions.

Option 2: a single subscriber class

If you want to handle multiple event types in one place, create an event subscriber. Register all your listeners inside the subscribe method and then register the subscriber class in your EventServiceProvider:
// app/Listeners/PolarEventSubscriber.php

namespace App\Listeners;

use Danestves\LaravelPolar\Events\CheckoutCreated;
use Danestves\LaravelPolar\Events\OrderCreated;
use Danestves\LaravelPolar\Events\SubscriptionCanceled;
use Danestves\LaravelPolar\Events\WebhookHandled;
use Illuminate\Events\Dispatcher;

class PolarEventSubscriber
{
    public function handleOrderCreated(OrderCreated $event): void
    {
        // provision access, send a receipt email, etc.
        $order = $event->order;
        $user  = $event->billable;
    }

    public function handleCheckoutCreated(CheckoutCreated $event): void
    {
        $checkout = $event->payload->checkout;
        // track analytics, send a "you started checkout" email, etc.
    }

    public function handleSubscriptionCanceled(SubscriptionCanceled $event): void
    {
        $subscription = $event->subscription;
        // send a win-back email, revoke feature flags, etc.
    }

    public function handleWebhookHandled(WebhookHandled $event): void
    {
        // catch-all: log every webhook for debugging
        logger('Polar webhook', ['type' => $event->payload['type']]);
    }

    public function subscribe(Dispatcher $events): void
    {
        $events->listen(OrderCreated::class,        [self::class, 'handleOrderCreated']);
        $events->listen(CheckoutCreated::class,     [self::class, 'handleCheckoutCreated']);
        $events->listen(SubscriptionCanceled::class,[self::class, 'handleSubscriptionCanceled']);
        $events->listen(WebhookHandled::class,      [self::class, 'handleWebhookHandled']);
    }
}
Register the subscriber in your EventServiceProvider:
// app/Providers/EventServiceProvider.php

namespace App\Providers;

use App\Listeners\PolarEventSubscriber;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $subscribe = [
        PolarEventSubscriber::class,
    ];
}
The subscriber approach works well when your billing logic is concentrated in a single class. The individual listener approach scales better when different parts of your application (notifications, feature flags, analytics) each care about different events.

Next steps

Subscriptions

Learn how to create and manage subscriptions in your application.

Orders

Retrieve order history and generate downloadable invoices.