> ## 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.

# Carts

> Manage shopping carts, calculate totals, and handle cart sessions in Lunar.

Carts manage the collection of items a customer intends to purchase, with dynamic price calculation.

## Overview

Carts hold a collection of purchasable items (typically product variants) that a customer intends to order. They belong to users (which relate to customers) and support a single currency each.

<Info>
  Cart prices are dynamically calculated and are **not** stored in the database. Once a cart is converted to an order, the prices are persisted on the order instead.
</Info>

## Cart Model

```php theme={null}
Lunar\Models\Cart
```

| Field         | Type                   | Description                                 |
| :------------ | :--------------------- | :------------------------------------------ |
| id            | `bigIncrements`        | Primary key                                 |
| user\_id      | `foreignId` `nullable` | For guest carts                             |
| customer\_id  | `foreignId` `nullable` |                                             |
| merged\_id    | `foreignId` `nullable` | References the cart this was merged into    |
| currency\_id  | `foreignId`            |                                             |
| channel\_id   | `foreignId`            |                                             |
| order\_id     | `foreignId` `nullable` | References the order created from this cart |
| coupon\_code  | `string` `nullable`    | Promotional coupon code                     |
| completed\_at | `dateTime` `nullable`  |                                             |
| meta          | `json` `nullable`      |                                             |
| created\_at   | `timestamp`            |                                             |
| updated\_at   | `timestamp`            |                                             |
| deleted\_at   | `timestamp` `nullable` |                                             |

### Creating a Cart

```php theme={null}
use Lunar\Models\Cart;

$cart = Cart::create([
    'currency_id' => 1,
    'channel_id' => 2,
]);
```

### Relationships

| Relationship      | Type       | Related Model   | Description                                           |
| :---------------- | :--------- | :-------------- | :---------------------------------------------------- |
| `lines`           | Has many   | `CartLine`      | The line items in the cart.                           |
| `addresses`       | Has many   | `CartAddress`   | All addresses associated with the cart.               |
| `shippingAddress` | Has one    | `CartAddress`   | The shipping address (filtered by `type = shipping`). |
| `billingAddress`  | Has one    | `CartAddress`   | The billing address (filtered by `type = billing`).   |
| `currency`        | Belongs to | `Currency`      | The currency for the cart.                            |
| `user`            | Belongs to | Auth user model | The authenticated user who owns the cart.             |
| `customer`        | Belongs to | `Customer`      | The customer associated with the cart.                |
| `orders`          | Has many   | `Order`         | All orders created from the cart.                     |
| `draftOrder`      | Has one    | `Order`         | The unplaced (draft) order.                           |
| `completedOrder`  | Has one    | `Order`         | A single placed (completed) order.                    |
| `completedOrders` | Has many   | `Order`         | All placed (completed) orders.                        |

The `draftOrder` and `completedOrder` relationships accept an optional order ID parameter to filter to a specific order:

```php theme={null}
$cart->draftOrder;
$cart->completedOrder;

// Filter to a specific order
$cart->draftOrder($orderId)->first();
```

### Scopes

| Scope      | Description                                                                         |
| :--------- | :---------------------------------------------------------------------------------- |
| `unmerged` | Filters to carts that have not been merged into another cart (`merged_id` is null). |
| `active`   | Filters to carts that have no orders, or only have unplaced (draft) orders.         |

```php theme={null}
use Lunar\Models\Cart;

$unmergedCarts = Cart::unmerged()->get();

$activeCarts = Cart::active()->get();
```

## Cart Lines

Each item in a cart is represented by a `CartLine`. Lines link to a purchasable model (usually a `ProductVariant`) and track the desired quantity.

```php theme={null}
Lunar\Models\CartLine
```

| Field             | Type                 | Description                         |
| :---------------- | :------------------- | :---------------------------------- |
| id                | `bigIncrements`      | Primary key                         |
| cart\_id          | `foreignId`          |                                     |
| purchasable\_type | `string`             | Morph type for the purchasable item |
| purchasable\_id   | `unsignedBigInteger` | Morph ID for the purchasable item   |
| quantity          | `unsignedInteger`    |                                     |
| meta              | `json` `nullable`    |                                     |
| created\_at       | `timestamp`          |                                     |
| updated\_at       | `timestamp`          |                                     |

### Relationships

