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

# Orders

Orders represent completed or in-progress purchases, created when a cart is converted at checkout.

## Overview

Orders represent completed or in-progress purchases in a store. Orders are linked to carts, and although there is generally only one order per cart, the system supports multiple orders per cart if needed.

<Info>
  All monetary values (such as `sub_total`, `total`, `tax_total`) are cast to `Lunar\DataTypes\Price` objects, providing access to `value`, `formatted()`, and `decimal` properties.
</Info>

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

### Fields

| Field                   | Type                   | Description                                                           |
| :---------------------- | :--------------------- | :-------------------------------------------------------------------- |
| id                      | `id`                   | Primary key                                                           |
| customer\_id            | `foreignId` `nullable` |                                                                       |
| user\_id                | `foreignId` `nullable` | The authenticated user who placed the order                           |
| channel\_id             | `foreignId`            | The channel the order was placed through                              |
| new\_customer           | `boolean`              | Whether the customer is a first-time buyer                            |
| cart\_id                | `foreignId` `nullable` | The cart used to create the order                                     |
| status                  | `string`               | The order status                                                      |
| reference               | `string` `nullable`    | The generated order reference                                         |
| customer\_reference     | `string` `nullable`    | A reference provided by the customer                                  |
| sub\_total              | `unsignedBigInteger`   | The subtotal minus any discounts, excluding tax                       |
| discount\_total         | `unsignedBigInteger`   | The discount amount, excluding tax                                    |
| discount\_breakdown     | `json` `nullable`      | Breakdown of applied discounts                                        |
| shipping\_breakdown     | `json` `nullable`      | Breakdown of shipping charges                                         |
| shipping\_total         | `unsignedBigInteger`   | The shipping total including tax                                      |
| tax\_breakdown          | `json`                 | Breakdown of applied taxes                                            |
| tax\_total              | `unsignedBigInteger`   | The total amount of tax applied                                       |
| total                   | `unsignedBigInteger`   | The grand total including tax                                         |
| notes                   | `text` `nullable`      | Additional order notes                                                |
| currency\_code          | `string`               | The currency code the order was placed in                             |
| compare\_currency\_code | `string` `nullable`    | The default currency code at the time of the order                    |
| exchange\_rate          | `decimal`              | The exchange rate between `currency_code` and `compare_currency_code` |
| placed\_at              | `dateTime` `nullable`  | The datetime the order was considered placed                          |
| fingerprint             | `string` `nullable`    | A hash used to detect cart changes                                    |
| meta                    | `json` `nullable`      | Custom metadata                                                       |
| created\_at             | `timestamp`            |                                                                       |
| updated\_at             | `timestamp`            |                                                                       |

### Relationships

| Relationship      | Type      | Related Model               | Description                                |
| :---------------- | :-------- | :-------------------------- | :----------------------------------------- |
| `channel`         | BelongsTo | `Lunar\Models\Channel`      |                                            |
| `cart`            | BelongsTo | `Lunar\Models\Cart`         |                                            |
| `currency`        | BelongsTo | `Lunar\Models\Currency`     | Matched on `currency_code`                 |
| `customer`        | BelongsTo | `Lunar\Models\Customer`     |                                            |
| `user`            | BelongsTo | User                        | The authenticatable model from auth config |
| `lines`           | HasMany   | `Lunar\Models\OrderLine`    |                                            |
| `physicalLines`   | HasMany   | `Lunar\Models\OrderLine`    | Lines where type is `physical`             |
| `digitalLines`    | HasMany   | `Lunar\Models\OrderLine`    | Lines where type is `digital`              |
| `shippingLines`   | HasMany   | `Lunar\Models\OrderLine`    | Lines where type is `shipping`             |
| `productLines`    | HasMany   | `Lunar\Models\OrderLine`    | All lines excluding shipping               |
| `addresses`       | HasMany   | `Lunar\Models\OrderAddress` |                                            |
| `shippingAddress` | HasOne    | `Lunar\Models\OrderAddress` | Address where type is `shipping`           |
| `billingAddress`  | HasOne    | `Lunar\Models\OrderAddress` | Address where type is `billing`            |
| `transactions`    | HasMany   | `Lunar\Models\Transaction`  |                                            |
| `captures`        | HasMany   | `Lunar\Models\Transaction`  | Transactions where type is `capture`       |
| `intents`         | HasMany   | `Lunar\Models\Transaction`  | Transactions where type is `intent`        |
| `refunds`         | HasMany   | `Lunar\Models\Transaction`  | Transactions where type is `refund`        |

