Download the PHP package develupers/laravel-plan-usage without Composer

On this page you can find all versions of the php package develupers/laravel-plan-usage. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-plan-usage

Laravel Plan Usage

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads License: MIT

A powerful Laravel package for managing subscription plans, features, quotas, and usage tracking. Perfect for SaaS applications that need flexible plan management with feature access control and usage monitoring. Supports Stripe, Paddle, and Polar billing integrations.

✨ Features

πŸ“‹ Requirements

πŸš€ Installation

Install the package from Packagist by running this command in your Laravel application's root directory:

Laravel automatically discovers the service provider and facade aliases. No custom Composer repository or manual provider registration is required.

Select Your Billing Provider

Stripe's laravel/cashier package is installed as a dependency. For Paddle or Polar, also install the matching integration:

Set your chosen provider in .env before publishing migrations, because the package publishes different migrations for each provider:

Use paddle or polar instead when appropriate. Clear any cached configuration:

Publish and Configure

For a fresh installation, publish the configuration and the selected provider's migrations:

This writes config/plan-usage.php and migration files in database/migrations; it does not run the migrations. The installer overwrites the published configuration, so use the individual vendor:publish commands when updating an existing installation:

Before migrating, configure models.billable and tables.billable in config/plan-usage.php for your billable model and table. Add the package's HasPlanFeatures trait and your provider's Billable trait to that model, and complete the provider's database and webhook setup. See the Installation Guide for model examples and provider configuration.

Then create the tables:

βš™οΈ Configuration

After installation, configure the package in config/plan-usage.php:

Default Plan for New Billables

When default_plan_id is configured, the package automatically assigns this plan to new billable models when they're created. This is useful for:

With this configured, you don't need to manually assign plans when creating billables:

πŸ’³ Billing Provider Setup

This package supports Stripe, Paddle, and Polar as billing providers. You can only use one provider at a time.

Option 1: Stripe (Default)

Install Laravel Cashier for Stripe:

Add to your .env:

Option 2: Paddle (Merchant of Record)

Paddle acts as Merchant of Record, handling all tax/VAT compliance for you. This is ideal if you want to avoid dealing with tax regulations across different countries.

Install Laravel Cashier for Paddle:

Add to your .env:

Option 3: Polar (Merchant of Record)

Install Laravel Polar and run its installer:

Add to your .env:

Each PlanPrice maps to a distinct Polar product through polar_product_id. Monthly and yearly subscriptions are separate Polar products even when they grant the same application plan.

Lifetime plans are fully supported on Polar: a PlanPrice with the lifetime interval is pushed as a one-time Polar product, the plan and quotas are assigned when the order.paid webhook arrives (no subscription is created), and a fully refunded order revokes them again. Mark the plan is_lifetime = true so subscription enforcement never touches its holders.

Auto-Detection

If BILLING_PROVIDER is set to auto (or not set), the package will automatically detect which billing package is installed and use the appropriate provider. Detection priority: Paddle > Polar > Stripe.

Automatic Migration Selection

The package automatically publishes the correct migration for your billable table based on the detected billing provider:

All migrations also add plan tracking columns: plan_id, plan_price_id, plan_changed_at.

The plan_prices table likewise only receives the selected provider's price/product identifier column (stripe_price_id, paddle_price_id, or polar_product_id). The managed subscription-change table (subscription_plan_changes) is published for providers that support managed plan changes (Stripe, Paddle, and Polar); the durable webhook-event table (billing_webhook_events) is Polar-only.

Switching billing providers? Migrations are selected from the provider detected at publish time, so after installing the new provider package and updating BILLING_PROVIDER, re-run php artisan vendor:publish --tag="plan-usage-migrations" and php artisan migrate. Only the new provider's migrations are added (already-published ones are skipped), and every provider-specific migration is guarded with hasColumn/hasTable checks so re-running is safe. Skipping this step leaves the new provider's column/tables missing β€” e.g. Polar webhooks will fail without the billing_webhook_events table.

The Paddle stub also adds a billing_email override (falls back to the owner/user email). Paddle allows only one customer per email, so each billable needs its own billing identity to subscribe independently; PaddleProvider::updateCustomerEmail() pushes changes to Paddle.

Important: You still need to publish and run the billing provider's own migrations separately. The package only adds the billable columns to your model's table.

🏁 Quick Start

1. Add Traits to Your Billable Model

For Stripe:

For Paddle:

For Polar:

2. Create Plans and Features

3. Assign Plan to Billable

4. Use Features in Your Application

There are two approaches -- pick whichever fits your use case:

Middleware approach (automatic, on routes):

Manual approach (in your code):

Other useful methods:

πŸ” Understanding Feature Checks

The package provides two main methods for checking features:

Method Purpose Mutates quota?
hasFeature('api-calls') Check if feature is included in the plan No
checkQuota('api-calls', 10) Check if quota is available for amount No
consume('api-calls', 10) Enforce + increment + log (the full operation) Yes
logUsage('api-calls', 10) Log usage only (no enforcement) No

