Skip to main content
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.
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.
Lunar\Models\Order

Fields

FieldTypeDescription
ididPrimary key
customer_idforeignId nullable
user_idforeignId nullableThe authenticated user who placed the order
channel_idforeignIdThe channel the order was placed through
new_customerbooleanWhether the customer is a first-time buyer
cart_idforeignId nullableThe cart used to create the order
statusstringThe order status
referencestring nullableThe generated order reference
customer_referencestring nullableA reference provided by the customer
sub_totalunsignedBigIntegerThe subtotal minus any discounts, excluding tax
discount_totalunsignedBigIntegerThe discount amount, excluding tax
discount_breakdownjson nullableBreakdown of applied discounts
shipping_breakdownjson nullableBreakdown of shipping charges
shipping_totalunsignedBigIntegerThe shipping total including tax
tax_breakdownjsonBreakdown of applied taxes
tax_totalunsignedBigIntegerThe total amount of tax applied
totalunsignedBigIntegerThe grand total including tax
notestext nullableAdditional order notes
currency_codestringThe currency code the order was placed in
compare_currency_codestring nullableThe default currency code at the time of the order
exchange_ratedecimalThe exchange rate between currency_code and compare_currency_code
placed_atdateTime nullableThe datetime the order was considered placed
fingerprintstring nullableA hash used to detect cart changes
metajson nullableCustom metadata
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
channelBelongsToLunar\Models\Channel
cartBelongsToLunar\Models\Cart
currencyBelongsToLunar\Models\CurrencyMatched on currency_code
customerBelongsToLunar\Models\Customer
userBelongsToUserThe authenticatable model from auth config
linesHasManyLunar\Models\OrderLine
physicalLinesHasManyLunar\Models\OrderLineLines where type is physical
digitalLinesHasManyLunar\Models\OrderLineLines where type is digital
shippingLinesHasManyLunar\Models\OrderLineLines where type is shipping
productLinesHasManyLunar\Models\OrderLineAll lines excluding shipping
addressesHasManyLunar\Models\OrderAddress
shippingAddressHasOneLunar\Models\OrderAddressAddress where type is shipping
billingAddressHasOneLunar\Models\OrderAddressAddress where type is billing
transactionsHasManyLunar\Models\Transaction
capturesHasManyLunar\Models\TransactionTransactions where type is capture
intentsHasManyLunar\Models\TransactionTransactions where type is intent
refundsHasManyLunar\Models\TransactionTransactions where type is refund

Creating an Order

An order can be created directly or, the recommended approach, via a Lunar\Models\Cart model.
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:
return [
    //  ...
    'actions' => [
        // ...
        'order_create' => CustomCreateOrder::class,
    ]
];
At minimum, a custom class should extend Lunar\Actions\AbstractAction:
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:
$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:
return [
    // ...
    'validators' => [
        'order_create' => [
            MyCustomValidator::class,
        ],
    ]
];
A custom validator should extend Lunar\Validation\BaseValidator:
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:
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:
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 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:
$order->isDraft();
$order->isPlaced();

Order Lines

Lunar\Models\OrderLine

Fields

FieldTypeDescription
ididPrimary key
order_idforeignId
purchasable_typestringPolymorphic type for the purchasable item
purchasable_idunsignedBigIntegerPolymorphic ID for the purchasable item
typestringThe line type, e.g. physical, digital, shipping
descriptionstringA description of the line item
optionstring nullableOption information if the item is a variant
identifierstringAn identifier for the purchasable item, typically a SKU
unit_priceunsignedBigIntegerThe unit price of the line
unit_quantityunsignedSmallIntegerThe unit quantity, typically 1
quantityunsignedIntegerThe quantity purchased
sub_totalunsignedBigIntegerThe subtotal minus any discounts, excluding tax
discount_totalunsignedBigIntegerThe discount amount, excluding tax
tax_breakdownjsonBreakdown of applied taxes
tax_totalunsignedBigIntegerThe total amount of tax applied
totalunsignedBigIntegerThe grand total including tax
notestext nullableAdditional line notes
metajson nullableCustom metadata
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
orderBelongsToLunar\Models\Order
purchasableMorphToThe polymorphic purchasable item
currencyHasOneThroughLunar\Models\CurrencyResolved through the order

Creating an Order Line

When using the createOrder method on a cart, order lines are created automatically.
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.
Lunar\Models\OrderAddress
When using the createOrder method on a cart, order addresses are created automatically.

Fields

FieldTypeDescription
ididPrimary key
order_idforeignId
country_idforeignId nullable
titlestring nullable
first_namestring nullable
last_namestring nullable
company_namestring nullable
tax_identifierstring nullableA tax identification number
line_onestring nullable
line_twostring nullable
line_threestring nullable
citystring nullable
statestring nullable
postcodestring nullable
delivery_instructionsstring nullable
contact_emailstring nullable
contact_phonestring nullable
typestringThe address type: billing or shipping
shipping_optionstring nullableA unique identifier for the selected shipping option
metajson nullableCustom metadata
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
orderBelongsToLunar\Models\Order
countryBelongsToLunar\Models\Country

Creating an Order Address

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:
$order->shippingAddress;
$order->billingAddress;

Shipping Options

A Shipping Tables add-on is planned to simplify shipping configuration in the admin panel.
To add shipping options, extend Lunar with custom logic. Shipping options can be fetched using the ShippingManifest facade:
\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:
$cart->setShippingOption(\Lunar\DataTypes\ShippingOption $option);

Transactions

Lunar\Models\Transaction

Fields

FieldTypeDescription
ididPrimary key
parent_transaction_idforeignId nullableA reference to a parent transaction
order_idforeignId
successbooleanWhether the transaction was successful
typeenumThe transaction type: capture, intent, or refund
driverstringThe payment driver used, e.g. stripe
amountunsignedBigIntegerThe transaction amount
referencestringThe reference returned from the payment provider
statusstringThe transaction status, e.g. settled
notesstring nullableAny relevant notes
card_typestringThe card type, e.g. visa
last_fourstring nullableThe last four digits of the card
metajson nullableCustom metadata
captured_atdateTime nullableWhen the transaction was captured
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
orderBelongsToLunar\Models\Order
currencyHasOneThroughLunar\Models\CurrencyResolved through the order

Creating a Transaction

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.
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.
use Lunar\Models\Transaction;

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

// Or via the order
$order->transactions()->create([
    //..
]);
Transactions can be retrieved via relationships:
$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:
'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.
These email notifications are not sent automatically when updating the status programmatically outside of the admin panel.

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:
<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:
php artisan vendor:publish --tag=lunarpanel.pdf
This creates a view at resources/vendor/lunarpanel/pdf/order.blade.php that can be freely customized.