Skip to content

Events

Meloncart fires events throughout the order lifecycle, cart operations, and product management. You can listen to these events in your plugin's boot() method to extend or customize store behavior.

php
public function boot()
{
    \Event::listen('shop.newOrder', function($order) {
        // Handle new order
    });
}

Order Events

shop.beforePlaceOrder

Fires before a new order is placed via checkout. Use this to perform pre-order validation or preparation.

ParameterTypeDescription
$cartNamestringName of the cart being converted to an order

Return: Not checked.

php
Event::listen('shop.beforePlaceOrder', function($cartName) {
    // Validate external conditions before order placement
    if (!ExternalService::isAvailable()) {
        throw new ApplicationException('Service unavailable, please try again.');
    }
});

shop.placeOrderError

Fires when an error occurs during the order placement process.

ParameterTypeDescription
$messagestringThe error message
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.placeOrderError', function($message, $cartName) {
    Log::error('Order placement failed', [
        'error' => $message,
        'cart' => $cartName
    ]);
});

shop.beforeCreateOrderRecord

Fires before a new order record is saved to the database, whether via checkout or the admin panel.

ParameterTypeDescription
$orderOrderThe order model about to be created

Return: Not checked.

php
Event::listen('shop.beforeCreateOrderRecord', function($order) {
    // Set a custom field before the order is created
    $order->custom_reference = ExternalService::generateReference();
});

shop.beforeUpdateOrderRecord

Fires before an existing order record is updated.

ParameterTypeDescription
$orderOrderThe order model about to be updated

Return: Not checked.

php
Event::listen('shop.beforeUpdateOrderRecord', function($order) {
    // Log changes to specific fields
    if ($order->isDirty('shipping_address_line1')) {
        Log::info('Shipping address changed for order #' . $order->id);
    }
});

shop.newOrder

Fires after a new order has been successfully placed and saved. Use this for post-order processing such as syncing with external systems.

ParameterTypeDescription
$orderOrderThe newly created order

Return: Not checked.

php
Event::listen('shop.newOrder', function($order) {
    // Sync to external ERP system
    ErpService::createOrder([
        'reference' => $order->id,
        'total' => $order->total,
        'items' => $order->items->toArray()
    ]);
});

shop.order.orderPaid

Fires when an order is marked as paid (payment has been confirmed).

ParameterTypeDescription
$orderOrderThe order that was paid

Return: Not checked.

php
Event::listen('shop.order.orderPaid', function($order) {
    // Trigger fulfillment workflow
    FulfillmentService::enqueue($order);
});

shop.order.getNotificationVars

Fires when building template variables for order notification emails. Return an array of additional variables to merge into the email template data.

ParameterTypeDescription
$orderOrderThe order model

Return: Array of variables to merge into the notification template.

php
Event::listen('shop.order.getNotificationVars', function($order) {
    return [
        'estimated_delivery' => ShippingCalculator::estimateDelivery($order),
        'loyalty_points_earned' => LoyaltyService::calculatePoints($order)
    ];
});

shop.order.itemDisplayDetails

Fires when rendering order item details, providing an opportunity to append extra information to the item display.

ParameterTypeDescription
$itemOrderItemThe order item being displayed
$plainTextbooleanWhether the output is plain text (true) or HTML (false)

Return: Array of detail strings to append to the item display.

php
Event::listen('shop.order.itemDisplayDetails', function($item, $plainText) {
    $details = [];

    if ($item->product->is_gift_wrapped) {
        $details[] = $plainText ? 'Gift wrapped' : '<em>Gift wrapped</em>';
    }

    return $details;
});

Order Status Events

shop.beforeUpdateOrderStatus

Fires before an order's status is changed. Return false to prevent the status update.

ParameterTypeDescription
$recordOrderStatusLogThe status log record being created
$orderOrderThe order model
$statusIdintegerThe new status ID
$previousStatusOrderStatusThe previous status

Return: Return false to cancel the status change.

php
Event::listen('shop.beforeUpdateOrderStatus', function($record, $order, $statusId, $previousStatus) {
    // Prevent transitioning to "Shipped" without a tracking code
    $shippedStatus = OrderStatus::where('code', 'shipped')->first();
    if ($statusId == $shippedStatus?->id && $order->tracking_codes->isEmpty()) {
        throw new ApplicationException('Cannot mark as shipped without a tracking code.');
    }
});