| Relationship  | Type            | Related Model | Description                                           |
| :------------ | :-------------- | :------------ | :---------------------------------------------------- |
| `cart`        | Belongs to      | `Cart`        | The cart this line belongs to.                        |
| `purchasable` | Morph to        | (polymorphic) | The purchasable item, typically a `ProductVariant`.   |
| `taxClass`    | Has one through | `TaxClass`    | The tax class, resolved through the purchasable item. |
| `discounts`   | Belongs to many | `Discount`    | Discounts applied to this line.                       |

### Adding Lines

Use the `add` method to add a purchasable item to the cart. If the item already exists in the cart, its quantity is updated instead.

```php theme={null}
$cart->add($purchasable, quantity: 2, meta: [
    'personalization' => 'Happy Birthday!',
]);
```

Multiple lines can be added at once using `addLines`:

```php theme={null}
use Lunar\Models\ProductVariant;

$cart->addLines([
    [
        'purchasable' => ProductVariant::find(1),
        'quantity' => 2,
        'meta' => ['foo' => 'bar'],
    ],
    [
        'purchasable' => ProductVariant::find(2),
        'quantity' => 1,
    ],
]);
```

Lines can also be created directly via the relationship:

```php theme={null}
$cart->lines()->create([
    'purchasable_type' => $purchasable->getMorphClass(),
    'purchasable_id' => $purchasable->id,
    'quantity' => 2,
    'meta' => [
        'personalization' => 'Happy Birthday!',
    ],
]);
```

### Updating Lines

```php theme={null}
// Update a single line's quantity and meta
$cart->updateLine($cartLineId, quantity: 3, meta: ['foo' => 'bar']);

// Update multiple lines at once
$cart->updateLines(collect([
    ['id' => 1, 'quantity' => 5, 'meta' => ['foo' => 'bar']],
    ['id' => 2, 'quantity' => 3],
]));
```

### Removing Lines

```php theme={null}
// Remove a specific line
$cart->remove($cartLineId);

// Remove all lines
$cart->clear();
```

### Line Validation

When adding, updating, or removing items, a series of validation actions are run (defined in `config/lunar/cart.php`). These throw a `CartException` on failure.

```php theme={null}
use Lunar\Exceptions\Carts\CartException;

try {
    $cart->add($purchasable, quantity: 500);
} catch (CartException $e) {
    $error = $e->getMessage();
}
```

## Exceptions

Lunar throws specific exceptions during cart operations to help identify what went wrong. All cart exceptions extend `Lunar\Exceptions\LunarException`.

### CartException

```php theme={null}
Lunar\Exceptions\Carts\CartException
```

The primary exception for cart validation failures. It is thrown by the validation pipeline when adding, updating, or removing cart lines, or when creating an order. It wraps a `MessageBag` containing one or more error messages.

```php theme={null}
use Lunar\Exceptions\Carts\CartException;

try {
    $cart->add($purchasable, quantity: 500);
} catch (CartException $e) {
    $e->getMessage(); // Summary of the first error
    $e->errors();     // Returns the full MessageBag
}
```

When creating an order, the `ValidateCartForOrderCreation` validator may throw a `CartException` with one of the following messages:

| Error Key                       | Cause                                                                                                        |
| :------------------------------ | :----------------------------------------------------------------------------------------------------------- |
| `carts.order_exists`            | The cart already has a completed order.                                                                      |
| `carts.billing_missing`         | No billing address has been set on the cart.                                                                 |
| `carts.billing_incomplete`      | The billing address is missing required fields (`country_id`, `first_name`, `line_one`, `city`, `postcode`). |
| `carts.shipping_missing`        | The cart contains shippable items but has no shipping address.                                               |
| `carts.shipping_incomplete`     | The shipping address is missing required fields.                                                             |
| `carts.shipping_option_missing` | The cart contains shippable items but no shipping option has been selected.                                  |

### InvalidCartLineQuantityException

```php theme={null}
Lunar\Exceptions\InvalidCartLineQuantityException
```

Thrown when attempting to add a purchasable to the cart with a quantity of zero or less.

```php theme={null}
use Lunar\Exceptions\InvalidCartLineQuantityException;

try {
    $cart->add($purchasable, quantity: 0);
} catch (InvalidCartLineQuantityException $e) {
    // Quantity must be at least 1
}
```

### NonPurchasableItemException

```php theme={null}
Lunar\Exceptions\NonPurchasableItemException
```

