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?
2. Can an order transition directly from new to complete?
3. What is the purpose of the holded state?
Flashcards
Question
Order states?
Click to reveal answer
Answer
new, pending_payment, processing, complete, closed, canceled, holded, payment_review
Question
Processing → Complete trigger?
Click to reveal answer
Answer
All items invoiced and shipped
Question
Holded state purpose?
Click to reveal answer
Answer
Temporarily pause order processing
Question
State transitions?
Click to reveal answer
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()