shop.order.stockChanged

Fires before stock is decreased when an order is marked as paid. Return false to prevent stock reduction, useful if you manage inventory externally.

ParameterTypeDescription
$orderOrderThe order model
$statusPaidbooleanWhether the new status represents a paid state

Return: Return false to prevent stock from being decreased.

php
Event::listen('shop.order.stockChanged', function($order, $statusPaid) {
    // Handle stock externally via warehouse API
    WarehouseApi::decreaseStock($order->items);

    // Prevent Meloncart from also decreasing stock
    return false;
});

shop.order.updateStatus

Fires after an order's status has been successfully updated.

ParameterTypeDescription
$orderOrderThe order model
$statusOrderStatusThe new status
$previousStatusOrderStatusThe previous status

Return: Not checked.

php
Event::listen('shop.order.updateStatus', function($order, $status, $previousStatus) {
    // Notify external systems of status change
    WebhookService::fire('order.status_changed', [
        'order_id' => $order->id,
        'from' => $previousStatus->code,
        'to' => $status->code
    ]);
});

shop.beforeOrderInternalStatusNotification

Fires before sending an internal status notification email to administrators. Return false to prevent the notification.

ParameterTypeDescription
$orderOrderThe order model
$statusOrderStatusThe status triggering the notification

Return: Return false to suppress the admin notification email.

php
Event::listen('shop.beforeOrderInternalStatusNotification', function($order, $status) {
    // Suppress internal notifications for test orders
    if ($order->billing_email === 'test@example.com') {
        return false;
    }
});

Cart Events

shop.cart.beforeAddProduct

Fires before a product is added to the cart.

ParameterTypeDescription
$productProductThe product being added
$optionsarrayCart options including quantity, selected options, extras

Return: Not checked.

php
Event::listen('shop.cart.beforeAddProduct', function($product, $options) {
    // Enforce purchase limits
    $maxQty = $product->max_purchase_quantity;
    if ($maxQty && $options['quantity'] > $maxQty) {
        throw new ApplicationException(
            "Maximum purchase quantity for this product is {$maxQty}."
        );
    }
});

shop.cart.addProduct

Fires after a product has been successfully added to the cart.

ParameterTypeDescription
$productProductThe product that was added
$optionsarrayCart options including quantity, selected options, extras

Return: Not checked.

php
Event::listen('shop.cart.addProduct', function($product, $options) {
    // Track add-to-cart analytics
    Analytics::track('product_added_to_cart', [
        'product_id' => $product->id,
        'product_name' => $product->name,
        'quantity' => $options['quantity']
    ]);
});

shop.cart.processCustomData

Fires when a product is added to the cart, allowing you to modify or inject custom data that is stored with the cart item. The custom data array is passed by reference.

ParameterTypeDescription
&$customDataarrayCustom data array (passed by reference)
$productProductThe product being added
$optionsarrayCart options

Return: Not checked. Modify $customData directly by reference.

php
Event::listen('shop.cart.processCustomData', function(&$customData, $product, $options) {
    // Attach a gift message to the cart item
    if ($message = post('gift_message')) {
        $customData['gift_message'] = $message;
    }
});

shop.cart.beforeRemoveItem

Fires before a cart item is removed.

ParameterTypeDescription
$itemKeystringThe unique key of the cart item being removed
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.cart.beforeRemoveItem', function($itemKey, $cartName) {
    Log::info("Removing item {$itemKey} from cart {$cartName}");
});

shop.cart.afterRemoveItem

Fires after a cart item has been removed.

ParameterTypeDescription
$itemKeystringThe unique key of the cart item that was removed
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.cart.afterRemoveItem', function($itemKey, $cartName) {
    Analytics::track('product_removed_from_cart', [
        'item_key' => $itemKey
    ]);
});

shop.cart.beforeSetQuantity

Fires before a cart item's quantity is changed.

ParameterTypeDescription
$itemKeystringThe unique key of the cart item
$quantityintegerThe new quantity
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.cart.beforeSetQuantity', function($itemKey, $quantity, $cartName) {
    if ($quantity > 100) {
        throw new ApplicationException('Maximum quantity per item is 100.');
    }
});

