Looking to hire Laravel developers? Try LaraJobs

laravel-team-paddle maintained by labrodev

Description
Paddle billing adapter for labrodev/laravel-team — Cashier-Paddle gateway, Billable team model, and webhook plan sync.
Author
Last update
2026/07/25 18:38 (dev-main)
License
Downloads
4

Comments
comments powered by Disqus

laravel-team-paddle

Paddle billing adapter for labrodev/laravel-team — a Cashier-Paddle implementation of the BillingClient seam, a Billable team model, and webhook-driven plan synchronization.

Requirements

Installation

composer require labrodev/laravel-team-paddle

Publish and run the Cashier Paddle migrations (customers, subscriptions, subscription items, transactions):

php artisan vendor:publish --tag="cashier-migrations"
php artisan migrate

Publish the package config:

php artisan vendor:publish --tag="team-paddle-config"

Configuration

1. Use the billable team model

Point laravel-team at the shipped Billable team model in config/team.php:

'team_model' => Labrodev\TeamPaddle\Models\BillableTeam::class,

Every team the package resolves then carries Cashier's Billable surface, and the team morph alias (used by Cashier's billable_type columns) maps to it. You may also subclass BillableTeam and configure your own class instead.

2. Map plans to Paddle prices

config/team-paddle.php maps your plan enum backing values to Paddle price ids:

'prices' => [
    'pro' => env('PADDLE_PRICE_PRO'),
],

Plans without an entry (e.g. the free plan) cannot be checked out — StartPlanCheckout downgrades to free by cancelling the subscription instead. Attempting to check out a plan missing from the map throws PaddlePriceNotConfiguredException (an internal misconfiguration error, not a validation concern).

'subscription_type' => 'default',

is the Cashier subscription type used for the single plan subscription.

3. Configure Cashier and the webhook

Follow the Cashier Paddle documentation for the Paddle API key, client-side token, sandbox mode, and — critically — the webhook: Cashier registers a /paddle/webhook route that persists subscription state and fires the Laravel\Paddle\Events\Subscription* events this package listens to. Point your Paddle notification destination at that route and set PADDLE_WEBHOOK_SECRET.

What the package wires up

  • Labrodev\Team\Contracts\BillingClient is bound to Labrodev\TeamPaddle\PaddleBillingGateway, so laravel-team's StartPlanCheckout (checkout creation, plan swaps, cancellation, subscription checks) runs against Paddle with no app code.
  • SubscriptionCreated, SubscriptionUpdated, SubscriptionCanceled, and SubscriptionPaused are handled by Labrodev\TeamPaddle\Listeners\SyncTeamPlanFromPaddle, which resolves the effective plan from the subscription's price and applies it via TeamPlanUpdate (plan + caps snapshot). Canceled, paused, or otherwise invalid subscriptions fall back to the configured free plan.

Reacting to plan changes

When a webhook actually changes a team's effective plan, the package dispatches Labrodev\TeamPaddle\Events\TeamPlanSynced:

final readonly class TeamPlanSynced
{
    public function __construct(
        public Team $team,
        public PlanContract $previousPlan,
        public PlanContract $newPlan,
    ) {}
}

Owner notifications and similar side effects are application infrastructure — listen to this event in your app:

Event::listen(TeamPlanSynced::class, NotifyOwnerOfPlanChange::class);

Testing

composer check   # pint --test, phpstan (level 7), rector --dry-run, pest

License

MIT — see LICENSE.md.