Thrown when creating or updating a cart line with a model that does not implement the `Lunar\Base\Purchasable` interface. This is checked automatically by the `CartLineObserver`.

### CartLineIdMismatchException

```php theme={null}
Lunar\Exceptions\CartLineIdMismatchException
```

Thrown when attempting to remove a cart line that does not belong to the specified cart.

```php theme={null}
use Lunar\Exceptions\CartLineIdMismatchException;

try {
    $cart->remove($cartLineId);
} catch (CartLineIdMismatchException $e) {
    // The cart line ID does not belong to this cart
}
```

### DisallowMultipleCartOrdersException

```php theme={null}
Lunar\Exceptions\DisallowMultipleCartOrdersException
```

Thrown when calling `createOrder()` on a cart that already has a completed order, unless `allowMultipleOrders: true` is passed.

```php theme={null}
use Lunar\Exceptions\DisallowMultipleCartOrdersException;

try {
    $order = $cart->createOrder();
} catch (DisallowMultipleCartOrdersException $e) {
    // Cart already has a completed order
}
```

### FingerprintMismatchException

```php theme={null}
Lunar\Exceptions\FingerprintMismatchException
```

Thrown when the fingerprint passed to `checkFingerprint()` does not match the cart's current fingerprint. See [Detecting Cart Changes](#detecting-cart-changes) for usage details.

## Calculating Totals

Call `calculate()` to hydrate the cart with computed prices and tax breakdowns.

```php theme={null}
$cart->calculate();
```

<Tip>
  All monetary values return a `Lunar\DataTypes\Price` object, providing access to `value`, `formatted`, and `decimal` properties.
</Tip>

To force recalculation (bypassing the cached result), use `recalculate`:

```php theme={null}
$cart->recalculate();
```

To check whether a cart has already been calculated:

```php theme={null}
if ($cart->isCalculated()) {
    // Totals are available
}
```

### Cart-Level Properties

| Property             | Description                                    |
| :------------------- | :--------------------------------------------- |
| `total`              | The total price for the cart (including tax).  |
| `subTotal`           | The cart sub total, excluding tax.             |
| `subTotalDiscounted` | The cart sub total, minus the discount amount. |
| `taxTotal`           | The total tax applied.                         |
| `discountTotal`      | The total discount applied.                    |
| `shippingSubTotal`   | The shipping total, excluding tax.             |
| `shippingTaxTotal`   | The tax amount applied to shipping.            |
| `shippingTotal`      | The shipping total, including tax.             |

### Line-Level Properties

After calculation, each cart line is also hydrated with its own totals:

| Property               | Description                                        |
| :--------------------- | :------------------------------------------------- |
| `unitPrice`            | Price for a single item.                           |
| `unitPriceInclTax`     | Price for a single item, including tax.            |
| `total`                | Total price for this line.                         |
| `subTotal`             | Sub total, excluding tax.                          |
| `subTotalDiscounted`   | Sub total, minus the discount amount.              |
| `taxAmount`            | Tax applied to this line.                          |
| `taxBreakdown`         | Collection of all taxes applied to this line.      |
| `discountTotal`        | Discount applied to this line.                     |
| `promotionDescription` | Description of the promotion applied to this line. |

```php theme={null}
foreach ($cart->lines as $cartLine) {
    $cartLine->unitPrice;
    $cartLine->subTotal;
    $cartLine->taxAmount;
    // ...
}
```

### Breakdowns

The cart also provides detailed breakdowns for tax, discounts, and shipping:

```php theme={null}
// Tax breakdown
foreach ($cart->taxBreakdown->amounts as $taxRate) {
    $taxRate->description;
    $taxRate->price->value;
}

// Discount breakdown
foreach ($cart->discountBreakdown as $discountBreakdown) {
    $discountBreakdown->discount->id;
    $discountBreakdown->price->value;

    foreach ($discountBreakdown->lines as $discountLine) {
        $discountLine->quantity;
        $discountLine->line;
    }
}

// Shipping breakdown
foreach ($cart->shippingBreakdown->items as $shippingBreakdown) {
    $shippingBreakdown->name;
    $shippingBreakdown->identifier;
    $shippingBreakdown->price->formatted();
}
```

### Extending Cart Calculations

To programmatically change cart values (e.g. custom discounts or prices), see [Cart Extending](/1.x/extending/carts).

## Cart Addresses