shop.cart.setQuantity

Fires after a cart item's quantity has been changed.

ParameterTypeDescription
$itemKeystringThe unique key of the cart item
$quantityintegerThe new quantity
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.cart.setQuantity', function($itemKey, $quantity, $cartName) {
    Analytics::track('cart_quantity_changed', [
        'item_key' => $itemKey,
        'quantity' => $quantity
    ]);
});

shop.cart.getPrice

Fires when a cart item price is calculated for display. The price is passed by reference, allowing you to override it.

ParameterTypeDescription
$itemCartItemThe cart item
&$priceintegerThe current price in cents (passed by reference)

Return: Not checked. Modify $price directly by reference.

php
Event::listen('shop.cart.getPrice', function($item, &$price) {
    // Apply a member-only discount
    $user = Auth::getUser();
    if ($user && $user->inGroup('vip')) {
        $price = (int) ($price * 0.9); // 10% VIP discount
    }
});

shop.cart.afterEvaluatePriceRules

Fires after all price rules have been evaluated on the cart.

ParameterTypeDescription
$resultmixedThe price rule evaluation result
$cartNamestringName of the cart

Return: Not checked.

php
Event::listen('shop.cart.afterEvaluatePriceRules', function($result, $cartName) {
    // Log discount totals for analytics
    Log::debug('Price rules evaluated for cart: ' . $cartName);
});

Product Events

shop.product.getOriginalPrice

Fires when a product's price is calculated for display. The price is passed by reference, allowing you to override it.

ParameterTypeDescription
&$priceintegerThe current price in cents (passed by reference)
$productProductThe product model
$quantityintegerThe quantity being priced
$userGroupIdinteger|nullThe user group ID for group-based pricing

Return: Not checked. Modify $price directly by reference.

php
Event::listen('shop.product.getOriginalPrice', function(&$price, $product, $quantity, $userGroupId) {
    // Apply a dynamic pricing algorithm
    if ($product->categories->contains('code', 'clearance')) {
        $price = (int) ($price * 0.5); // 50% off clearance items
    }
});

shop.product.getExtraOriginalPrice

Fires when a product extra's price is calculated for display. The price is passed by reference.

ParameterTypeDescription
&$priceintegerThe current extra price in cents (passed by reference)
$extraProductExtraThe product extra model
$productProductThe parent product model

Return: Not checked. Modify $price directly by reference.

php
Event::listen('shop.product.getExtraOriginalPrice', function(&$price, $extra, $product) {
    // Make extras free for VIP users
    $user = Auth::getUser();
    if ($user && $user->inGroup('vip')) {
        $price = 0;
    }
});

shop.productOutOfStock

Fires when a product goes out of stock after inventory is decreased.

ParameterTypeDescription
$productProductThe product that is now out of stock

Return: Not checked.

php
Event::listen('shop.productOutOfStock', function($product) {
    // Notify the merchandising team
    Notification::send($merchandisingTeam, new ProductOutOfStockNotification($product));
});

shop.variantOutOfStock

Fires when a product variant goes out of stock after inventory is decreased.

ParameterTypeDescription
$variantProductVariantThe variant that is now out of stock

Return: Not checked.

php
Event::listen('shop.variantOutOfStock', function($variant) {
    Log::warning("Variant out of stock: {$variant->product->name} - {$variant->sku}");
});

shop.product.getNotificationVars

Fires when building template variables for product notification emails (such as low stock alerts). Return an array of additional variables.

ParameterTypeDescription
$productProductThe product model

Return: Array of variables to merge into the notification template.

php
Event::listen('shop.product.getNotificationVars', function($product) {
    return [
        'reorder_url' => SupplierService::getReorderUrl($product->sku),
        'supplier_name' => $product->supplier?->name
    ];
});

Checkout Events

shop.checkout.beforeSetCouponCode

Fires before a coupon code is applied to the checkout. The code is passed by reference, allowing you to modify it.

ParameterTypeDescription
&$codestringThe coupon code being applied (passed by reference)

Return: Not checked. Modify $code directly by reference.