## Creating an Order

An order can be created directly or, the recommended approach, via a `Lunar\Models\Cart` model.

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

$order = Order::create([/** .. */]);

// Recommended approach
$order = Cart::first()->createOrder(
    allowMultipleOrders: false,
    orderIdToUpdate: null,
);
```

* `allowMultipleOrders` - Carts generally only have one draft order associated. Pass `true` to allow multiple orders per cart.
* `orderIdToUpdate` - Optionally pass the ID of an existing draft order to update instead of creating a new one. The order must have a null `placed_at` value and belong to the cart.

The underlying class for creating an order is `Lunar\Actions\Carts\CreateOrder`. This can be overridden in `config/lunar/cart.php`:

```php theme={null}
return [
    //  ...
    'actions' => [
        // ...
        'order_create' => CustomCreateOrder::class,
    ]
];
```

At minimum, a custom class should extend `Lunar\Actions\AbstractAction`:

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

final class CreateOrder extends AbstractAction
{
    public function execute(
        Cart $cart,
        bool $allowMultipleOrders = false,
        ?int $orderIdToUpdate = null
    ): self {
        // ...
        return $this;
    }
}
```

### Validating a Cart Before Creation

To check whether a cart is ready to create an order:

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

This uses the `Lunar\Validation\Cart\ValidateCartForOrderCreation` class, which throws validation exceptions with helpful messages if the cart is not ready.

A custom validation class can be specified in `config/lunar/cart.php`:

```php theme={null}
return [
    // ...
    'validators' => [
        'order_create' => [
            MyCustomValidator::class,
        ],
    ]
];
```

A custom validator should extend `Lunar\Validation\BaseValidator`:

```php theme={null}
use Lunar\Validation\BaseValidator;

class MyCustomValidator extends BaseValidator
{
    public function validate(): bool
    {
        $cart = $this->parameters['cart'];

        if ($somethingWentWrong) {
            return $this->fail('cart', 'There was an issue');
        }

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

## Order Reference Generation

By default, Lunar generates an order reference when creating an order from a cart. The format is:

```
{prefix?}{0..0}{orderId}
```

`{0..0}` indicates the order ID is padded to 8 digits (not including the prefix). The prefix is optional and defined in `config/lunar/orders.php`.

### Custom Generators

To use a custom reference generator, update `config/lunar/orders.php`:

```php theme={null}
return [
    'reference_generator' => App\Generators\MyCustomGenerator::class,
];
```

To disable reference generation entirely (not recommended), set the value to `null`.

A custom generator must implement `Lunar\Base\OrderReferenceGeneratorInterface`:

```php theme={null}
namespace App\Generators;

use Lunar\Base\OrderReferenceGeneratorInterface;
use Lunar\Models\Contracts\Order;

