Skip to content
intermediate Phase 49 · Order Lifecycle

Order States and Statuses

State transitions, custom statuses, and the order state machine in Magento 2

45m
0 problems
Topic Progress 0%

Order States Overview

Built-in Order States

// Magento\Sales\Model\Order
namespace Magento\Sales\Model\Order;

const STATE_NEW = 'new';
const STATE_PENDING_PAYMENT = 'pending_payment';
const STATE_PROCESSING = 'processing';
const STATE_COMPLETE = 'complete';
const STATE_CLOSED = 'closed';
const STATE_CANCELED = 'canceled';
const STATE_HOLDED = 'holded';
const STATE_PAYMENT_REVIEW = 'payment_review';

State Descriptions

State Description
new Order just placed, awaiting processing
pending_payment Awaiting payment confirmation
processing Payment received, being processed
complete All items shipped and invoiced
closed Order completed with refund
canceled Order canceled
holded Order on hold
payment_review Payment under review

State Transitions

State Transition Flow

// Order state transitions
namespace Magento\Sales\Model\Order;

// State transitions are triggered by actions:
// 1. new → pending_payment: Order placed, awaiting payment
// 2. pending_payment → processing: Payment confirmed
// 3. processing → complete: All items invoiced and shipped
// 4. processing → closed: Completed with refund
// 5. any → canceled: Order canceled
// 6. any → holded: Order held
// 7. processing → payment_review: Payment needs review

State Check Methods

$order = $this->orderRepository->get($orderId);

// Check current state
echo $order->getState(); // 'processing'

// Check if order can transition
if ($order->canInvoice()) {
    // Can still invoice items
}

if ($order->canShip()) {
    // Can still ship items
}

if ($order->canCreditmemo()) {
    // Can still refund items
}

// Check completion status
if ($order->getState() === \Magento\Sales\Model\Order::STATE_COMPLETE) {
    // Order fully processed
}

Custom Order Statuses

Creating Custom Statuses

// Create custom order status
namespace Vendor\Order\Service;

class CustomStatusManager
{
    public function __construct(
        private \Magento\Sales\Model\ResourceModel\Order\Status\Factory $statusFactory,
        private \Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory $statusCollectionFactory
    ) {}

    public function createStatus(
        string $code,
        string $label,
        string $state
    ): void {
        $status = $this->statusFactory->create();
        $status->setData([
            'status' => $code,
            'label' => $label,
            'state' => $state,
        ]);
        $status->save();

        // Assign to state
        $stateModel = $this->stateFactory->create()->load($state);
        $stateModel->assignStatus($code);
    }
}

Status Configuration

<!-- etc/di.xml -->
<config>
    <type name="Magento\Sales\Model\Order">
        <plugin name="custom_status" type="Vendor\Order\Plugin\OrderStatusPlugin"/>
    </type>
</config>

Status Plugin

namespace Vendor\Order\Plugin;

class OrderStatusPlugin
{
    public function beforeSetState(
        \Magento\Sales\Model\Order $order,
        string $state
    ): array {
        // Validate or modify state transition
        $currentState = $order->getState();

        // Custom transition rules
        $allowedTransitions = [
            'new' => ['pending_payment', 'canceled'],
            'pending_payment' => ['processing', 'canceled'],
            'processing' => ['complete', 'closed', 'canceled', 'holded'],
        ];

        if (!isset($allowedTransitions[$currentState]) ||
            !in_array($state, $allowedTransitions[$currentState])) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Invalid state transition from %1 to %2', $currentState, $state)
            );
        }

        return [$state];
    }
}

Order State Machine

State Machine Pattern

// State machine for order transitions
namespace Vendor\Order\StateMachine;

class OrderStateMachine
{
    private array $transitions = [
        'new' => ['pending_payment', 'canceled'],
        'pending_payment' => ['processing', 'canceled'],
        'processing' => ['complete', 'closed', 'canceled', 'holded', 'payment_review'],
        'complete' => ['closed', 'canceled'],
        'closed' => [],
        'canceled' => [],
        'holded' => ['processing', 'canceled'],
        'payment_review' => ['processing', 'canceled'],
    ];

    public function canTransition(
        string $fromState,
        string $toState
    ): bool {
        return isset($this->transitions[$fromState]) &&
               in_array($toState, $this->transitions[$fromState]);
    }

    public function transition(
        \Magento\Sales\Api\Data\OrderInterface $order,
        string $newState
    ): void {
        $currentState = $order->getState();

        if (!$this->canTransition($currentState, $newState)) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Cannot transition from %1 to %2', $currentState, $newState)
            );
        }

        $order->setState($newState);
        $this->orderRepository->save($order);
    }
}

Automatic State Updates

// State updates happen automatically:
// 1. Order placed → new
// 2. Payment captured → processing
// 3. All items invoiced + shipped → complete
// 4. Refund issued → closed
// 5. Cancel action → canceled

// Manual state change
$order->setState(\Magento\Sales\Model\Order::STATE_HOLDED);
$order->addStatusHistoryComment('Order placed on hold for review');
$this->orderRepository->save($order);

Quiz

1. What triggers the transition from processing to complete?

Question 1 options

2. Can an order transition directly from new to complete?

Question 2 options

3. What is the purpose of the holded state?

Question 3 options

Flashcards

Question

Order states?

Answer

new, pending_payment, processing, complete, closed, canceled, holded, payment_review

Question

Processing → Complete trigger?

Answer

All items invoiced and shipped

Question

Holded state purpose?

Answer

Temporarily pause order processing

Question

State transitions?

Answer

Controlled by canTransition rules and actions

Revision Notes

Key Takeaways

  • 1. Order states represent the current phase in the order lifecycle
  • 2. State transitions are triggered by actions (payment, shipment, refund)
  • 3. Custom statuses can be created and assigned to states
  • 4. State machine pattern controls valid transitions
  • 5. Automatic state updates occur based on item status

Interview Tips

  • Map out the complete state machine with transitions
  • Explain why certain transitions are not allowed
  • Discuss how custom statuses extend the default state system

Cheat Sheet

Order States:
  new → pending_payment → processing → complete
  complete → closed (with refund)
  any → canceled
  any → holded → processing

Transitions:
  new: → pending_payment, canceled
  pending_payment: → processing, canceled
  processing: → complete, closed, canceled, holded
  complete: → closed, canceled

Custom Statuses:
  Create via statusFactory
  Assign to state via assignStatus()