Each cart can have a shipping and billing address, represented by the `CartAddress` model. These addresses are used when calculating tax breakdowns and shipping costs.

```php theme={null}
Lunar\Models\CartAddress
```

| Field                  | Type                   | Description                                            |
| :--------------------- | :--------------------- | :----------------------------------------------------- |
| id                     | `bigIncrements`        | Primary key                                            |
| cart\_id               | `foreignId`            |                                                        |
| country\_id            | `foreignId` `nullable` |                                                        |
| title                  | `string` `nullable`    |                                                        |
| first\_name            | `string` `nullable`    |                                                        |
| last\_name             | `string` `nullable`    |                                                        |
| company\_name          | `string` `nullable`    |                                                        |
| tax\_identifier        | `string` `nullable`    | Tax ID or VAT number                                   |
| line\_one              | `string` `nullable`    |                                                        |
| line\_two              | `string` `nullable`    |                                                        |
| line\_three            | `string` `nullable`    |                                                        |
| city                   | `string` `nullable`    |                                                        |
| state                  | `string` `nullable`    |                                                        |
| postcode               | `string` `nullable`    |                                                        |
| delivery\_instructions | `string` `nullable`    |                                                        |
| contact\_email         | `string` `nullable`    |                                                        |
| contact\_phone         | `string` `nullable`    |                                                        |
| type                   | `string`               | Either `shipping` or `billing`. Defaults to `shipping` |
| shipping\_option       | `string` `nullable`    | The selected shipping option identifier                |
| meta                   | `json` `nullable`      |                                                        |
| created\_at            | `timestamp`            |                                                        |
| updated\_at            | `timestamp`            |                                                        |

### Relationships

| Relationship | Type       | Related Model | Description                       |
| :----------- | :--------- | :------------ | :-------------------------------- |
| `cart`       | Belongs to | `Cart`        | The cart this address belongs to. |
| `country`    | Belongs to | `Country`     | The country for this address.     |

### Cached Properties

After the cart is calculated, shipping addresses are hydrated with shipping-related totals:

| Property           | Description                                 |
| :----------------- | :------------------------------------------ |
| `shippingOption`   | The resolved `ShippingOption` data type.    |
| `shippingSubTotal` | The shipping sub total, excluding tax.      |
| `shippingTaxTotal` | The tax amount applied to shipping.         |
| `shippingTotal`    | The shipping total, including tax.          |
| `taxBreakdown`     | Breakdown of all taxes applied to shipping. |

## Setting Addresses

Shipping and billing addresses can be set on the cart, which are used when calculating tax breakdowns.

```php theme={null}
$cart->setShippingAddress([
    'first_name' => null,
    'last_name' => null,
    'line_one' => null,
    'line_two' => null,
    'line_three' => null,
    'city' => null,
    'state' => null,
    'postcode' => null,
    'country_id' => null,
]);

$cart->setBillingAddress([
    'first_name' => null,
    'last_name' => null,
    'line_one' => null,
    'line_two' => null,
    'line_three' => null,
    'city' => null,
    'state' => null,
    'postcode' => null,
    'country_id' => null,
]);
```

An `Address` model or a `CartAddress` model can also be passed:

```php theme={null}
use Lunar\Models\Address;

$shippingAddress = Address::first();

$cart->setShippingAddress($shippingAddress);

// Use the same address for billing
$cart->setBillingAddress($cart->shippingAddress);
```

Retrieve addresses via the properties:

```php theme={null}
$cart->shippingAddress;
$cart->billingAddress;
```

<Info>
  During a cart's early lifetime, address information may not yet be available. Some countries don't display tax until checkout. The address-based tax calculation is designed to handle this: the addresses can be set when they become available.
</Info>

## Shipping Options

A shipping option can be set on the cart after an address has been provided. The available options are determined by the shipping manifest.

```php theme={null}
$cart->setShippingOption($shippingOption);
```

To retrieve the currently selected shipping option:

```php theme={null}
$shippingOption = $cart->getShippingOption();
```

To check whether the cart contains any shippable items:

```php theme={null}
if ($cart->isShippable()) {
    // Cart has items that require shipping
}
```

## Shipping Estimates

It may be useful to show an estimated shipping cost before a full address is provided. The `getEstimatedShipping` method returns the cheapest available shipping option based on partial address data.

```php theme={null}
use Lunar\Models\Country;

$shippingOption = $cart->getEstimatedShipping([
    'postcode' => '123456',
    'state' => 'Essex',
    'country' => Country::first(),
]);
```