Key difference: hasFeature() checks plan inclusion, checkQuota() checks current quota availability, and consume() actually uses the quota.

🏷️ Plan Types

Plans can be categorized by visibility and distribution type:

Type Description Purchasable Visible to
public Current plans on pricing page Yes, self-service checkout Everyone
private Gated plans requiring access code, invite, or membership Yes, with access Invited/authorized users
legacy Discontinued plans, grandfathered for existing subscribers No, existing subscribers only Current holders only
hidden Internal/admin-only plans (lifetime deals, staff plans) No, manually assigned Admin only

♾️ Lifetime Plans

Lifetime plans are plans that don't require an active billing subscription. They're ideal for one-time purchase deals, promotional offers, or manually assigned plans that should never be revoked by subscription enforcement.

Creating a Lifetime Plan

Querying Lifetime Plans

How Lifetime Plans Work

Aspect Regular Plan Lifetime Plan
Requires subscription Yes No
Quotas reset periodically Yes Yes
Subject to plan enforcement Yes No
Shown on pricing page If type = public Typically type = hidden
Assigned via Stripe/Paddle/Polar checkout Admin panel, seeder, or manual assignment

Lifetime plans still benefit from quota resets β€” a lifetime plan with 8,000 monthly credits will reset to 0 used credits each month. The only difference is that the plan is never removed due to a missing subscription.

Lifetime Purchases on Polar

Polar is the only provider with true one-time purchases: a LIFETIME-interval PlanPrice maps to a one-time Polar product. The order.paid webhook assigns the plan and quotas (orders belonging to a subscription are ignored), and a fully refunded order is the only thing that revokes a lifetime plan β€” subscription lifecycle events, reconciliation, and enforcement all skip lifetime holders, so a historical (cancelled) subscription can never wipe a lifetime plan bought afterwards.

Checkout blocks buying the same lifetime plan price twice (one-time purchases create no subscription row, so the usual subscribed() guard cannot catch a repeat purchase). Known limitation: purchases made outside the package's checkout (e.g. Polar dashboard payment links) are not deduplicated β€” sell lifetime products through CreateCheckoutSessionAction to keep refund semantics safe.

Migration

If upgrading from a previous version, publish and run the migration:

This adds the is_lifetime boolean column (default: false) to the plans table.

πŸ’° Pricing Structure

Plans support multiple pricing options with different intervals and currencies:

Creating Multiple Prices

Working with Prices

Supported Intervals

Interval Description
day Daily billing
week Weekly billing
month Monthly billing
year Annual billing
lifetime One-time payment

🎯 Feature Types

The package supports three types of features:

Type Description Example
Boolean On/off features Advanced Analytics, Priority Support
Limit Maximum allowed quantity Max Projects, Team Members
Quota Usage-based with periodic reset API Calls, Storage, Bandwidth

πŸ›‘οΈ Middleware

Protect your routes with built-in middleware:

πŸ“Š Usage Tracking & Analytics

Feature Usage Details

⚠️ getFeatureUsage() can return null β€” always guard for it

getFeatureUsage() returns ?array. It gives back null whenever there is nothing to meter, which is deliberately different from a feature that is metered but exhausted (['limit' => 0, ...]). Treat null as "not applicable" rather than rendering it as a maxed-out progress bar.

Scenario Return value
Feature not found, or boolean type null
Billable has no plan null
Plan doesn't include the feature null
Granted, unlimited ['limit' => null, 'used' => N, 'remaining' => null]
Granted with a limit ['limit' => 5000, 'used' => 1250, 'remaining' => 3750]

Track Usage

Quota Management

πŸŽͺ Events

The package dispatches events you can listen to:

Event Deduplication (trigger_once)

By default, QuotaWarning and QuotaExceeded events fire on every consume() call where usage exceeds a threshold. This can result in duplicate notifications (e.g., an email on every API request after 80% usage).

Set trigger_once to true to fire each event only once per billing period:

When enabled, the package uses a cache key scoped to the billable, feature, and threshold. The key expires at the quota's reset_at timestamp (or 24 hours if no reset period is configured). Each threshold fires independently β€” crossing 80% sends one event, and later crossing 100% sends another.

This is handled at the package level, so your listeners don't need any deduplication logic.

πŸ’³ Billing Provider Integration

The package integrates with Cashier Stripe, Cashier Paddle, and Laravel Polar through one billing provider contract.

Design boundary: the provider libraries own the money β€” payments, invoices, taxes, payment methods, and the billing clock. This package owns the entitlements β€” what a customer may use inside your application, and how much of it is left. It only reaches into a provider where entitlement correctness requires owning the call path (plan changes, cancellation), and it never replicates provider machinery it can consume instead.

Webhook Behavior & Entitlement Policy

Providers do not guarantee webhook delivery order, so payloads are treated only as triggers: listeners re-fetch the subscription's current state from the provider API inside a per-billable lock and converge the local plan to that authoritative response β€” out-of-order deliveries are harmless. Failed deliveries return a non-2xx response (and release their dedupe key) so the provider redelivers.