class MyCustomGenerator implements OrderReferenceGeneratorInterface
{
    public function generate(Order $order): string
    {
        // ...
        return 'my-custom-reference';
    }
}
```

## Modifying Orders

To programmatically change order values or add new behavior, the order system can be extended.

See [Order Modifiers](/1.x/extending/orders) for more details.

## Order Status

The `placed_at` field determines whether an order is considered draft or placed. The `Lunar\Models\Order` model provides two helper methods:

```php theme={null}
$order->isDraft();
$order->isPlaced();
```

## Order Lines

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

### Fields

| Field             | Type                   | Description                                             |
| :---------------- | :--------------------- | :------------------------------------------------------ |
| id                | `id`                   | Primary key                                             |
| order\_id         | `foreignId`            |                                                         |
| purchasable\_type | `string`               | Polymorphic type for the purchasable item               |
| purchasable\_id   | `unsignedBigInteger`   | Polymorphic ID for the purchasable item                 |
| type              | `string`               | The line type, e.g. `physical`, `digital`, `shipping`   |
| description       | `string`               | A description of the line item                          |
| option            | `string` `nullable`    | Option information if the item is a variant             |
| identifier        | `string`               | An identifier for the purchasable item, typically a SKU |
| unit\_price       | `unsignedBigInteger`   | The unit price of the line                              |
| unit\_quantity    | `unsignedSmallInteger` | The unit quantity, typically `1`                        |
| quantity          | `unsignedInteger`      | The quantity purchased                                  |
| sub\_total        | `unsignedBigInteger`   | The subtotal minus any discounts, excluding tax         |
| discount\_total   | `unsignedBigInteger`   | The discount amount, excluding tax                      |
| tax\_breakdown    | `json`                 | Breakdown of applied taxes                              |
| tax\_total        | `unsignedBigInteger`   | The total amount of tax applied                         |
| total             | `unsignedBigInteger`   | The grand total including tax                           |
| notes             | `text` `nullable`      | Additional line notes                                   |
| meta              | `json` `nullable`      | Custom metadata                                         |
| created\_at       | `timestamp`            |                                                         |
| updated\_at       | `timestamp`            |                                                         |

### Relationships

| Relationship  | Type          | Related Model           | Description                      |
| :------------ | :------------ | :---------------------- | :------------------------------- |
| `order`       | BelongsTo     | `Lunar\Models\Order`    |                                  |
| `purchasable` | MorphTo       | —                       | The polymorphic purchasable item |
| `currency`    | HasOneThrough | `Lunar\Models\Currency` | Resolved through the order       |

### Creating an Order Line

<Tip>
  When using the `createOrder` method on a cart, order lines are created automatically.
</Tip>

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

OrderLine::create([
    // ...
]);

// Or via the relationship
$order->lines()->create([
    // ...
]);
```

## Order Addresses

An order can have many addresses, typically one for billing and one for shipping.

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

<Tip>
  When using the `createOrder` method on a cart, order addresses are created automatically.
</Tip>

### Fields

| Field                  | Type                   | Description                                          |
| :--------------------- | :--------------------- | :--------------------------------------------------- |
| id                     | `id`                   | Primary key                                          |
| order\_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`    | A tax identification 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`               | The address type: `billing` or `shipping`            |
| shipping\_option       | `string` `nullable`    | A unique identifier for the selected shipping option |
| meta                   | `json` `nullable`      | Custom metadata                                      |
| created\_at            | `timestamp`            |                                                      |
| updated\_at            | `timestamp`            |                                                      |

### Relationships

| Relationship | Type      | Related Model          | Description |
| :----------- | :-------- | :--------------------- | :---------- |
| `order`      | BelongsTo | `Lunar\Models\Order`   |             |
| `country`    | BelongsTo | `Lunar\Models\Country` |             |

### Creating an Order Address

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

OrderAddress::create([
    'order_id' => 1,
    'country_id' => 1,
    'title' => null,
    'first_name' => 'Jacob',
    'last_name' => null,
    'company_name' => null,
    'line_one' => '123 Foo Street',
    'line_two' => null,
    'line_three' => null,
    'city' => 'London',
    'state' => null,
    'postcode' => 'NW1 1WN',
    'delivery_instructions' => null,
    'contact_email' => null,
    'contact_phone' => null,
    'type' => 'shipping',
    'shipping_option' => null,
]);

