Looking to hire Laravel developers? Try LaraJobs

laravel-pricing maintained by itmosoft

Description
A reusable Laravel pricing package with polymorphic prices, currency support, scheduled pricing, quantity breaks, and contextual price resolution.
Last update
2026/11/04 12:51 (dev-main)
License
Links
Downloads
2

Comments
comments powered by Disqus

ITMOSOFT Laravel Pricing

A reusable Laravel pricing package for polymorphic model pricing, multiple currencies, scheduled prices, quantity breaks, and context-specific price resolution.

Requirements

  • PHP 8.3+
  • Laravel 12 or 13

Installation

composer require itmosoft/laravel-pricing
php artisan vendor:publish --tag=pricing-config
php artisan migrate

Attach the price relationship

Add a morph-many relationship to any Eloquent model that needs pricing:

use Illuminate\Database\Eloquent\Relations\MorphMany;
use ItmoSoft\Pricing\Models\Price;

public function prices(): MorphMany
{
    return $this->morphMany(Price::class, 'priceable');
}

Create prices

$product->prices()->create([
    'currency' => 'USD',
    'amount' => 99.00,
    'compare_at_amount' => 119.00,
    'min_quantity' => 1,
    'is_active' => true,
]);

// Quantity break
$product->prices()->create([
    'currency' => 'USD',
    'amount' => 85.00,
    'min_quantity' => 10,
    'is_active' => true,
]);

// Context-specific price
$product->prices()->create([
    'currency' => 'USD',
    'amount' => 75.00,
    'context' => ['customer_group' => 'wholesale'],
    'is_active' => true,
]);

Resolve a price

use ItmoSoft\Pricing\Services\PriceResolver;

$price = app(PriceResolver::class)->resolve(
    priceable: $product,
    currency: 'USD',
    quantity: 12,
    context: ['customer_group' => 'wholesale'],
);

if ($price !== null) {
    $price->amount;
    $price->compare_at_amount;
}

Resolution rules:

  1. Match the model, currency, active state, quantity threshold, and current schedule.
  2. A price's context must be a subset of the supplied context with exact key/value matches.
  3. The most context-specific matching price wins.
  4. Ties are resolved by higher priority, then newest record.
  5. Returns null when no price applies.

The caller is responsible for supplying trusted context values and handling a missing price.

Scheduled pricing

starts_at and ends_at are inclusive boundaries. Null means no boundary.

$product->prices()->create([
    'currency' => 'USD',
    'amount' => 79.00,
    'starts_at' => now()->startOfDay(),
    'ends_at' => now()->addDays(7)->endOfDay(),
    'is_active' => true,
]);

Configuration

Publish config/pricing.php:

php artisan vendor:publish --tag=pricing-config
  • table_prefix: default pricing_; configure before running migrations.
  • default_currency: default USD.
  • models.price: price model class mapping.

The default table is pricing_prices.

Design boundaries

This package provides price storage and deterministic price resolution. It does not implement tax calculation, currency conversion, coupon codes, promotion campaigns, cart totals, checkout, payment processing, or automatic scheduled activation jobs. Store monetary amounts in the chosen currency; this package does not perform FX conversion.

Testing

composer install
composer validate --no-check-publish
composer test

GitHub Actions tests Laravel 12 and 13 separately using the matching Orchestra Testbench major version.

License

MIT. See LICENSE.