By default, this estimate is **not** used in cart total calculations. To include it, pass `setOverride: true`:

```php theme={null}
use Lunar\Models\Country;

$shippingOption = $cart->getEstimatedShipping([
    'postcode' => '123456',
    'state' => 'Essex',
    'country' => Country::first(),
], setOverride: true);
```

<Info>
  When `setOverride` is enabled, the returned shipping option bypasses other shipping logic in the cart pipelines, but only for that single request.
</Info>

The override can also be set manually:

```php theme={null}
use Lunar\DataTypes\ShippingOption;

$cart->shippingOptionOverride = new ShippingOption(/* .. */);
```

## Creating Orders

Once a cart has been calculated and all required information (addresses, shipping, etc.) has been provided, an order can be created from the cart.

```php theme={null}
$order = $cart->createOrder();
```

By default, a cart can only have one order. To allow multiple orders from the same cart (e.g. for split shipments), pass `allowMultipleOrders`:

```php theme={null}
$order = $cart->createOrder(allowMultipleOrders: true);
```

To update an existing order instead of creating a new one, pass the order ID:

```php theme={null}
$order = $cart->createOrder(orderIdToUpdate: $existingOrderId);
```

### Checking Order Readiness

Before attempting to create an order, check whether the cart has sufficient information:

```php theme={null}
if ($cart->canCreateOrder()) {
    $order = $cart->createOrder();
}
```

To check whether the cart already has completed (placed) orders:

```php theme={null}
$cart->hasCompletedOrders(); // bool
```

