Blax\Shop\Services\TaxService::rates($exempt, $rates=null) is one source of
truth that turns "which rate(s)" (config shop.tax.rates or an explicit list)
and "is this customer exempt" into the array Stripe expects. It fails loud
(TaxRateNotConfiguredException) when shop.tax.require is set and no rate is
configured for a non-exempt charge, instead of silently billing 0% VAT.
New config('shop.tax') block (SHOP_TAX_RATES / SHOP_TAX_REQUIRE). Unit tested.
Consumers (learn-atc #1509) delegate getApplicableTaxRates() here so no path
can apply the rate twice (Stripe rejects duplicate tax rates) or drop VAT to 0%.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .github | ||
| config | ||
| database | ||
| docs | ||
| routes | ||
| src | ||
| tests | ||
| workbench | ||
| .dockerignore | ||
| .env.example | ||
| .envrc | ||
| .gitattributes | ||
| .gitignore | ||
| README.md | ||
| composer.json | ||
| phpunit.xml | ||
| pint.json | ||
| shell.nix | ||
| test.sh | ||
| testbench.yaml | ||
README.md
Laravel Shop
A comprehensive headless e-commerce package for Laravel with stock management, Stripe integration, and product actions.
Features
- 🛍️ Product Management - Simple, variable, grouped, external, booking, and pool products
- 💰 Multi-Currency Support - Handle multiple currencies with ease
- 📦 Advanced Stock Management - Stock reservations, low stock alerts, and backorders
- 💳 Stripe Integration - Built-in Stripe product and price synchronization
- 🎯 Product Actions - Execute custom actions on product events (purchases, refunds)
- 🔗 Product Relations - Related products, upsells, and cross-sells
- 🌍 Translation Ready - Built-in meta translation support
- 📊 Stock Logging - Complete audit trail of stock changes
- 🎨 Headless Architecture - Perfect for API-first applications
- ⚡ Caching Support - Built-in cache management for better performance
- 🛒 Shopping Capabilities - Built-in trait for any purchaser model
- 🎭 Facade Support - Clean, expressive API through Shop and Cart facades
- 👤 Guest Cart Support - Session-based carts for unauthenticated users
Installation
composer require blax-software/laravel-shop
php artisan migrate
That's it — the package's migrations are auto-loaded from vendor/ so a fresh migrate is all you need.
Optionally publish the config:
php artisan vendor:publish --tag="shop-config"
If you'd rather own the migrations in your own database/migrations/ directory (e.g. to customise schemas, switch ID types, etc.):
php artisan vendor:publish --tag="shop-migrations"
To stop the package from also auto-loading them, set 'run_migrations' => false in config/shop.php.
Configuration
The main configuration file is located at config/shop.php. Here you can configure:
- Database table names
- Caching settings
- Stripe integration keys and settings
- Currency settings
Quick Start
Setup Your User Model
Add the HasShoppingCapabilities trait to any model that should be able to purchase products (typically your User model):
use Blax\Shop\Traits\HasShoppingCapabilities;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasShoppingCapabilities;
// ...existing code...
}
Creating Your First Product
Use the provided Enums to ensure type safety and consistency.
use Blax\Shop\Models\Product;
use Blax\Shop\Enums\ProductType;
use Blax\Shop\Enums\ProductStatus;
use Blax\Shop\Enums\StockType;
$product = Product::create([
'slug' => 'amazing-t-shirt',
'sku' => 'TSH-001',
'type' => ProductType::SIMPLE,
'manage_stock' => true,
'status' => ProductStatus::PUBLISHED,
'name' => 'Amazing T-Shirt', // Uses meta translation
'description' => 'A comfortable cotton t-shirt',
]);
// Add Price
$product->prices()->create([
'currency' => 'USD',
'unit_amount' => 1999, // $19.99
'sale_unit_amount' => 1499, // $14.99
'is_default' => true,
]);
// Manage Stock
$product->adjustStock(StockType::INCREASE, 100); // Add 100 items to stock
$product->adjustStock(StockType::DECREASE, 10); // Remove 10 items from stock
// Reserve Stock (e.g., for a booking)
$product->adjustStock(
StockType::CLAIMED,
1,
from: now(),
until: now()->addDay(),
note: 'Reserved for Order #123'
);
Working with Cart
use Blax\Shop\Facades\Cart;
// Add item to cart
Cart::addToCart($product, 1);
// Add item with date range (for bookings)
Cart::addToCart($product, 1, [], now(), now()->addDay());
// Checkout
$cart = Cart::getCart();
$cart->checkout(); // Creates purchases, claims stock, etc.
Advanced Usage
Pool Products
Pool products are collections of single items (e.g., "Parking Spaces" containing "Spot A1", "Spot A2").
use Blax\Shop\Models\Product;
use Blax\Shop\Enums\ProductType;
// Create the Pool Parent
$pool = Product::create([
'type' => ProductType::POOL,
'name' => 'Parking Spaces',
'manage_stock' => true, // Pool manages availability
]);
// Create Single Items
$spot1 = Product::create([
'type' => ProductType::BOOKING,
'name' => 'Spot A1',
]);
$spot2 = Product::create([
'type' => ProductType::BOOKING,
'name' => 'Spot A2',
]);
// Attach Singles to Pool
$pool->attachSingleItems([$spot1->id, $spot2->id]);
Booking Products
Booking products are time-based and require from and until dates when adding to cart.
use Blax\Shop\Models\Product;
use Blax\Shop\Enums\ProductType;
$room = Product::create([
'type' => ProductType::BOOKING,
'name' => 'Conference Room',
'manage_stock' => true,
]);
// Check availability
$isAvailable = $room->availableOnDate(now(), now()->addHour());
Testing
We test this package for many edge cases across every surface — products, stock, pricing strategies, cart/checkout, loan lifecycle, pool aggregation, booking, Stripe sync and the event surface — so host applications can lean on the behaviour with confidence.
Tests: 1409, Assertions: 3774
CI runs the full suite on every push (see the badge above). To run it locally:
./vendor/bin/phpunit
The tests use an in-memory SQLite database and Orchestra Testbench, so they run in roughly a minute with no external services required.
Documentation
For more detailed documentation, please refer to the docs/ directory in the repository.
License
MIT. See LICENSE.