offline/oc-mall-plugin
Mall is a full-featured e-commerce plugin for October CMS built on Laravel. It adds products, variants, carts, orders, payments, discounts, tax and shipping handling, plus a customizable checkout and extensible Twig storefront components for building online shops quickly.
offline/oc-mall-plugin is the October CMS integration for the offline/mall e-commerce package. Treat it as "Mall for October": the core commerce logic lives in the underlying Laravel package, and this plugin wires it into October's backend menus, CMS components, settings, and mail templating.
Version choice first. As of v2.0.0 the plugin requires October 2+. If you are still on October 1, you must stay on the ^1.0 line — ^2.0 will not install or run there. Pin explicitly rather than relying on ^ resolution:
# October 2+
composer require offline/oc-mall-plugin:^2.0
# October 1 (legacy)
composer require offline/oc-mall-plugin:^1.0
Then:
php artisan october:migrate
Open the backend Mall menu. This is where you will spend most of your first day: create a category, create a product with at least one variant, set a price and tax class, and publish it. Before the frontend works, configure Settings → Mall: currency/locale, tax classes and rates, shipping methods, payment methods, and order statuses.
Where to look first:
plugins/offline/mall/ (or vendor/offline/oc-mall-plugin/) — read Plugin.php, components/, classes/, and models/ to understand what is exposed.First real use case: build a product page and cart. Add the Product and Cart components to a CMS page/partial, render the product and its variants, and use October AJAX handlers to add to cart:
{% component 'Product' %}
{% component 'Cart' %}
$cart = \OFFLINE\Mall\Facades\Mall::cart();
$cartItem = $cart->add($variant, 2); // variant-aware
Once add-to-cart works, move to the Checkout component and a test payment method (offline/invoice is the easiest). That sequence — catalog → cart → checkout → order in backend — is the fastest way to validate the whole install.
Mall separates Product from Variant. Stock, SKU, and often price overrides live on the variant. Model prices as integer minor units (cents) and always format through Mall's money helpers rather than raw number formatting. If a product has no variants, Mall still exposes a default variant behavior, but be explicit in your Twig and PHP code: $product->variants vs $product->price.
Do not reach into the session for cart data. Use:
use OFFLINE\Mall\Facades\Mall;
$cart = Mall::cart();
$cart->add($variant, $quantity);
$cart->remove($cartItem);
$cart->updateQuantity($cartItem, 3);
Cart data is session-backed, so any route or component that touches it must run inside October's web middleware/session stack. This is the most common source of "cart is empty on AJAX" bugs.
The Checkout component handles address collection, shipping selection, payment selection, and order creation. For custom checkouts, reuse Mall's cart/order services rather than hand-rolling Order records — order creation triggers inventory reduction, discount application, tax recalculation, and mail dispatch.
Orders follow a status flow configurable in settings. Hook into it with events:
Event::listen('mall.order.created', function ($order) {
// push to ERP, send Slack notification, etc.
});
Event::listen('mall.order.status.changed', function ($order, $previousStatus) {
//
});
Custom payment gateways extend Mall's payment method base class and implement the process/redirect/webhook lifecycle. Custom shipping methods extend the shipping method base class and return rates for a given cart/address. Register them through the backend settings so shop admins can enable them per store.
Webhooks are the tricky part: your gateway's callback route must be publicly reachable, excluded from CSRF where appropriate, and must resolve the correct Order and Payment record. Test with the gateway's sandbox and a real HTTPS tunnel — localhost-only testing hides signature/URL bugs.
Use October's Model::extend() in your own plugin's boot() method:
\OFFLINE\Mall\Models\Product::extend(function ($model) {
$model->addDynamicMethod('isFeatured', fn () => $model->featured);
});
Add fields via a migration in your own plugin, then extend the backend form YAML with $model->bindEvent('backend.form.extendFields', ...) or October's standard form extension config. Never edit files inside vendor/ or the plugin directory if it is Composer-managed.
Mall supports discounts and coupon codes. Prefer configuring them in the backend over hard-coding price logic. If you need custom eligibility rules, extend the discount classes/events rather than mutating cart totals directly — manual total mutations get overwritten by Mall's recalculation on the next cart operation.
Mall ships Twig components (Product, ProductList, Category, Cart, Checkout, address/account components). Use October's AJAX framework for cart interactions so partials re-render correctly. Keep cart/checkout partials small and re-render only the changed region; full-page reloads on quantity changes make the checkout feel broken.
v2.0.0 is a platform bump, not a feature release. The only substantive change beyond the October 2 requirement is a fix for issues with newer Laravel versions. That means:
Mall::cart(), Twig components, events, model extension — is unchanged, so existing integrations should carry over without code rewrites.^1.0; October 2 shops should pin ^2.0. Do not use a loose constraint and let Composer pick — a silent major bump across an October version boundary is an outage, not an upgrade.config/mail.php.Model::extend(), and backend form/list extensions. This matters more than ever across a major bump.v1.16.2...v2.0.0), and test the full checkout flow on a staging clone. Pin the version in composer.json and budget time for compatibility patches — 2.x fixes newer-Laravel breakage but does not guarantee your other plugins are October 2-ready.mall.order.created, cart events) to storage/logs temporarily, and inspect the mall_orders, mall_order_items, and mall_carts tables directly. A surprising number of "totals are wrong" reports are stale cart rows or misassigned tax classes.number_format directly on stored prices; use Mall's formatter so locale, currency symbol placement, and rounding stay consistent between cart, order emails, and backend.How can I help you explore Laravel packages today?