> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lunarphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Extending Carts

The cart system is extended by swapping action contracts in the container, and by registering pipeline steps and validators for its calculation and validation lists.

## Overview

Every operation `Lunar\Core\Models\Cart` performs — adding a line, setting a shipping option, creating an order — is a one-line delegation from a verb method on the model to an action contract resolved from the container. Swapping behavior means binding a different implementation of that contract in a service provider; the verb picks it up automatically.

```php theme={null}
public function add(Purchasable $purchasable, int $quantity = 1, array $meta = [], bool $refresh = true): Cart
{
    // ...
    app(AddsOrUpdatesPurchasable::class)->execute($this, $purchasable, $quantity, $meta);

    return $refresh ? $this->refresh()->recalculate() : $this;
}
```

## Cart action contracts

`Lunar\Core\ActionServiceProvider`'s `$actions` map is the canonical list of swappable action seams. The cart-related entries, each a `Lunar\Core\Contracts\Actions\Carts\*` interface bound to its `Lunar\Core\Actions\Carts\*` default implementation:

| Contract                   | Default implementation   |
| -------------------------- | ------------------------ |
| `AddsAddress`              | `AddAddress`             |
| `AddsOrUpdatesPurchasable` | `AddOrUpdatePurchasable` |
| `AssociatesUser`           | `AssociateUser`          |
| `CalculatesLine`           | `CalculateLine`          |
| `CalculatesLineSubtotal`   | `CalculateLineSubtotal`  |
| `CreatesOrder`             | `CreateOrder`            |
| `GeneratesFingerprint`     | `GenerateFingerprint`    |
| `GetsExistingCartLine`     | `GetExistingCartLine`    |
| `MergesCart`               | `MergeCart`              |
| `RemovesPurchasable`       | `RemovePurchasable`      |
| `SetsShippingOption`       | `SetShippingOption`      |
| `UpdatesCartLine`          | `UpdateCartLine`         |

Rebind a contract from a consumer's own service provider:

```php theme={null}
use Lunar\Core\Contracts\Actions\Carts\AddsOrUpdatesPurchasable;

public function register(): void
{
    $this->app->bind(AddsOrUpdatesPurchasable::class, MyAddsOrUpdatesPurchasable::class);
}
```

<Info>
  Config-string class substitution (`config('lunar.*', SomeClass::class)`) is not used for these seams — the contract is bound in the container, and a consumer swaps it there. Do not add a config key to point at a custom class.
</Info>

## Calculation pipelines

Cart totals are calculated by running the cart through an ordered list of pipeline classes, and cart lines through a second list. Both lists live in `config/lunar/cart.php`, as ordered lists rather than single-implementation swaps:

```php theme={null}
'pipelines' => [
    'cart' => [
        Lunar\Core\Pipelines\Cart\CalculateLines::class,
        Lunar\Core\Pipelines\Cart\ApplyShipping::class,
        Lunar\Core\Pipelines\Cart\CalculateShippingSubTotal::class,
        Lunar\Core\Pipelines\Cart\ApplyDiscounts::class,
        Lunar\Core\Pipelines\Cart\CalculateTax::class,
        Lunar\Core\Pipelines\Cart\Calculate::class,
    ],

    'cart_lines' => [
        Lunar\Core\Pipelines\CartLine\GetUnitPrice::class,
    ],
],
```

Add a pipe by appending a class to the relevant array. A cart pipe implements `handle(Cart $cart, Closure $next): mixed`; a cart line pipe implements the same shape against `CartLine`.

```php theme={null}
<?php

namespace App\Pipelines\Cart;

use Closure;
use Lunar\Core\Models\Cart;

class CustomCartPipeline
{
    public function handle(Cart $cart, Closure $next): mixed
    {
        // ...

        return $next($cart);
    }
}
```

<Tip>
  Pipes run in the order listed, top to bottom.
</Tip>

## Validators

`Cart::add()`, `updateLine()`, `remove()`, `addAddress()`, `setShippingOption()`, and `createOrder()` each run a list of validators, configured the same way, in `config/lunar/cart.php`:

```php theme={null}
'validators' => [
    'add_to_cart' => [
        Lunar\Core\Validation\CartLine\CartLineQuantity::class,
        Lunar\Core\Validation\CartLine\CartLineStock::class,
        Lunar\Core\Validation\CartLine\CartLineAvailability::class,
    ],
    'update_cart_line' => [/* ... */],
    'remove_from_cart' => [],
    'set_shipping_option' => [
        Lunar\Core\Validation\Cart\ShippingOptionValidator::class,
    ],
    'order_create' => [
        Lunar\Core\Validation\Cart\ValidateCartForOrderCreation::class,
    ],
],
```

A validator extends `Lunar\Core\Validation\BaseValidator`:

```php theme={null}
<?php

namespace App\Validation\CartLine;

use Lunar\Core\Validation\BaseValidator;

class CartLineQuantity extends BaseValidator
{
    public function validate(): bool
    {
        $quantity = $this->parameters['quantity'] ?? 0;

        if (! $condition) {
            return $this->fail('cart', 'Something went wrong');
        }

        return $this->pass();
    }
}
```

Register it against the relevant action in `config/lunar/cart.php`:

```php theme={null}
'validators' => [
    'add_to_cart' => [
        // ...
        App\Validation\CartLine\CartLineQuantity::class,
    ],
],
```

If validation fails, `Lunar\Core\Exceptions\Carts\CartException` is thrown. Errors are accessed the same way as Laravel's own validation responses:

```php theme={null}
try {
    $cart->setShippingOption($option);
} catch (\Lunar\Core\Exceptions\Carts\CartException $e) {
    $e->errors()->all();
}
```

## Cart line modifiers

A cart line unit price and subtotal can be adjusted just after it is calculated, using a `Lunar\Core\Modifiers\CartLineModifier`:

```php theme={null}
<?php

namespace App\Modifiers;

use Closure;
use Lunar\Core\Models\CartLine;
use Lunar\Core\Modifiers\CartLineModifier;

class MyCartLineModifier extends CartLineModifier
{
    public function subtotalled(CartLine $cartLine, Closure $next): CartLine
    {
        // Adjust $cartLine->subTotal, ->unitPrice, ...

        return $next($cartLine);
    }
}
```

`CartLineModifier` also exposes `calculating()` and `calculated()` hooks. Register an instance with the `Lunar\Core\Modifiers\CartLineModifiers` collection, resolved from the container:

```php theme={null}
use App\Modifiers\MyCartLineModifier;
use Lunar\Core\Modifiers\CartLineModifiers;

public function boot(): void
{
    app(CartLineModifiers::class)->add(new MyCartLineModifier);
}
```

<Warning>
  `Lunar\Core\Modifiers\CartModifier` and its `CartModifiers` collection are also registered in the container, but nothing in core currently runs a cart through them — only `CartLineModifier` (via `CalculateLineSubtotal`) and the shipping equivalent, `ShippingModifier` (see [Extending Shipping](/2.x/extending/shipping)), are wired into a live calculation step. Do not rely on `CartModifier` for cart-level hooks until it is dispatched somewhere in core.
</Warning>

## Overriding order creation

`Cart::createOrder()` delegates to `Contracts\Carts\CreatesOrder`, which in turn runs the order through the pipeline configured in `config/lunar/orders.php`. See [Extending Orders](/2.x/extending/orders) for the order-side pipeline and action contracts.
