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

# Upgrade Guide

> Instructions for upgrading between Lunar versions, including breaking changes and migration steps.

This page covers the steps required to upgrade between Lunar releases, along with any breaking changes or manual actions needed for each version.

## General Upgrade Instructions

<Warning>
  Before upgrading, back up the database and commit (or stash) any uncommitted changes. This makes it easy to roll back if something goes wrong.
</Warning>

Update the Lunar package to the latest version:

```sh theme={null}
composer update lunarphp/lunar
```

Run any new migrations:

```sh theme={null}
php artisan migrate
```

After migrating, clear cached config and views to ensure the application picks up any changes:

```sh theme={null}
php artisan optimize:clear
```

<Tip>
  Pin Lunar to a specific minor version in `composer.json` (e.g., `"lunarphp/lunar": "^1.4"`) to avoid unexpected breaking changes when running `composer update`.
</Tip>

Review the version-specific notes below for any additional steps required by the target release.

***

## 1.5

<Warning>
  **Breaking changes ahead.** Lunar 1.5 raises the minimum PHP and Laravel versions, upgrades the admin panel to Filament v4, and changes a number of database columns. Every section below should be reviewed before upgrading; skipping steps will leave the application in a broken state.
</Warning>

<Info>
  **Filament v4 only.** Lunar 1.5 upgrades the admin panel to Filament v4 and drops support for Filament v3. Projects that need to stay on Filament v3 should remain on the [1.4 release line](#14), which is the final Lunar version built on Filament v3.
</Info>

<Tip>
  Test the upgrade on a staging environment before applying it to production. Filament v4 and the Filament v3 to v4 migration account for most of the work involved.
</Tip>

### Summary of breaking changes

* **PHP 8.3+ required.** PHP 8.2 is no longer supported.
* **Laravel 12 or 13 required.** Laravel 11 is no longer supported.
* **Admin panel upgraded to Filament v4.** Filament v3 is no longer supported. Custom resources, pages, widgets, and actions must be migrated to the new APIs.
* **Staff two-factor columns renamed.** `two_factor_secret` → `app_authentication_secret`, `two_factor_recovery_codes` → `app_authentication_recovery_codes`, and `two_factor_confirmed_at` removed.
* **Nested set package replaced.** `kalnoy/nestedset` is replaced by `lunarphp/nestedset`, which changes the trait and query builder namespaces used by `Lunar\Models\Collection`.
* **Order line purchasable morph is now nullable.** Shipping lines no longer store a placeholder morph, so `$orderLine->purchasable` returns `null` for them.
* **Shipping method `cutoff` column removed.** Method availability is now controlled by an availability schedule.

Each item is covered in detail below.

### High Impact

#### PHP and Laravel requirements

Lunar 1.5 raises the minimum supported versions:

* PHP 8.3 or later (PHP 8.2 is no longer supported).
* Laravel 12 or Laravel 13 (Laravel 11 is no longer supported, as it has reached end of life).

Upgrade the application's `composer.json` accordingly before bumping Lunar:

```diff theme={null}
- "php": "^8.2",
+ "php": "^8.3",
- "laravel/framework": "^11.0",
+ "laravel/framework": "^12.0|^13.0",
```

#### Filament v4

The admin panel has been upgraded from Filament v3 to Filament v4. Projects that extend the panel with custom resources, pages, widgets, or actions will need to follow the official [Filament v4 upgrade guide](https://filamentphp.com/docs/4.x/upgrade-guide) and adapt any extension code to the new APIs.

Update the constraint in `composer.json`:

```diff theme={null}
- "filament/filament": "^3.0",
+ "filament/filament": "^4.1",
```

Common areas that require changes:

* Form and table builders now use the dedicated `Filament\Schemas\Schema` and `Filament\Tables\Table` signatures.
* Actions have moved to the `Filament\Actions` namespace (for example, `EditAction`, `DeleteBulkAction`, and `BulkActionGroup`).
* Form components such as `Section`, `Group`, and `Get` now live under `Filament\Schemas\Components`.

Run the Filament upgrade tooling to assist with the migration:

```sh theme={null}
composer require filament/upgrade:^4.0 -W --dev
vendor/bin/filament-v4
```

After upgrading, run migrations to apply the staff table column rename described below:

```sh theme={null}
php artisan migrate
```

#### Two-factor staff columns renamed

Filament v4 ships its own multi-factor authentication primitives. As part of the upgrade, the columns on the `staff` table have been renamed and the redundant confirmation timestamp removed:

* `two_factor_secret` is renamed to `app_authentication_secret`.
* `two_factor_recovery_codes` is renamed to `app_authentication_recovery_codes`.
* `two_factor_confirmed_at` is dropped.

The rename is handled automatically by a Lunar migration. Update any application code that references the old column names directly.

The previous `lunarphp/filament3-2fa` package is no longer required and can be removed from `composer.json`.

#### Nested set package replaced

Collections build their tree using a nested set implementation. Lunar 1.5 switches from `kalnoy/nestedset` to `lunarphp/nestedset`, a Lunar-maintained fork kept current for Laravel 12 and 13. Composer resolves the new package as a Lunar dependency, so no manual `require` is needed, but the namespaces have changed:

```diff theme={null}
- use Kalnoy\Nestedset\NodeTrait;
- use Kalnoy\Nestedset\QueryBuilder;
- use Kalnoy\Nestedset\NestedSetServiceProvider;
+ use Lunar\Nestedset\NodeTrait;
+ use Lunar\Nestedset\QueryBuilder;
+ use Lunar\Nestedset\NestedSetServiceProvider;
```

Update any application code that imports these classes directly, type-hints against them, or extends `Lunar\Models\Collection` and re-declares a nested set method. The API itself is unchanged, so tree queries such as `whereIsRoot()`, `appendNode()`, and `withDepth()` continue to work as before. See [Collections → Nested Collections](/1.x/reference/collections#nested-collections).

If the application requires `kalnoy/nestedset` for its own models, it can stay in `composer.json` alongside the fork.

### Medium Impact

#### Order line purchasable morph is now nullable

Shipping order lines previously stored a placeholder morph (`purchasable_type` set to `Lunar\DataTypes\ShippingOption`, `purchasable_id` set to `1`) purely to satisfy the non-nullable morph columns. `ShippingOption` is a data transfer object rather than an Eloquent model, so reading `$orderLine->purchasable` on a shipping line raised a fatal error, as did eager loading `lines.purchasable` across an order.

In 1.5, `order_lines.purchasable_type` and `order_lines.purchasable_id` are nullable, and shipping lines are stored with no morph at all. A migration nulls the placeholder morph on existing shipping lines. The migration is scoped to that exact class, so custom morphs stored on shipping-type lines are left untouched.

```php theme={null}
$order->shippingLines->first()->purchasable; // null (previously a fatal error)
```

Shipping lines are self-describing: `description`, `unit_price`, `total`, and `meta` are snapshotted onto the line, so display and totals are unaffected. Storefront code that iterates `$order->lines` and reaches for `$line->purchasable` should guard against `null`:

```php theme={null}
foreach ($order->lines as $line) {
    if (! $line->purchasable) {
        continue; // Shipping line
    }

    // ...
}
```

Alternatively, iterate `$order->productLines` to exclude shipping lines entirely. See [Orders → Order Lines](/1.x/reference/orders#order-lines).

#### Discount conditions are now honored by BuyXGetY

`Lunar\DiscountTypes\BuyXGetY` did not check the discount's conditions before awarding a reward. Minimum spend, customer restrictions, and `max_uses_per_user` were all ignored, and only the quantity condition (`min_qty`) gated the discount.

These conditions are now enforced, matching `Lunar\DiscountTypes\AmountOff`. Existing BuyXGetY discounts that were configured with a minimum spend or a customer restriction will start applying it, so a discount that previously awarded a reward on every qualifying cart may now award it on fewer carts. Review live BuyXGetY discounts before upgrading to confirm their conditions reflect what the store intends to offer.

#### Stripe amounts converted using currency decimal places

`Lunar\Stripe\Managers\StripeManager::toStripeAmount()` previously passed the stored cart total straight through to Stripe for all but three currencies, assuming the currency's configured `decimal_places` matched the sub-unit Stripe expects. A currency configured with more decimal places than Stripe uses was overcharged, by a factor of 100 in the case of a four decimal place currency.

Amounts are now converted back to the major unit using the currency's `decimal_places`, then re-scaled to the sub-unit Stripe expects for that currency code (zero decimal places for JPY, KRW, and similar, three for BHD, JOD, KWD, OMR, and TND, two otherwise).

Stores using the Stripe add-on with a currency whose `decimal_places` is not `2` should verify the amounts sent to Stripe after upgrading. No action is needed for currencies configured with two decimal places. See [Stripe](/1.x/addons/payments/stripe).

#### Weight tiers evaluated in the shipping method's weight unit

The `ship-by` driver hardcoded kilograms when summing cart weight, ignoring the `weight_unit` column on the shipping method. Each cart line's weight is now converted from its own unit into the method's configured `weight_unit`, defaulting to `kg` when the column is empty.

Methods that left `weight_unit` empty behave exactly as before. Methods that set a `weight_unit` other than `kg` will now match tiers in that unit, so their tier thresholds need to be re-entered in the configured unit. Weight tiers remain whole numbers; the admin panel now shows the configured unit alongside the field and rejects decimal values instead of silently truncating them. See [Table Rate Shipping → Ship By](/1.x/addons/table-rate-shipping#ship-by).

#### Optimized default search indexer relationship select

The default search indexers now eager-load only the columns each indexer uses in `toSearchableArray()`. Search output is unchanged, so no action is required when using the default indexers.

Projects with custom indexers that extend Lunar's indexers should review their `makeAllSearchableUsing()` method to confirm the required columns are still selected.

* Indexers updated: `CustomerIndexer`, `OrderIndexer`, `ProductIndexer`, `ProductOptionIndexer`.
* Indexers unchanged: `BrandIndexer`, `CollectionIndexer`.

#### Shipping method cutoff replaced by availability schedule

The `cutoff` column on `shipping_methods` has been removed. Method availability is now controlled through a dedicated availability schedule, offering finer control over when each shipping method is offered. A migration drops the column and Lunar's migration state copies any existing cutoff value into a schedule entry.

Update any application code that reads or writes `cutoff` directly on `Lunar\Shipping\Models\ShippingMethod`. See [Table Rate Shipping](/1.x/addons/table-rate-shipping) for the new model.

### Low Impact

#### Min and max weight on shipping methods

Shipping methods now support optional `min_weight`, `max_weight`, and `weight_unit` fields. A migration adds the columns as nullable, so existing methods continue to work without changes.

#### `card_type` on transactions is now nullable

The `card_type` column on the `transactions` table is now nullable and stored as `string` (no length constraint). Existing rows are unaffected. Custom code that assumed a non-null value should be updated.

#### Per-currency base price for table rate shipping

Table rate shipping rates can now hold a base price per currency, not just the default currency. No schema change is required; the admin panel exposes the additional inputs.

#### New `ShippingDiscount` discount type

The Table Rate Shipping add-on registers a new `ShippingDiscount` discount type, allowing discounts to target specific shipping methods (or all methods) with a fixed price per currency or a percentage off. See [Table Rate Shipping](/1.x/addons/table-rate-shipping) for usage.

#### Date parameters widened to `DateTimeInterface`

Applications that call `Date::use(CarbonImmutable::class)` receive a `CarbonImmutable` from `now()`, which is not a `DateTime` subclass. Any cart or discount calculation then failed with a `TypeError`, because these methods declared `DateTime` parameters.

The parameter type hints are now `DateTimeInterface` across three traits:

| Trait | Methods |
| :- | :- |
| `Lunar\Base\Traits\HasCustomerGroups` | `scheduleCustomerGroup()`, `applyCustomerGroupScope()`, `scopeCustomerGroup()` |
| `Lunar\Base\Traits\HasChannels` | `scheduleChannel()`, `scopeChannel()` |
| `Lunar\Base\Traits\CanScheduleAvailability` | `schedule()` |

Widening a parameter type is backwards compatible, so all existing call sites continue to work. The one exception is a custom model that overrides one of these methods and re-declares the narrower `DateTime` type: PHP raises a fatal error when the class loads. Widen the override's signature to `DateTimeInterface` to match.

#### `getDefault()` can return `null`

`Lunar\Base\Traits\HasDefaultRecord::getDefault()` was annotated as returning a model, but it returns `null` when no record is flagged as the default. The annotation now reflects that.

There is no runtime change, but static analysis will start reporting calls that assume a model is always returned, such as `Currency::getDefault()->code`. Guard these calls or seed a default record:

```php theme={null}
if ($currency = \Lunar\Models\Currency::getDefault()) {
    // ...
}
```

#### `DefaultPriceFormatter` formatter style type

The `$formatterStyle` parameter on `Lunar\Pricing\DefaultPriceFormatter` is now typed `int` rather than `string`, matching the `NumberFormatter` constants (such as `NumberFormatter::CURRENCY`) that are passed to it. Custom price formatters that extend this class should update their signature to match.

#### Deterministic relationship ordering

Several relationships had no explicit ordering, so the row order was left to the database engine. MySQL happens to return primary key order; PostgreSQL returns heap order, which can change after an unrelated update. Because `Lunar\Actions\Carts\GenerateFingerprint` reduces cart lines in iteration order, an unstable order could change a cart's fingerprint.

Explicit ordering has been added:

| Relationship | Ordering |
| :- | :- |
| `Lunar\Models\Cart::lines()` | `id` |
| `Lunar\Models\Order::lines()` | `id` |
| `Lunar\Models\ProductVariant::values()` | `position`, then the pivot `id` |

Ordering `Order::lines()` also stabilizes the derived `physicalLines`, `digitalLines`, `shippingLines`, and `productLines` relationships. Applications that relied on the previous incidental ordering may see a different (but now consistent) line order.

#### Order addresses are no longer duplicated by type

`Lunar\Pipelines\Order\Creation\CreateOrderAddresses` matched existing order addresses on both type and postcode, so a cart whose draft order was rebuilt with a changed address added a second row of the same type rather than updating the first. Because `$order->shippingAddress` returns the oldest match, the order then reported the stale address.

Existing addresses are now matched on type alone, so each order holds at most one address per type and rebuilding the draft order updates it in place. No migration is included, so orders that already hold duplicate addresses keep them, and the extra rows can be cleaned up manually if needed.

#### State tax zones are scoped to the address country

A tax zone attached to a state matched any address whose state code or name matched, in any country. Because state codes collide across countries (`WA` is both Washington and Western Australia), the wrong zone could apply, and a wrong state match also suppressed the correct country zone.

State lookups are now scoped to the address country, so a state zone applies only to addresses in that state's country. Stores that unintentionally relied on a cross-country match will see the lookup fall through to the country zone, then the default zone. See [Taxation → Zone resolution](/1.x/reference/taxation#zone-resolution).

#### Cart repriced when the session currency changes

`Lunar\Facades\CartSession::setCurrency()` updated the cart's `currency_id` but left the loaded relationships in place, so the next `calculate()` priced every line in the previous currency. The cart's loaded `currency` and `lines` relationships are now reset when the currency changes, so pricing resolves against the new currency's `Price` records. See [Carts → Setting the currency](/1.x/reference/carts#setting-the-currency).

***

## 1.4

Lunar 1.4 is the final minor version built on Filament v3. [Lunar 1.5](#15) upgrades the admin panel to Filament v4, so projects that need to remain on Filament v3 should stay on the 1.4 release line.

### Low Impact

#### `shipping:manage` permission

The Table Rate Shipping add-on now enforces a `shipping:manage` permission on its admin resources (Shipping Methods, Shipping Zones, and Shipping Exclusion Lists). A migration creates the permission automatically; staff who previously had unrestricted access need to be assigned the new permission (or a role that includes it) to continue managing shipping.

See [Table Rate Shipping](/1.x/addons/table-rate-shipping) for details.

***

## 1.3

### Low Impact

The `stephenjude/filament-two-factor-authentication` package has been removed due to an issue with Fortify and no suitable release being tagged. Replace the package with the forked version provided by Lunar:

```diff theme={null}
- "stephenjude/filament-two-factor-authentication": "^2.0",
+ "lunarphp/filament3-2fa": "^2.1.0",
```

***

## 1.2

### Low Impact

#### Product association types ENUM

Product association types have been moved to a dedicated ENUM. The model constants are now deprecated. The ENUM class can be swapped in config to allow for extending.

#### Added meta fields to product options

The `ProductOption` and `ProductOptionValue` models now include a `meta` field. If the application already defines custom `meta` fields on these models, a migration conflict may occur.

***

## 1.1

### Medium Impact

#### Renamed Order resource extension hook

There was a typo in the extension hook. Rename `exendOrderSummaryInfolist` to `extendOrderSummaryInfolist` in any code that references it.

***

## 1.0.0 (stable release)

This release introduces anonymous usage insights, sent via a deferred API call. The purpose of this addition is to provide insight into how Lunar is being used and at what capacity. No identifying information is sent or stored.

This is completely optional; however, it is turned on by default. To opt out, add the following to a service provider:

```php theme={null}
\Lunar\Facades\Telemetry::optOut();
```

***

## 1.0.0-beta.24

### Medium Impact

#### Customer `vat_no` field renamed

The field on the `customers` table has been renamed to `tax_identifier`. This aligns with the new field of the same name on `addresses`, `cart_addresses`, and `order_addresses`.

### Low Impact

#### Buy X Get Y discount conditions and rewards

Buy X Get Y discounts can now use collections and variants as conditions, and variants as rewards. As part of this change, the `discount_purchasables` table has been renamed to `discountables` and has its own `Discountable` model. Any code referencing `discount_purchasables` directly, or the `purchasables` relation on the discount model, must be updated.

***

## 1.0.0-beta.22

### High Impact

<Warning>
  This release removes Laravel 10 support. Projects must be upgraded to Laravel 11 or later before updating Lunar. [Laravel Shift](https://laravelshift.com/) can assist with this process.
</Warning>

#### Lunar Panel discount interface

The `LunarPanelDiscountInterface` now requires a `lunarPanelRelationManagers` method that returns an array of relation managers to show in the admin panel for the discount type. Update any custom discount types to include this method.

***

## 1.0.0-beta.21

### High Impact

#### Order reference generation changes

The previous order reference generator used the format `YYYY-MM-{X}`, which had been in place since the early days of the project. This approach was not ideal for order references and could lead to anomalies when determining the next reference in the sequence.

The new format uses the Order ID with leading zeros and an optional prefix:

```
// Old (assuming order #250 in April 2025)
2025-04-00250

// New (assuming order ID is 1965)
00001965
```

The length of the reference and the prefix can be defined in the `lunar/orders.php` config file:

```php theme={null}
'reference_format' => [
    /**
     * Optional prefix for the order reference
     */
    'prefix' => null,

    /**
     * STR_PAD_LEFT: 00001965
     * STR_PAD_RIGHT: 19650000
     * STR_PAD_BOTH: 00196500
     */
    'padding_direction' => STR_PAD_LEFT,

    /**
     * 00001965
     * AAAA1965
     */
    'padding_character' => '0',

    /**
     * If the length specified below is smaller than the length
     * of the Order ID, then no padding will take place.
     */
    'length' => 8,
],
```

To keep using the previous reference generation logic, [copy the existing class](https://github.com/lunarphp/lunar/blob/1.0.0-beta20/packages/core/src/Base/OrderReferenceGenerator.php) into the application and update the `reference_generator` path in config.

### Medium Impact

#### Two-factor authentication

Staff members now have the ability to set up two-factor authentication (2FA). Currently this is opt-in; however, it can be enforced for all staff members:

```php theme={null}
public function register()
{
    \Lunar\Admin\Support\Facades\LunarPanel::enforceTwoFactorAuth()->register();
}
```

To disable 2FA entirely (the setup option will not appear):

```php theme={null}
public function register()
{
    \Lunar\Admin\Support\Facades\LunarPanel::disableTwoFactorAuth()->register();
}
```

***

## 1.0.0-beta.1

### High Impact

#### Model extending

<Warning>
  Model extending has been completely rewritten and requires changes to any Laravel application that has previously extended Lunar models.
</Warning>

Lunar models now implement a contract (interface) and support dependency injection across the storefront and the Lunar panel.

Update how models are registered:

```php theme={null}
// Old
ModelManifest::register(collect([
    Product::class => \App\Models\Product::class,
    // ...
]));

// New
ModelManifest::replace(
    \Lunar\Models\Contracts\Product::class,
    \App\Models\Product::class
);
```

<Tip>
  If custom models do not extend their Lunar counterpart, they must implement the relevant contract in `Lunar\Models\Contracts`.
</Tip>

See the [model extending](/1.x/extending/models) section for all available functionality.

#### Polymorphic relationships

To better support model extending, all polymorphic relationships now use an alias instead of the fully qualified class name. This allows relationships to resolve to custom models when interacting with Eloquent.

There is an additional config setting in `config/lunar/database.php` where polymorph mappings can be prefixed:

```php theme={null}
'morph_prefix' => null,
```

By default this is set to `null`, so the mapping for a product would just be `product`.

A migration handles this change for Lunar tables and some third-party tables; however, additional migrations may be needed for other tables or custom models.

#### Shipping methods availability

Shipping methods are now associated with customer groups. When using the shipping add-on, ensure that all shipping methods are associated with the correct customer groups.

#### Stripe add-on

The Stripe add-on now attempts to update an order's billing and shipping address based on what is stored against the Payment Intent. This is due to Stripe not always returning this information during express checkout flows. To disable this behavior, set the `lunar.stripe.sync_addresses` config value to `false`.

##### PaymentIntent storage and reference to carts/orders

Previously, PaymentIntent information was stored in the Cart model's meta and then transferred to the order when created. This approach caused limitations and meant that if the cart's meta was updated elsewhere (or the intent information was removed), it could result in unrecoverable data loss.

PaymentIntent data has been moved from the `payment_intent` key in meta to a dedicated `StripePaymentIntent` model. This allows more flexibility in how payment intents are handled. A `StripePaymentIntent` is associated with both a cart and an order.

The stored information is now:

* `intent_id` — the PaymentIntent ID provided by Stripe
* `status` — the PaymentIntent status
* `event_id` — if the order was placed via the webhook, this contains the event ID
* `processing_at` — populated when a request to place the order is made
* `processed_at` — populated with the current timestamp once the order is placed

##### Preventing overlap

Previously, the job to place the order was dispatched to the queue with a 20-second delay. Now the payment type checks whether the order is already being processed and, if so, takes no further action. This prevents overlaps regardless of how they are triggered.

***

## 1.0.0-alpha.34

### Medium Impact

#### Stripe add-on

The Stripe driver now checks whether an order already has a `placed_at` value. If so, no further processing takes place.

Additionally, the webhook logic has been moved to the job queue with a 20-second dispatch delay. This allows storefronts to manually process a payment intent alongside the webhook without worrying about overlap.

The Stripe webhook environment variable has been renamed:

```diff theme={null}
- STRIPE_WEBHOOK_PAYMENT_INTENT=...
+ LUNAR_STRIPE_WEBHOOK_SECRET=...
```

The Stripe config that Lunar looks for in `config/services.php` has changed:

```php theme={null}
'stripe' => [
    'key' => env('STRIPE_SECRET'),
    'public_key' => env('STRIPE_PK'),
    'webhooks' => [
        'lunar' => env('LUNAR_STRIPE_WEBHOOK_SECRET'),
    ],
],
```

***

## 1.0.0-alpha.32

### High Impact

<Warning>
  A new `LunarUser` interface must be implemented on the application's `User` model.
</Warning>

```php theme={null}
// ...
class User extends Authenticatable implements \Lunar\Base\LunarUser
{
    use \Lunar\Base\Traits\LunarUser;
}
```

***

## 1.0.0-alpha.31

### High Impact

Certain parts of `config/cart.php` that are specific to session-based cart interaction have been relocated to a new `config/cart_session.php` file.

```php theme={null}
// Move to config/cart_session.php
'session_key' => 'lunar_cart',
'auto_create' => false,
```

Check this file for any new config values that may need to be added.

***

## 1.0.0-alpha.29

### High Impact

#### Cart calculate function no longer recalculates

The `$cart->calculate()` method previously ran calculations every time it was called, regardless of whether the cart had already been calculated. Now `calculate()` only runs if cart totals do not exist. To force a recalculation, use `$cart->recalculate()`.

#### Unique index for collection group handle

The `handle` column on collection groups now has a unique index. If collection groups are created through the admin panel, no changes are required.

### Medium Impact

#### Update custom shipping modifiers signature

The `\Lunar\Base\ShippingModifier` `handle` method now correctly passes a closure as the second parameter. Update any custom shipping modifiers that extend this class:

```php theme={null}
public function handle(\Lunar\Models\Cart $cart, \Closure $next)
{
    // ...

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

***

## 1.0.0-alpha.26

### Medium Impact

If custom classes implement the `Purchasable` interface, add the following methods:

```php theme={null}
public function canBeFulfilledAtQuantity(int $quantity): bool;
public function getTotalInventory(): int;
```

If checking the `ProductVariant` `purchasable` attribute, update the following check:

```php theme={null}
// Old
$variant->purchasable == 'backorder';

// New
$variant->purchasable == 'in_stock_or_on_backorder';
```

***

## 1.0.0-alpha.22

### Medium Impact

Carts now use soft deletes. A cart is deleted when `CartSession::forget()` is called. To forget the session without deleting the cart, pass `delete: false`:

```php theme={null}
\Lunar\Facades\CartSession::forget(delete: false);
```

***

## 1.0.0-alpha.20

### High Impact

#### Stripe add-on facade change

The Stripe add-on facade has been renamed:

```php theme={null}
// Old
\Lunar\Stripe\Facades\StripeFacade;

// New
\Lunar\Stripe\Facades\Stripe;
```

***

## 1.0.0-alpha.x

<Warning>
  When upgrading to `1.x` from `0.x`, ensure the application is upgraded to `0.8` first.
</Warning>

### High Impact

#### Change to Staff model namespace

The Staff model has moved from `Lunar\Hub\Models\Staff` to `Lunar\Admin\Models\Staff`. Update all references in the codebase and any polymorphic relations.

#### Spatie Media Library

This package has been upgraded to version 11, which introduces some breaking changes.
See the [Spatie Media Library upgrade guide](https://github.com/spatie/laravel-medialibrary/blob/main/UPGRADING.md) for more information.

#### Media conversions

The `lunar.media.conversions` configuration has been removed in favor of registering custom media definitions instead. Media definition classes allow registration of media collections, conversions, and more. See [Media Collections](/1.x/reference/media.html#media-collections) for further information.

#### Product options

The `position` field has been removed from the `product_options` table and is now found on the `product_product_option` pivot table. Position data is automatically adjusted when running migrations.

#### Tiers renamed to price breaks

The `tier` column on pricing has been renamed to `min_quantity`. Any references in code to `tiers` must be updated.

##### Price model

```php theme={null}
// Old
$priceModel->tier
// New
$priceModel->min_quantity

// Old
$priceModel->tiers
// New
$priceModel->priceBreaks
```

##### Lunar\Base\DataTransferObjects\PricingResponse

```php theme={null}
// Old
public Collection $tiered,
// New
public Collection $priceBreaks,
```

##### Lunar\Base\DataTransferObjects\PaymentAuthorize

Two new properties have been added to the constructor for this DTO:

```php theme={null}
public ?int $orderId = null,
public ?string $paymentType = null
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.