Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ecommerce Laravel Laravel Package

liberu-eccommerce/ecommerce-laravel

A modular Laravel ecommerce package with products, carts, checkout, orders, payments, taxes, shipping, and admin-ready essentials out of the box. Built for modern PHP apps, its clean, extensible architecture helps you launch, customize, and scale online stores quickly, from catalog to fulfillment.

View on GitHub
Deep Wiki
Context7

Getting Started

This is an application skeleton, not a library you composer require into an existing project. Day one is clone-and-own: you fork it, rename the app, and treat the upstream as a reference implementation.

Prerequisites: PHP 8.2+, Composer, Node 20+, and a database (SQLite is fine to start).

Pin the version before you clone. As of v1.0.3 this is not optional. The pre-1.0 history was tagged to track the Laravel major (the release before v1.0.0 was v13.0.0), and Packagist still resolves v13.0.0 as the highest version. composer create-project gets you that old tag unless you ask for 1.0.3 explicitly. Resolve to a commit SHA in your README and move on.

Boot it in five commands:

git clone --branch 1.0.3 https://github.com/liberu-ecommerce/ecommerce-laravel.git my-shop
cd my-shop
composer install && npm install
cp .env.example .env && php artisan key:generate
php artisan migrate --seed && npm run dev & php artisan serve

Add php artisan storage:link if product/media uploads are enabled — but read the SVG note in Gotchas first, because that symlink turns a latent stored-XSS into a live one. Check .env.example for anything the seeder needs (mail driver, queue connection, payment sandbox keys).

Where to look first, in this order:

  1. .env.example — what services the app assumes exist.
  2. database/seeders/ — the fastest way to learn the domain model; seeded data reveals how products, variants, carts, and orders relate.
  3. routes/web.php and routes/ generally — the storefront/admin surface area in one screen.
  4. app/Models/ — Eloquent relationships are the real schema documentation. Pay particular attention to User, Team, and the panel-access contract (canAccessPanel).
  5. CONTEXT.md at the repo root and docs/adr/ — the project keeps a single-context domain doc and an ADR log; read these before you form opinions about the architecture, and read AGENTS.md if you use coding agents (it defines the issue tracker and triage label conventions: needs-triage, needs-info, ready-for-agent, ready-for-human, wontfix).

First use case — trace one order end to end. Boot the app, log in as the seeded admin, browse the storefront, add a product to the cart, and check out with a test payment method. Then open the /app panel and find that same order.

Two things will trip you up here, both documented in the v1.0.3 known issues:

  • The demo storefront ships sold out. The seeder sets inventory_count = 0 on every product, so "add to cart" fails before you reach checkout. Bump a product's inventory in tinker or the admin panel first.
  • A freshly registered account cannot reach /app, by design. Registration produces a plain shopper with no team and no role; team membership is what gates the app panel. Use the seeded admin for the admin-side half of the trace.

Anything that breaks on this path — mail, queue, storage, payment callback — is your actual integration checklist, and it tells you far more than the README.

Implementation Patterns

Fork-and-track workflow. Keep upstream pointing at the original repo:

git remote add upstream https://github.com/liberu-ecommerce/ecommerce-laravel.git
git fetch upstream && git log --oneline main..upstream/main

Cherry-pick or manually port upstream fixes; never merge wholesale once you've changed branding, schema, or checkout logic. Pin the base commit (currently 1.0.3) in your README — the version string alone is untrustworthy given the v13/v1 tag collision.

Vertical slice per feature. The skeleton rewards Laravel orthodoxy — one slice at a time, in this order:

  1. Migration (+ factory, always, so tests stay cheap).
  2. Model with casts, relationships, and a scope or two.
  3. Form Request for validation, Policy for authorization, and a permission seeder entry — see the policy/permission pairing note below.
  4. A single-purpose Action or Service class for the business rule (pricing, stock decrement, order transition).
  5. Controller, Livewire component, or Filament resource as the delivery layer.
  6. A Pest/PHPUnit feature test hitting the HTTP route.

Keep business logic out of controllers and Filament resources — that's what makes the skeleton maintainable when upstream diverges.