// Or via the relationship
$order->addresses()->create([
    // ...
]);
```

The shipping and billing addresses can be accessed directly:

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

## Shipping Options

<Tip>
  A Shipping Tables add-on is planned to simplify shipping configuration in the admin panel.
</Tip>

To add shipping options, [extend Lunar](/1.x/extending/shipping) with custom logic.

Shipping options can be fetched using the `ShippingManifest` facade:

```php theme={null}
\Lunar\Facades\ShippingManifest::getOptions(\Lunar\Models\Cart $cart);
```

This returns a collection of `Lunar\DataTypes\ShippingOption` objects.

### Adding a Shipping Option to the Cart

Once a shipping option has been selected, add it to the cart so totals can be recalculated:

```php theme={null}
$cart->setShippingOption(\Lunar\DataTypes\ShippingOption $option);
```

## Transactions

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

### Fields

| Field                   | Type                   | Description                                            |
| :---------------------- | :--------------------- | :----------------------------------------------------- |
| id                      | `id`                   | Primary key                                            |
| parent\_transaction\_id | `foreignId` `nullable` | A reference to a parent transaction                    |
| order\_id               | `foreignId`            |                                                        |
| success                 | `boolean`              | Whether the transaction was successful                 |
| type                    | `enum`                 | The transaction type: `capture`, `intent`, or `refund` |
| driver                  | `string`               | The payment driver used, e.g. `stripe`                 |
| amount                  | `unsignedBigInteger`   | The transaction amount                                 |
| reference               | `string`               | The reference returned from the payment provider       |
| status                  | `string`               | The transaction status, e.g. `settled`                 |
| notes                   | `string` `nullable`    | Any relevant notes                                     |
| card\_type              | `string`               | The card type, e.g. `visa`                             |
| last\_four              | `string` `nullable`    | The last four digits of the card                       |
| meta                    | `json` `nullable`      | Custom metadata                                        |
| captured\_at            | `dateTime` `nullable`  | When the transaction was captured                      |
| created\_at             | `timestamp`            |                                                        |
| updated\_at             | `timestamp`            |                                                        |

### Relationships

| Relationship | Type          | Related Model           | Description                |
| :----------- | :------------ | :---------------------- | :------------------------- |
| `order`      | BelongsTo     | `Lunar\Models\Order`    |                            |
| `currency`   | HasOneThrough | `Lunar\Models\Currency` | Resolved through the order |

### Creating a Transaction

<Tip>
  An order having transactions does not mean it has been placed. Lunar determines whether an order is placed based on whether the `placed_at` column has a datetime value, regardless of any transactions.
</Tip>

Most stores will want to store transactions against orders to track how much has been paid, the payment method used, and how to issue refunds if needed.

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

Transaction::create([
    //...
]);

// Or via the order
$order->transactions()->create([
    //..
]);
```

Transactions can be retrieved via relationships:

```php theme={null}
$order->transactions; // All transactions
$order->captures;     // Capture transactions
$order->intents;      // Intent transactions
$order->refunds;      // Refund transactions
```

## Payments

Lunar is payment-provider agnostic. Any payment provider can be integrated with a storefront.

The key factor for an order is whether the `placed_at` column is populated. Everything else about payment handling is left to the store implementation. Lunar provides helper utilities (as described above) to manage the payment lifecycle.

## Order Notifications

Lunar allows specifying which Laravel mailers and notifications should be available when updating an order's status. These are configured in `config/lunar/orders.php`:

```php theme={null}
'statuses' => [
    'awaiting-payment' => [
        'label' => 'Awaiting Payment',
        'color' => '#848a8c',
        'mailers' => [
            App\Mail\MyMailer::class,
            App\Mail\MyOtherMailer::class,
        ],
        'notifications' => [],
    ],
    // ...
],
```

When updating an order's status in the admin panel, any configured mailers for the new status are available to select. Email addresses can be chosen, and additional addresses can be added.

Lunar stores a render of the sent email in the activity log, providing a clear history of communications.

<Tip>
  These email notifications are not sent automatically when updating the status programmatically outside of the admin panel.
</Tip>

### Mailer Template

When building a mailer template, the `$order` model is available in the view data. When the status is updated, the order is passed through along with any additional content entered. Since additional content may not always be present, check for its existence first.

Example template:

```blade theme={null}
<h1>It's on the way!</h1>

<p>Your order with reference {{ $order->reference }} has been dispatched!</p>

<p>{{ $order->total->formatted() }}</p>

@if($content ?? null)
    <h2>Additional notes</h2>
    <p>{{ $content }}</p>
@endif

@foreach($order->lines as $line)
    <!--  -->
@endforeach
```

## Order Invoice PDF

By default, clicking "Download PDF" in the admin panel when viewing an order generates a basic PDF. The view powering this PDF can be published for customization:

```bash theme={null}
php artisan vendor:publish --tag=lunarpanel.pdf
```

This creates a view at `resources/vendor/lunarpanel/pdf/order.blade.php` that can be freely customized.
