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

> Order creation, lifecycle, lines, transactions, cancellation, and refunds in Lunar.

An order is a purchase created from a cart, tracked through payment and fulfilment until it is archived.

## Overview

An order is created from a [cart](/2.x/reference/carts) at checkout. It snapshots the cart's lines, addresses, and totals so the record stays accurate even if products, prices, or addresses change later. An order's lifecycle is read from two independently derived rollups, `payment_status` and `fulfilment_status`, plus an open/closed archive flag — there is no single headline "order status" column.

<Info>
  All monetary values (such as `sub_total`, `total`, `tax_total`) are stored as integers in the currency's minor unit and cast through `Lunar\Core\Models\Concerns\FormatsPrices`, which exposes formatted/decimal accessors.
</Info>

Fulfilment (shipping, collection, digital provisioning, tracking, carriers) is covered in full on the [Fulfilments](/2.x/reference/fulfilments) page. Sending emails to the customer is covered on the [Notifications](/2.x/reference/notifications) page. Capturing and refunding money through a payment provider is covered on the [Payments](/2.x/reference/payments) page — this page documents the order-side records those operations write to.

## Order model

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

### Fields

| Field                   | Type                            | Description                                                                                |
| :---------------------- | :------------------------------ | :----------------------------------------------------------------------------------------- |
| id                      | `bigIncrements`                 | Primary key                                                                                |
| public\_id              | `ulid`                          | Stable external identifier for APIs and integrations                                       |
| user\_id                | `foreignId` `nullable`          | The authenticated user who placed the order                                                |
| channel\_id             | `foreignId`                     | The channel the order was placed through                                                   |
| payment\_status         | `string`                        | Derived rollup of the transaction ledger, see [Payment status](#payment-status)            |
| fulfilment\_status      | `string`                        | Derived rollup of the order's fulfilments, see [Fulfilment status](#fulfilment-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                                                         |
| shipping\_total         | `unsignedBigInteger`            | The shipping total including tax                                                           |
| tax\_breakdown          | `jsonb`                         | 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. Null means the order is still a draft        |
| closed\_at              | `dateTime` `nullable`           | The datetime the order was archived, see [Open and closed orders](#open-and-closed-orders) |
| cancelled\_at           | `dateTime` `nullable`           | The datetime the order was cancelled, see [Cancelling an order](#cancelling-an-order)      |
| cancel\_reason          | `string` `nullable`             | The cancellation reason key                                                                |
| cancel\_note            | `text` `nullable`               | A staff note recorded with the cancellation                                                |
| meta                    | `jsonb` `nullable`              | Custom metadata                                                                            |
| created\_at             | `timestamp`                     |                                                                                            |
| updated\_at             | `timestamp`                     |                                                                                            |
| customer\_id            | `foreignId` `nullable`          |                                                                                            |
| new\_customer           | `boolean`                       | Whether the customer is a first-time buyer                                                 |
| discount\_breakdown     | `jsonb` `nullable`              | Breakdown of applied discounts                                                             |
| shipping\_breakdown     | `jsonb` `nullable`              | Breakdown of shipping charges                                                              |
| cart\_id                | `unsignedBigInteger` `nullable` | The cart used to create the order                                                          |
| fingerprint             | `string` `nullable`             | A hash used to detect cart changes since the order was created                             |
| region\_id              | `foreignId` `nullable`          | The region resolved for the order                                                          |

### Relationships

| Relationship       | Type      | Related Model                    | Description                                                                           |
| :----------------- | :-------- | :------------------------------- | :------------------------------------------------------------------------------------ |
| `channel`          | BelongsTo | `Lunar\Core\Models\Channel`      |                                                                                       |
| `region`           | BelongsTo | `Lunar\Core\Models\Region`       |                                                                                       |
| `cart`             | BelongsTo | `Lunar\Core\Models\Cart`         |                                                                                       |
| `lines`            | HasMany   | `Lunar\Core\Models\OrderLine`    | Ordered by `id`                                                                       |
| `fulfilments`      | HasMany   | `Lunar\Core\Models\Fulfilment`   | See [Fulfilments](/2.x/reference/fulfilments)                                         |
| `physicalLines`    | HasMany   | `Lunar\Core\Models\OrderLine`    | Lines where `type` is `physical`                                                      |
| `fulfillableLines` | HasMany   | `Lunar\Core\Models\OrderLine`    | Lines where `requires_fulfilment` is `true` — the set fulfilments are created against |
| `digitalLines`     | HasMany   | `Lunar\Core\Models\OrderLine`    | Lines where `type` is `digital`                                                       |
| `shippingLines`    | HasMany   | `Lunar\Core\Models\OrderLine`    | Lines where `type` is `shipping`                                                      |
| `productLines`     | HasMany   | `Lunar\Core\Models\OrderLine`    | All lines excluding shipping                                                          |
| `currency`         | BelongsTo | `Lunar\Core\Models\Currency`     | Matched on `currency_code`                                                            |
| `addresses`        | HasMany   | `Lunar\Core\Models\OrderAddress` |                                                                                       |
| `shippingAddress`  | HasOne    | `Lunar\Core\Models\OrderAddress` | Address where `type` is `shipping`                                                    |
| `billingAddress`   | HasOne    | `Lunar\Core\Models\OrderAddress` | Address where `type` is `billing`                                                     |
| `transactions`     | HasMany   | `Lunar\Core\Models\Transaction`  | Ordered by `created_at` descending                                                    |
| `captures`         | HasMany   | `Lunar\Core\Models\Transaction`  | Transactions where `type` is `capture`                                                |
| `intents`          | HasMany   | `Lunar\Core\Models\Transaction`  | Transactions where `type` is `intent`                                                 |
| `refunds`          | HasMany   | `Lunar\Core\Models\Transaction`  | Transactions where `type` is `refund`                                                 |
| `customer`         | BelongsTo | `Lunar\Core\Models\Customer`     |                                                                                       |
| `user`             | BelongsTo | User                             | The authenticatable model from the auth config                                        |

### Scopes

| Scope       | Description                        |
| :---------- | :--------------------------------- |
| `open`      | Orders where `closed_at` is null   |
| `closed`    | Orders where `closed_at` is set    |
| `cancelled` | Orders where `cancelled_at` is set |

### Other useful methods

| Method                                | Description                                                                                             |
| :------------------------------------ | :------------------------------------------------------------------------------------------------------ |
| `isDraft(): bool`                     | `true` when `placed_at` is empty                                                                        |
| `isPlaced(): bool`                    | `true` when `placed_at` is set                                                                          |
| `isOpen(): bool` / `isClosed(): bool` | Whether the order has been archived, see [Open and closed orders](#open-and-closed-orders)              |
| `isCancelled(): bool`                 | Whether the order has been cancelled                                                                    |
| `lifecycleStatus(): string`           | Returns `'cancelled'`, `'closed'`, or `'open'` — cancelled takes precedence. Used for badges and search |
| `cancelReasonLabel(): ?string`        | The human-readable label for `cancel_reason`                                                            |

## Creating an order from a cart

An order is created from a cart via the `createOrder()` verb:

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

$cart = Cart::find(1);

$order = $cart->createOrder(
    allowMultipleOrders: false,
    orderIdToUpdate: null,
);
```

* `allowMultipleOrders` — a cart generally has one draft order associated with it. Pass `true` to allow multiple orders per cart (for example, a declined payment followed by a new checkout attempt with a different order).
* `orderIdToUpdate` — optionally pass the ID of an existing draft order (one with a null `placed_at`) belonging to the cart, to update it instead of creating a new one.

Before creating, the cart is validated against `config('lunar.cart.validators.order_create')` (defaulting to `Lunar\Core\Validation\Cart\ValidateCartForOrderCreation`), which throws a validation exception with a helpful message when the cart is not ready. Check readiness without throwing:

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

`createOrder()` delegates to `Lunar\Core\Contracts\Actions\Carts\CreatesOrder` (default `Lunar\Core\Actions\Carts\CreateOrder`), which fills the order's `cart_id` and `fingerprint`, then runs it through the pipeline configured under `lunar.orders.pipelines.creation` in `config/lunar/orders.php`:

```php theme={null}
'pipelines' => [
    'creation' => [
        FillOrderFromCart::class,
        CreateOrderLines::class,
        CreateOrderAddresses::class,
        CreateShippingLine::class,
        CleanUpOrderLines::class,
        MapDiscountBreakdown::class,
    ],
],
```

Each stage runs in order: `FillOrderFromCart` copies totals, currency, and customer data across and generates the reference; `CreateOrderLines` snapshots the cart's lines onto `OrderLine` rows; `CreateOrderAddresses` copies the cart's addresses; `CreateShippingLine` adds a `shipping` line when a shipping option was chosen; `CleanUpOrderLines` and `MapDiscountBreakdown` tidy up lines and rewrite the discount breakdown once discounts are marked as used. Add a custom stage by extending the array, or replace `CreatesOrder`'s binding to change the flow entirely.

`payment_status` and `fulfilment_status` are not set explicitly during creation — they take the migration column defaults (`pending` and `unfulfilled`) until the first recompute runs (for example, when a transaction or fulfilment is recorded against the order).

### Order reference generation

The order reference is generated from `config/lunar/orders.php`:

```php theme={null}
'reference_format' => [
    'prefix' => null,
    'padding_direction' => STR_PAD_LEFT,
    'padding_character' => '0',
    'length' => 8,
],

'reference_generator' => Lunar\Core\Orders\ReferenceGenerator::class,
```

The default generator pads the order's ID to `length` characters using `padding_character`/`padding_direction`, then prepends `prefix`. Set `reference_generator` to `null` to disable reference generation. A custom generator implements `Lunar\Core\Contracts\OrderReferenceGenerator`:

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

use Lunar\Core\Contracts\OrderReferenceGenerator;
use Lunar\Core\Models\Order;

class CustomReferenceGenerator implements OrderReferenceGenerator
{
    public function generate(Order $order): string
    {
        return 'ORD-'.$order->id;
    }
}
```

## Order lifecycle

An order's lifecycle is read from three independent signals rather than one status column: whether it is a draft or placed (`placed_at`), its two derived rollups (`payment_status`, `fulfilment_status`), and whether it has been archived (`closed_at`) or cancelled (`cancelled_at`).

<Warning>
  `payment_status` and `fulfilment_status` are **derived, unguarded rollups**, not a hand-driven state machine — there is no fixed transition graph and nothing to call `transitionTo()` on. They are recomputed automatically from the transaction ledger and the order's fulfilments whenever either changes, and any value can follow any other as the underlying records change.
</Warning>

### Payment status

`payment_status` is cast to a `Lunar\Core\States\Order\Payment\PaymentStatus` instance, rolled up from the order's `transactions`:

| State               | `$name`              | Meaning                                                             |
| :------------------ | :------------------- | :------------------------------------------------------------------ |
| `Pending`           | `pending`            | No transactions recorded                                            |
| `Voided`            | `voided`             | Transactions exist but none succeeded                               |
| `Authorized`        | `authorized`         | A successful payment intent exists, nothing captured yet            |
| `PartiallyPaid`     | `partially-paid`     | Some, but not all, of the order total has been captured             |
| `Paid`              | `paid`               | The full order total has been captured (or the order total is zero) |
| `PartiallyRefunded` | `partially-refunded` | The order was paid in full and part of it has since been refunded   |
| `Refunded`          | `refunded`           | Everything captured has been refunded                               |

### Fulfilment status

`fulfilment_status` is cast to a `Lunar\Core\States\Order\Fulfilment\FulfilmentStatus` instance, rolled up from the order's fulfillable lines and its `fulfilments` (see [Fulfilments](/2.x/reference/fulfilments) for the per-fulfilment lifecycle):

| State                | `$name`               | Meaning                                                                                                  |
| :------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------- |
| `Unfulfilled`        | `unfulfilled`         | None of the fulfillable quantity has been dispatched                                                     |
| `PartiallyFulfilled` | `partially-fulfilled` | Some, but not all, of the fulfillable quantity has been dispatched                                       |
| `Fulfilled`          | `fulfilled`           | All fulfillable quantity has been dispatched (an order with nothing to fulfil resolves here immediately) |
| `PartiallyReturned`  | `partially-returned`  | Some dispatched quantity has been returned                                                               |
| `Returned`           | `returned`            | All dispatched quantity has been returned                                                                |

Both rollups recompute automatically whenever a `Transaction` or a `Fulfilment`/`FulfilmentLine` changes, dispatching `Lunar\Core\Events\Orders\OrderPaymentStatusUpdated` / `OrderFulfilmentStatusUpdated` when the value actually changes.

### Open and closed orders

Independently of payment and fulfilment, an order is either open (in the active work queue) or closed (archived):

```php theme={null}
$order->close();   // Lunar\Core\Contracts\Actions\Orders\ClosesOrder
$order->reopen();  // Lunar\Core\Contracts\Actions\Orders\ReopensOrder
```

Both are idempotent — closing a closed order, or reopening an open one, is a no-op. A cancelled order cannot be reopened.

<Tip>
  An order can be closed automatically the moment it becomes fully paid (`Paid`) and fully fulfilled (`Fulfilled`). It is off by default — bind a custom `Lunar\Core\Contracts\OrderSettings` implementation and return `true` from `autoClosesSettledOrders()` to opt in:

  ```php theme={null}
  use Lunar\Core\Contracts\OrderSettings;
  use Lunar\Core\Models\Order;

  class AutoCloseOrderSettings implements OrderSettings
  {
      public function autoClosesSettledOrders(Order $order): bool
      {
          return true;
      }
  }
  ```

  Bind it in a service provider: `$this->app->bind(OrderSettings::class, AutoCloseOrderSettings::class);`. Auto-close never reopens an order that later regresses (a return or a partial refund) — that stays a deliberate `reopen()`.
</Tip>

### Cancelling an order

```php theme={null}
$order->cancel(
    reason: 'customer',
    note: 'Customer changed their mind',
    notify: true,
);
```

Cancellation is one-way and covers status only — it does not issue a refund or restock inventory. An order can be cancelled as long as it is not already cancelled and nothing on it has shipped or returned. Cancelling voids the order's un-shipped fulfilments, stamps `cancelled_at`/`cancel_reason`/`cancel_note`, and also closes the order.

Reasons come from the `CancelReasons` manifest rather than config, so they can be adjusted per application:

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

CancelReasons::all();                          // array<string, string>
CancelReasons::add('warehouse-error', 'Warehouse error');
CancelReasons::forget('fraud');
```

## Order lines

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

<Info>
  There is no generic "fee" or "extra charge" line primitive. A line's `type` is whatever the purchasable's `getType()` returns (core's `ProductVariant` returns `physical` or `digital`); an extra charge is modelled as an order line tied to its own `Purchasable`, and incoming money is tracked separately as `Transaction` rows (see [Transactions](#transactions)) rather than as a line item.
</Info>

### Fields

| Field                | Type                            | Description                                                                                                             |
| :------------------- | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------- |
| id                   | `bigIncrements`                 | Primary key                                                                                                             |
| public\_id           | `ulid`                          | Stable external identifier                                                                                              |
| order\_id            | `foreignId`                     |                                                                                                                         |
| purchasable\_type    | `string` `nullable`             | Polymorphic type for the purchasable item. Empty on shipping lines                                                      |
| purchasable\_id      | `unsignedBigInteger` `nullable` | Polymorphic ID for the purchasable item. Empty on shipping lines                                                        |
| type                 | `string`                        | The line type, from the purchasable's `getType()` (e.g. `physical`, `digital`); always `shipping` for the shipping line |
| requires\_shipping   | `boolean`                       | Needs physical delivery, stamped from the purchasable's `isShippable()`                                                 |
| requires\_fulfilment | `boolean`                       | Needs fulfilling at all — a superset of `requires_shipping` that drives the fulfilment rollup                           |
| 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       | `smallInteger`                  | 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       | `jsonb`                         | Breakdown of applied taxes                                                                                              |
| tax\_total           | `unsignedBigInteger`            | The total amount of tax applied                                                                                         |
| total                | `unsignedBigInteger`            | The grand total including tax                                                                                           |
| refunded\_quantity   | `unsignedInteger`               | Rollup of `refund_lines.quantity` for this line, see [Refunds](#refunds)                                                |
| notes                | `text` `nullable`               | Additional line notes                                                                                                   |
| meta                 | `jsonb` `nullable`              | Custom metadata                                                                                                         |
| created\_at          | `timestamp`                     |                                                                                                                         |
| updated\_at          | `timestamp`                     |                                                                                                                         |

### Relationships

| Relationship      | Type          | Related Model                      | Description                                                |
| :---------------- | :------------ | :--------------------------------- | :--------------------------------------------------------- |
| `order`           | BelongsTo     | `Lunar\Core\Models\Order`          |                                                            |
| `purchasable`     | MorphTo       | —                                  | The polymorphic purchasable item. `null` on shipping lines |
| `fulfilmentLines` | HasMany       | `Lunar\Core\Models\FulfilmentLine` | See [Fulfilments](/2.x/reference/fulfilments)              |
| `refundLines`     | HasMany       | `Lunar\Core\Models\RefundLine`     | See [Refunds](#refunds)                                    |
| `currency`        | HasOneThrough | `Lunar\Core\Models\Currency`       | Resolved through the order                                 |

### Scopes

| Scope               | Description                               |
| :------------------ | :---------------------------------------- |
| `withoutFulfilment` | Lines not yet allocated to any fulfilment |

### Other useful methods

```php theme={null}
$line->refundableQuantity(): int; // quantity minus refunded_quantity, floored at 0
```

<Info>
  Shipping lines have no `purchasable` — a shipping option is a data transfer object rather than a model, so shipping lines are stored with no morph. Everything needed to display the line (`description`, `unit_price`, `total`, `meta`) is snapshotted onto the line itself. Guard for `null` when iterating `$order->lines`, or use `$order->productLines` to exclude shipping lines.
</Info>

## Order addresses

An order has many addresses, typically one for billing and one for shipping. They are created automatically by the `createOrder()` pipeline.

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

### Fields

| Field                  | Type                   | Description                                          |
| :--------------------- | :--------------------- | :--------------------------------------------------- |
| id                     | `bigIncrements`        | Primary key                                          |
| public\_id             | `ulid`                 | Stable external identifier                           |
| order\_id              | `foreignId`            |                                                      |
| country\_id            | `foreignId` `nullable` |                                                      |
| title                  | `string` `nullable`    |                                                      |
| first\_name            | `string` `nullable`    |                                                      |
| last\_name             | `string` `nullable`    |                                                      |
| company\_name          | `string` `nullable`    |                                                      |
| 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                   | `jsonb` `nullable`     | Custom metadata                                      |
| created\_at            | `timestamp`            |                                                      |
| updated\_at            | `timestamp`            |                                                      |
| tax\_identifier        | `string` `nullable`    | A tax identification number                          |

### Relationships

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

The shipping and billing addresses are accessed directly from the order:

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

## Transactions

Money movement against an order — authorizations, captures, and refunds — is recorded on `Transaction` rows. There is no separate "tender" model: a successful `capture` transaction *is* the record of money received, and a `refund` transaction is money paid back out.

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

### Fields

| Field                   | Type                   | Description                                                |
| :---------------------- | :--------------------- | :--------------------------------------------------------- |
| id                      | `bigIncrements`        | Primary key                                                |
| public\_id              | `ulid`                 | Stable external identifier                                 |
| order\_id               | `foreignId`            |                                                            |
| success                 | `boolean`              | Whether the transaction was successful                     |
| driver                  | `string`               | The payment driver used, e.g. `stripe`                     |
| amount                  | `integer`              | The transaction amount, in the order currency's minor unit |
| 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` `nullable`    | The card type, e.g. `visa`                                 |
| last\_four              | `string` `nullable`    | The last four digits of the card                           |
| meta                    | `jsonb` `nullable`     | Custom metadata                                            |
| created\_at             | `timestamp`            |                                                            |
| updated\_at             | `timestamp`            |                                                            |
| parent\_transaction\_id | `foreignId` `nullable` | A reference to a parent transaction                        |
| captured\_at            | `dateTime` `nullable`  | When the transaction was captured                          |
| type                    | `enum`                 | The transaction type: `capture`, `intent`, or `refund`     |

### Relationships

| Relationship  | Type          | Related Model                  | Description                                                              |
| :------------ | :------------ | :----------------------------- | :----------------------------------------------------------------------- |
| `order`       | BelongsTo     | `Lunar\Core\Models\Order`      |                                                                          |
| `currency`    | HasOneThrough | `Lunar\Core\Models\Currency`   | Resolved through the order                                               |
| `refundLines` | HasMany       | `Lunar\Core\Models\RefundLine` | Line-level allocations for a refund transaction, see [Refunds](#refunds) |

<Info>
  See the [Payments](/2.x/reference/payments) reference for how a payment driver authorizes and creates these transactions in the first place.
</Info>

### Capturing a payment

Capture an amount against a successful payment intent transaction:

```php theme={null}
$capture = $order->capture(
    transactionId: $transaction->id,
    amount: 49.99, // major units, converted to the currency's minor unit internally
);

$capture->success; // bool
$capture->message; // string
```

`$order->capture()` delegates to `Lunar\Core\Contracts\Actions\Orders\CapturesOrder`, guards that the amount does not exceed the intent, and dispatches the driver's `capture()` — which creates the `capture` `Transaction`. Recording the capture triggers a `payment_status` recompute automatically.

## Refunds

A refund targets a specific capture `Transaction` and can cover order lines, shipping, and a manual adjustment in one request:

```php theme={null}
use Lunar\Core\DataObjects\RefundRequest;

$refund = $order->refund(new RefundRequest(
    transactionId: $transaction->id,
    lines: [
        ['order_line_id' => $line->id, 'quantity' => 1],
    ],
    shipping: 0,
    adjustment: 0,
    notes: 'Damaged on arrival',
    notify: true,
));

$refund->success;     // bool
$refund->message;     // ?string
$refund->transaction; // ?Lunar\Core\Models\Transaction — the refund transaction, when the driver records one
```

Each requested line is validated against its own `refundableQuantity()`, and the total requested amount is validated against the order's available-to-refund balance (captured minus already refunded). An amount-only refund — no line allocation — is expressed with an empty `lines` array and the whole amount on `adjustment`.

When the driver's refund succeeds and returns a `Transaction`, each line allocation is recorded as a `RefundLine` and the corresponding `OrderLine.refunded_quantity` is incremented:

```php theme={null}
Lunar\Core\Models\RefundLine
```

| Field           | Type                 | Description                                             |
| :-------------- | :------------------- | :------------------------------------------------------ |
| id              | `bigIncrements`      | Primary key                                             |
| public\_id      | `ulid`               | Stable external identifier                              |
| transaction\_id | `foreignId`          | The refund transaction this allocation belongs to       |
| order\_line\_id | `foreignId`          | The order line this allocation refunds                  |
| quantity        | `unsignedInteger`    | The quantity refunded                                   |
| amount          | `unsignedBigInteger` | The refunded amount, in the order currency's minor unit |
| created\_at     | `timestamp`          |                                                         |
| updated\_at     | `timestamp`          |                                                         |

| Relationship  | Type      | Related Model                   | Description |
| :------------ | :-------- | :------------------------------ | :---------- |
| `transaction` | BelongsTo | `Lunar\Core\Models\Transaction` |             |
| `orderLine`   | BelongsTo | `Lunar\Core\Models\OrderLine`   |             |

<Info>
  Not every payment driver hands back the `Transaction` it created for a refund — `$refund->transaction` can be `null`. In that case the money still refunds correctly through the provider, it just cannot be attributed to specific lines in the ledger.
</Info>

## Notifying the customer

```php theme={null}
$order->notifyCustomer(
    notification: 'order-update',
    message: 'Your order has been delayed by a day.',
    recipients: [], // defaults to the order's billing + shipping contacts
);
```

See the [Notifications](/2.x/reference/notifications) reference for the notification manifest, mail routing, and the notifications Lunar sends automatically.

## Fulfilments

An order's physical, digital, or click-and-collect fulfilment is tracked through `Fulfilment` records, created against the order's `fulfillableLines`:

```php theme={null}
$fulfilment = $order->createFulfilment(
    lines: [$line->id => 1],
);
```

See the [Fulfilments](/2.x/reference/fulfilments) reference for the full fulfilment lifecycle, methods, carriers, and tracking.