Registration creates a shopper, and that's the contract now. v1.0.3 removed the old assignOrCreateTeam behaviour that attached every registrant to Team::first() and gave them a staff role. Registration must produce a user with no team and no role. Teams are granted deliberately through TeamPolicy::create (which requires the create_store permission). Do not reintroduce convenience role assignment at signup — it bypasses the gate that keeps strangers out of the merchant panel. If you add a second registration path (invites, B2B signup), have it go through the same policy, not through a copy of the Fortify action.

Panel authorization is per-panel, and it reads $this. The v1.0.3 canAccessPanel asks a different question per panel: admin → super_admin, app → team membership. Copy that shape for any panel you add rather than a bare return true. Note it now reads $this rather than auth()->user() — the old form authorized the session user instead of the user being passed in, which is a subtle and dangerous bug class. If you override panel access anywhere, do the same.

Post-login redirect targets the storefront. Login and register now default there and use intended() so a shopper interrupted mid-checkout returns to it. Shoppers keep /orders, /wishlist, /invoices, /payment_methods. If you add an account area, extend that redirect logic rather than adding another competing one — and don't resurrect RoleBasedRedirect (see Gotchas).

Cart → order pattern. Model the cart as session/DB state that is not an order, and convert it in one transactional service call:

DB::transaction(function () use ($cart) {
    $order = Order::createFromCart($cart);   // snapshots prices & line items
    $order->reserveStock();                  // locks rows, throws on shortage
    event(new OrderPlaced($order));
});

Line-item price snapshots are non-negotiable: catalog prices change, historical orders must not.

Admin surface. Use the admin resource layer (Filament/Livewire) for CRUD, but put anything with side effects (refunds, stock adjustments, order status transitions) behind a Policy plus an Action class so the same logic is reachable from the storefront, the admin, and the console. When you add a resource, add both the policy and the Shield permission rows in the same commit — v1.0.3's ArticleResource/CollectionResource gap exists precisely because those two halves were never seeded together, and strictAuthorization being off means the omission fails open rather than throwing.

Testing loop:

php artisan test --filter=Checkout
php artisan test --parallel

The suite is healthy (1,245 tests, 7 added in v1.0.3) — use it as a regression harness, not just a smoke test. Prefer RefreshDatabase + factories over seeding in tests. Seeders are for humans; factories are for CI.

Test fixtures must mirror production seeding. The registration bug in v1.0.3 survived CI because a test helper (FortifyActionsTest::makeTeam()) created the team and the staff role in one step — a state production could never reach after RolesSeeder stopped creating staff. That helper is gone. When you write fixtures for auth, billing, or team flows, build the state the way the real seeder and the real signup path build it. A green pipeline with a synthetic fixture is worse than a red one.

Local dev stack. Run npm run dev (Vite HMR) alongside php artisan serve. If you touch jobs, mail, or webhooks, run a worker (php artisan queue:work) and a mail catcher in the same terminal tab set — otherwise "it works for the storefront, hangs in checkout."

Agent-assisted development. Follow the repo's own conventions: file issues on the upstream tracker rather than in a scratch file, apply one triage label, and record any architectural decision that agents should not re-litigate as an ADR under docs/adr/. Update the root CONTEXT.md when you add a bounded context (e.g. "subscriptions") that the single-context doc doesn't cover. The v1.0.3 release notes themselves are a good model of the expected detail level: root cause, why CI missed it, what's still broken.

Gotchas and Tips

Version resolution is actively hostile. Packagist serves v13.0.0 as the highest tag because it sorts above 1.0.x, and the v1 tag is the only thing pointing at the current line. Pinning 1.0.3 (or dev-main at a SHA) is mandatory, not hygiene. Same warning applies to composer outdated output and any dependency bot in your fork.

The description lies. "The skeleton application for the Laravel framework" is the stock Laravel one-liner — it tells you nothing about ecommerce features. Don't plan from it; plan from CONTEXT.md, the seeders, and the admin resource registration.

It's a skeleton, so upgrades are manual. There's no composer update path that carries upstream features into your fork. Budget for divergence: keep your changes in clearly separated namespaces/config keys so upstream diffs stay readable.