php
Event::listen('shop.checkout.beforeSetCouponCode', function(&$code) {
    // Normalize coupon codes to uppercase
    $code = strtoupper(trim($code));
});

Backend UI Events

These events are fired in the admin panel and can be used to extend backend pages.

shop.orders.extendPreviewTabs

Add custom tabs to the order preview page in the admin panel. Return an array mapping tab names to partial paths.

Return: Array of ['Tab Name' => '~/path/to/partial.php'].

php
Event::listen('shop.orders.extendPreviewTabs', function() {
    return [
        'Shipping Labels' => '~/plugins/vendor/shipping/controllers/labels/_order_tab.php'
    ];
});

shop.products.extendPreviewTabs

Add custom tabs to the product preview page in the admin panel.

Return: Array of ['Tab Name' => '~/path/to/partial.php'].

php
Event::listen('shop.products.extendPreviewTabs', function() {
    return [
        'Analytics' => '~/plugins/vendor/analytics/controllers/products/_analytics_tab.php'
    ];
});

shop.orders.extendListToolbar

Add custom buttons or controls to the orders list toolbar in the admin panel. Return HTML to insert into the toolbar.

php
Event::listen('shop.orders.extendListToolbar', function($controller) {
    return '<a href="/export-orders" class="btn btn-sm btn-secondary ms-3">Export All</a>';
});

shop.products.extendListToolbar

Add custom buttons or controls to the products list toolbar.

php
Event::listen('shop.products.extendListToolbar', function($controller) {
    return '<a href="/sync-products" class="btn btn-sm btn-secondary ms-3">Sync Inventory</a>';
});

shop.categories.extendListToolbar

Add custom buttons or controls to the categories list toolbar.

php
Event::listen('shop.categories.extendListToolbar', function($controller) {
    return '<a href="/rebuild-tree" class="btn btn-sm btn-secondary ms-3">Rebuild Tree</a>';
});

Event Reference

Summary Table

EventParametersCancellableCategory
shop.beforePlaceOrder$cartNameNoOrder
shop.placeOrderError$message, $cartNameNoOrder
shop.beforeCreateOrderRecord$orderNoOrder
shop.beforeUpdateOrderRecord$orderNoOrder
shop.newOrder$orderNoOrder
shop.order.orderPaid$orderNoOrder
shop.order.getNotificationVars$orderNoOrder
shop.order.itemDisplayDetails$item, $plainTextNoOrder
shop.beforeUpdateOrderStatus$record, $order, $statusId, $prevStatusYesStatus
shop.order.stockChanged$order, $statusPaidYesStatus
shop.order.updateStatus$order, $status, $prevStatusNoStatus
shop.beforeOrderInternalStatusNotification$order, $statusYesStatus
shop.cart.beforeAddProduct$product, $optionsNoCart
shop.cart.addProduct$product, $optionsNoCart
shop.cart.processCustomData&$customData, $product, $optionsNoCart
shop.cart.beforeRemoveItem$itemKey, $cartNameNoCart
shop.cart.afterRemoveItem$itemKey, $cartNameNoCart
shop.cart.beforeSetQuantity$itemKey, $quantity, $cartNameNoCart
shop.cart.setQuantity$itemKey, $quantity, $cartNameNoCart
shop.cart.getPrice$item, &$priceNoCart
shop.cart.afterEvaluatePriceRules$result, $cartNameNoCart
shop.product.getOriginalPrice&$price, $product, $quantity, $groupIdNoProduct
shop.product.getExtraOriginalPrice&$price, $extra, $productNoProduct
shop.productOutOfStock$productNoProduct
shop.variantOutOfStock$variantNoProduct
shop.product.getNotificationVars$productNoProduct
shop.checkout.beforeSetCouponCode&$codeNoCheckout
shop.orders.extendPreviewTabs--NoBackend
shop.products.extendPreviewTabs--NoBackend
shop.orders.extendListToolbar$controllerNoBackend
shop.products.extendListToolbar$controllerNoBackend
shop.categories.extendListToolbar$controllerNoBackend

TIP

Parameters prefixed with & are passed by reference and can be modified directly in your event listener.

WARNING

Events marked as Cancellable will prevent the default behavior when you return false. Use these carefully to avoid disrupting the normal order workflow.