One shared status policy governs listeners, subscriptions:reconcile, and subscription enforcement:

Remote status Outcome
active, trialing Plan granted / synced
past_due Kept by default; set plan-usage.{stripe,paddle,polar}.past_due_keeps_entitlements to false to revoke
canceled (Polar) Grace period β€” kept until the effective end passes
Everything else (incomplete, unpaid, paused, canceled, unknown) Plan revoked

Revocation is fail-closed (the plan-clear commits before quota cleanup, so a cleanup failure still denies access) and idempotent (a billable already on the configured subscription.default_plan_id is never re-processed, preserving free-tier usage).

Provider-Agnostic Methods

Managed Plan Changes

Use the managed plan-change API instead of calling the provider's subscription model directly. This keeps the remote subscription, local plan, quotas, and pending-change audit record consistent.

Provider support:

Timing Stripe Paddle Polar
Immediate (swap + prorate now) βœ… βœ… βœ…
NextPeriod (scheduled change at renewal) ❌ ❌ βœ…

Only the configured default-type subscription (subscription.default_name, 'default' by default) controls the billable's plan: changePlan() rejects other subscription names, webhook events for add-on subscriptions are ignored, and immediate cancellation only revokes the plan when the default subscription itself was cancelled.

A timing is exposed only where the provider supports it natively β€” the package never emulates provider-side scheduling with local timers, because the provider owns the billing clock and only it can bill the new price correctly at renewal. (Stripe could gain NextPeriod later via its native Subscription Schedules; Paddle has no native equivalent.) Feature-detect instead of catching exceptions:

The package applies these entitlement rules:

For asset limits such as active projects, this package updates the numeric plan limit but does not delete or lock application records. Your application should enforce the new limit after the scheduled change is appliedβ€”for example, by asking the user which projects remain active and making excess projects read-only.

Syncing Plans to Billing Provider

Use the unified plans:push command to sync your local plans to the billing provider:

Resetting Expired Quotas

Quotas with a reset period (daily, weekly, monthly, yearly) need to be periodically reset. The package provides a command and a queued job for this.

Command:

Schedule it in your routes/console.php:

Dispatch the job directly from your code:

The job finds all quotas where reset_at has passed and used > 0, resets the usage to 0, and sets the next reset_at date based on the feature's reset period.

Enforcing Plan Subscriptions

Plan enforcement ensures that billables with a paid plan but no active subscription get their plan revoked. Lifetime plans are automatically exempt.

Command:

Schedule it in your routes/console.php:

What it does:

  1. Finds all billables with a plan_id where the plan is NOT lifetime (is_lifetime = false)
  2. Checks if they have an active subscription or are on a grace period
  3. If not, clears plan_id (or sets to default_plan_id from config)
  4. Dispatches a PlanRevoked event for each revoked plan

Listen for revocations:

The PlanRevoked event contains:

Reconciling Subscriptions

If webhooks are missed, you can reconcile local subscription status with the billing provider. Every billable with subscription rows is checked against the provider's current state: expired subscriptions are revoked, active subscriptions have plan/price drift corrected, and a billable whose initial checkout webhook was lost gets its plan recovered. Billables with a plan but no subscription rows (lifetime purchases, manually granted plans) are never touched. Only the configured default-type subscription controls the plan β€” custom-typed subscriptions are deliberately ignored.

Stripe-Specific

Paddle-Specific

Polar-Specific

Polar sells products rather than a separate product/price pair. Each local PlanPrice therefore maps to one Polar product:

Use plans:push --provider=polar to create those products automatically. Laravel Polar owns customer, checkout, portal, order, subscription, and webhook transport; this package owns the mapping from the confirmed Polar subscription to plans, features, quotas, and usage.

⏰ Recommended Schedule

Add both commands to your routes/console.php to keep quotas and plans in sync:

πŸ”„ Plan Comparison

Compare plans to show upgrade benefits:

πŸ§ͺ Testing

Run the test suite:

Run tests with coverage:

Format code:

Static analysis:

πŸ“š Documentation

For detailed documentation, see:

🀝 Contributing

Please see CONTRIBUTING for details on how to contribute to this project.

Development Setup

  1. Clone the repository
  2. Install dependencies: composer install
  3. Run tests: composer test
  4. Check code style: composer format

πŸ”’ Security

If you discover any security-related issues, please email [email protected] instead of using the issue tracker.

πŸ“ Changelog

Please see CHANGELOG for more information on what has changed recently.

πŸ‘₯ Credits

πŸ“„ License

The MIT License (MIT). Please see License File for more information.

πŸ’ͺ Support

Need help or have questions? Feel free to:


Built with ❀️ by Develupers


All versions of laravel-plan-usage with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
spatie/laravel-package-tools Version ^1.18
illuminate/contracts Version ^11.0||^12.0||^13.0
illuminate/support Version ^11.0||^12.0||^13.0
illuminate/database Version ^11.0||^12.0||^13.0
laravel/cashier Version ^15.0||^16.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package develupers/laravel-plan-usage contains the following files

Loading the files please wait ...