To retrieve the current draft order (matching the cart's fingerprint and total):

```php theme={null}
$draftOrder = $cart->currentDraftOrder();
```

### Stock Validation

Stock levels can be validated before order creation. This checks each cart line against the available stock for its purchasable item.

```php theme={null}
use Lunar\Exceptions\Carts\CartException;

try {
    $cart->validateStock();
} catch (CartException $e) {
    // One or more items are out of stock
}
```

## Associating Users and Customers

A user can be associated directly on the cart model. The `policy` parameter controls how the association behaves when the user already has an existing cart: `merge` combines the carts, while `override` replaces the existing one.

```php theme={null}
$cart->associate($user, policy: 'merge');
```

A customer can also be associated:

```php theme={null}
$cart->setCustomer($customer);
```

## Cart Session Manager

<Tip>
  The cart session manager is useful when building a traditional Laravel storefront that uses sessions.
</Tip>

The session manager provides a convenient API for managing carts tied to the current user's session.

### Configuration

Cart configuration lives in `config/lunar/cart.php`:

| Option        | Description                                                          | Default   |
| :------------ | :------------------------------------------------------------------- | :-------- |
| `auth_policy` | How to handle merging when a user logs in (`merge` or `override`).   | `merge`   |
| `eager_load`  | Relationships to eager load by default when calculating cart totals. | See below |

The default `eager_load` relationships are:

```php theme={null}
'eager_load' => [
    'currency',
    'lines.purchasable.taxClass',
    'lines.purchasable.values',
    'lines.purchasable.product.thumbnail',
    'lines.purchasable.prices.currency',
    'lines.purchasable.prices.priceable',
    'lines.purchasable.product',
    'lines.cart.currency',
],
```

Additional session-specific config is in `config/lunar/cart_session.php`:

| Option                           | Description                                                 | Default      |
| :------------------------------- | :---------------------------------------------------------- | :----------- |
| `session_key`                    | Key used when storing the cart ID in the session.           | `lunar_cart` |
| `auto_create`                    | Create a cart automatically if none exists for the session. | `false`      |
| `allow_multiple_orders_per_cart` | Whether carts can have multiple orders associated to them.  | `false`      |

### Getting the Session Instance

Use the facade or inject the interface:

```php theme={null}
use Lunar\Facades\CartSession;

$cart = CartSession::current();

// Or via dependency injection
use Lunar\Base\CartSessionInterface;

public function __construct(
    protected CartSessionInterface $cartSession
) {
    // ...
}
```

When `current()` is called, the behavior depends on the `auto_create` config. With `auto_create` set to `false` (default), `null` is returned if no cart exists, preventing unnecessary database records.

### Managing Lines

```php theme={null}
use Lunar\Facades\CartSession;
use Lunar\Models\ProductVariant;

// Add a single line
CartSession::add($purchasable, $quantity);

// Add multiple lines (accepts a collection or array)
CartSession::addLines([
    [
        'purchasable' => ProductVariant::find(123),
        'quantity' => 25,
        'meta' => ['foo' => 'bar'],
    ],
    // ...
]);

// Update a single line
CartSession::updateLine($cartLineId, $quantity, $meta);

// Update multiple lines
CartSession::updateLines(collect([
    [
        'id' => 1,
        'quantity' => 25,
        'meta' => ['foo' => 'bar'],
    ],
    // ...
]));

// Remove a line
CartSession::remove($cartLineId);

// Remove all lines
CartSession::clear();
```

### Using a Specific Cart

```php theme={null}
use Lunar\Facades\CartSession;
use Lunar\Models\Cart;

$cart = Cart::first();
CartSession::use($cart);
```

### Associating a User

```php theme={null}
use Lunar\Facades\CartSession;

// Associate a cart with a user and set it as the session cart
// The third argument is the auth policy: 'merge' or 'override'
CartSession::associate($cart, $user, 'merge');
```

### Associating a Customer

A customer can be associated directly on the cart model:

```php theme={null}
$cart->setCustomer($customer);
```

### Forgetting the Cart

Forgetting a cart removes it from the session and soft-deletes it from the database:

```php theme={null}
use Lunar\Facades\CartSession;

CartSession::forget();
```

To remove it from the session without deleting:

```php theme={null}
use Lunar\Facades\CartSession;

CartSession::forget(delete: false);
```

### Shipping Estimates

When using the `CartSession` manager, shipping estimation parameters can be persisted so they do not need to be passed each time:

```php theme={null}
use Lunar\Facades\CartSession;
use Lunar\Models\Country;

CartSession::estimateShippingUsing([
    'postcode' => '123456',
    'state' => 'Essex',
    'country' => Country::first(),
]);

// Default behavior: no shipping estimate applied
CartSession::current();

// Apply the shipping estimate set above
CartSession::current(estimateShipping: true);
```

See [Shipping Estimates](#shipping-estimates) above for more on how the underlying estimation works.

## Handling User Login

When a user logs in, Lunar automatically listens to authentication events and handles cart association. If the user had a guest cart, it will be merged with (or override) any existing cart on their account, depending on the `auth_policy` config.

## Detecting Cart Changes

Carts are dynamic: items, quantities, and prices can change at any moment. To detect whether a cart has been modified (e.g. on a different browser tab during checkout), use the fingerprint system:

```php theme={null}
use Lunar\Exceptions\FingerprintMismatchException;

$fingerprint = $cart->fingerprint();

try {
    $cart->checkFingerprint('previously_stored_fingerprint');
} catch (FingerprintMismatchException $e) {
    // Cart has changed, refresh it
}
```

The fingerprint generator class can be customized in `config/lunar/cart.php`:

```php theme={null}
return [
    // ...
    'fingerprint_generator' => Lunar\Actions\Carts\GenerateFingerprint::class,
];
```

## Pruning Old Carts

Over time, unused carts accumulate in the database. Lunar can automatically prune carts that have no associated order.

Enable pruning in `config/lunar/cart.php`:

```php theme={null}
return [
    // ...
    'prune_tables' => [
        'enabled' => false, // Set to true to enable

        'pipelines' => [
            Lunar\Pipelines\CartPrune\PruneAfter::class,
            Lunar\Pipelines\CartPrune\WithoutOrders::class,
            Lunar\Pipelines\CartPrune\WhereNotMerged::class,
        ],

        'prune_interval' => 90, // days
    ],
];
```

| Option           | Description                                                           | Default |
| :--------------- | :-------------------------------------------------------------------- | :------ |
| `enabled`        | Whether automatic pruning is active.                                  | `false` |
| `prune_interval` | Number of days to retain carts before pruning.                        | `90`    |
| `pipelines`      | Pipeline classes that determine which carts are eligible for removal. |         |

## Activity Logging

The Cart model uses [Spatie Activity Log](https://spatie.be/docs/laravel-activitylog) to automatically record changes. All attribute changes are logged except for `updated_at`.

## Macros

The Cart model supports macros, allowing custom methods to be added at runtime:

```php theme={null}
use Lunar\Models\Cart;

Cart::macro('myCustomMethod', function () {
    // ...
});

$cart->myCustomMethod();
```