Missing policies on ArticleResource / CollectionResource. Neither has a policy, and Filament's strictAuthorization is off, so a team member can CRUD their own tenant's articles and collections — price included — without any permission check. v1.0.3 narrowed the blast radius (it now takes a team to reach, not a stranger), but the hole remains. If you're doing anything content- or pricing-related, write those policies and seed the permission set in the same commit; the Shield set has no article_*/collection_* entries, so a policy alone will lock the resource down completely for everyone but super admins. Also consider turning strictAuthorization on once you've covered the resources you actually use — failing open is the default here.

SVG upload is stored XSS, and storage:link arms it. Product upload accepts image/svg+xml onto a public disk. It's unreachable today only because no public/storage symlink exists — the moment you run the storage:link step in Getting Started, it goes live. Decide before deploy: drop SVG from the allowed MIME list, sanitize on upload, or serve media from a separate origin. Don't leave it to a later ticket.

/user/profile breaks if social login is enabled. // use HasConnectedAccounts; is commented out on User, so connectedAccounts is null and Socialstream calls ->map() on it. Either enable the trait or disable Socialstream; a half-configured install 500s on the profile route.

Seeded storefront is sold out. inventory_count = 0 on every product. Not a bug in checkout — the demo data. Set inventory before debugging "add to cart does nothing."

Money. Never let floats touch a total. Use integer minor units or a dedicated money cast/value object, and round once, at the boundary. Check how the skeleton casts price columns before adding features — mismatches here surface as one-cent differences in order totals.

Inventory races. Two simultaneous checkouts will oversell unless you lock. Wrap stock decrement in a transaction with lockForUpdate() (or an atomic conditional UPDATE ... WHERE stock >= ?) rather than read-then-write.

permission.teams is false. Roles are global in practice, even though plenty of code reads as if they were team-scoped. If you build multi-tenant authorization, understand this first: team gating currently comes from team membership, not from team-scoped roles. Don't design a permission model on an assumption the config contradicts.

RoleBasedRedirect is dead code inherited from a property-management app; all six of its targets 404. Don't extend it, don't debug it — delete it and use the storefront-plus-intended() redirect.

Password rules are weak by default. Password::default() is min 8, no complexity, no breach check. Set the policy in your own service provider before launch.

Tiny ecosystem, thin docs. With ~190 stars and no dependents, expect to be the first person to hit most issues. Treat the source as the documentation, and prefer reading Filament resources / route files over searching the web. Vendor-dir grepping beats guessing:

grep -rn "class OrderService" app/ vendor/ | head

Config cache bites. php artisan config:cache in a deploy will freeze the config; if a value comes from .env at runtime it will silently be null. After changing .env or config, run php artisan optimize:clear.

Storage and media. Uploaded product images 404 until php artisan storage:link, and on a container deploy the link must be recreated. Prefer an object-storage disk in staging/prod so you're debugging the same code path as production.

Queue-backed side effects. Order confirmation mail, webhook dispatch, and invoice generation are usually queued. If checkout "succeeds" but nothing happens, check jobs/failed_jobs first — php artisan queue:failed and queue:retry all are the two commands you'll reach for.

Extension points to prefer over editing core files:

  • Container bindings in a service provider for swapping payment/shipping implementations.
  • Events + listeners for post-order side effects instead of appending code to the checkout service.
  • Filament plugins / resource extension rather than editing vendor resources — and a policy plus seeded permissions whenever you add one.
  • Custom casts and model scopes for domain rules that must apply everywhere.

Debugging triad. php artisan about for environment sanity, DB::listen(fn ($q) => logger($q->sql)) for N+1 hunting in the catalog, and ddd() inside a Blade/Livewire component when a view variable is mysteriously empty. Eager-load product relations (with('variants.prices')) — the storefront listing is the usual N+1 hotspot. For panel-access problems specifically, log inside canAccessPanel and check $this->roles and $this->teams — not auth()->user(), which may be a different user than the one being authorized.

Seeders are slow. Full seeding on every migrate:fresh will dominate your test loop. Add a --class= targeted seeder for the slice you're working on, and keep RefreshDatabase + factories as the default for tests.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin