Order Status Management
EverShop uses a three-dimensional status system to track orders. Each order has three independent statuses that together determine the overall order state:
- Payment Status — Tracks the state of payment (e.g.,
pending,paid,refunded). - Shipment Status — Tracks the state of fulfillment (e.g.,
pending,shipped,delivered). - Order Status — The overall status, automatically resolved from the combination of payment and shipment statuses.
How Order Status Is Resolved
When either the payment status or shipment status changes, EverShop automatically resolves the new order status using a PSO (Payment-Shipment-Order) mapping. This mapping defines which order status corresponds to each combination of payment and shipment statuses.
Payment Status Change → PSO Mapping Lookup → Order Status Update
Shipment Status Change → PSO Mapping Lookup → Order Status Update
This means you typically never set the order status directly. Instead, you update the payment or shipment status, and the order status follows automatically.
Configuring Statuses
All statuses are defined in your configuration files.
Order Status
{
"oms": {
"order": {
"status": {
"new": {
"name": "New",
"badge": "default",
"progress": "incomplete",
"isDefault": true,
"next": ["processing", "canceled"]
},
"processing": {
"name": "Processing",
"badge": "default",
"progress": "incomplete",
"next": ["completed", "canceled"]
},
"completed": {
"name": "Completed",
"badge": "success",
"progress": "complete",
"next": ["closed"]
},
"canceled": {
"name": "Canceled",
"badge": "critical",
"progress": "complete",
"next": []
}
}
}
}
}
Each status has:
name— Display name in the admin panel.badge— Visual style (default,success,attention,critical).progress— Whether the order isincompleteorcomplete.isDefault— Whether this is the initial status for new orders.next— Array of statuses this status can transition to. An empty array means the status is final.
Payment Status
{
"oms": {
"order": {
"paymentStatus": {
"pending": {
"name": "Pending",
"badge": "default",
"isDefault": true
},
"paid": {
"name": "Paid",
"badge": "success"
},
"failed": {
"name": "Failed",
"badge": "critical"
}
}
}
}
}
Shipment Status
{
"oms": {
"order": {
"shipmentStatus": {
"pending": {
"name": "Pending",
"badge": "default",
"isDefault": true
},
"shipped": {
"name": "Shipped",
"badge": "attention"
},
"delivered": {
"name": "Delivered",
"badge": "success"
}
}
}
}
}
PSO Mapping
The PSO mapping connects payment and shipment statuses to order statuses:
{
"oms": {
"order": {
"psoMapping": {
"paid:delivered": "completed",
"paid:shipped": "processing",
"paid:pending": "processing",
"pending:*": "new",
"failed:*": "canceled",
"*:*": "new"
}
}
}
}
The format is {paymentStatus}:{shipmentStatus}. The * wildcard matches any status. Mappings are checked in this order:
- Exact match:
paid:delivered - Wildcard shipment:
paid:* - Wildcard payment:
*:delivered - Double wildcard:
*:*
Registering Custom Statuses
EverShop provides dedicated functions to register custom statuses programmatically from your extension's bootstrap.ts. All functions are imported from @evershop/evershop/oms/services.
Register a Payment Status
import { registerPaymentStatus } from '@evershop/evershop/oms/services';
export default async () => {
// Register with inline PSO mapping
registerPaymentStatus('my_gateway_authorized', {
name: 'Authorized',
badge: 'warning',
isDefault: false,
isCancelable: true
}, {
'my_gateway_authorized:*': 'processing'
});
registerPaymentStatus('my_gateway_captured', {
name: 'Captured',
badge: 'success',
isDefault: false,
isCancelable: false
}, {
'my_gateway_captured:*': 'processing',
'my_gateway_captured:delivered': 'completed'
});
registerPaymentStatus('my_gateway_refunded', {
name: 'Refunded',
badge: 'destructive',
isDefault: false,
isCancelable: false
}, {
'my_gateway_refunded:*': 'closed'
});
};
Signature:
registerPaymentStatus(
id: string, // Unique status ID (no spaces)
detail: PaymentStatus, // Status properties
psoMapping?: Record<string, string> // Optional PSO mappings
): void
Register a Shipment Status
import { registerShipmentStatus } from '@evershop/evershop/oms/services';
export default async () => {
registerShipmentStatus('in_transit', {
name: 'In Transit',
badge: 'attention',
isDefault: false
}, {
'paid:in_transit': 'processing'
});
registerShipmentStatus('out_for_delivery', {
name: 'Out for Delivery',
badge: 'warning',
isDefault: false
});
};
Signature:
registerShipmentStatus(
id: string, // Unique status ID (no spaces)
detail: ShipmentStatus, // Status properties
psoMapping?: Record<string, string> // Optional PSO mappings
): void
Register an Order Status
import { registerOrderStatus } from '@evershop/evershop/oms/services';
export default async () => {
registerOrderStatus('on_hold', {
name: 'On Hold',
badge: 'attention',
isDefault: false,
next: ['processing', 'canceled']
});
};
Signature:
registerOrderStatus(
id: string, // Unique status ID (no spaces)
detail: OrderStatus // Status properties including 'next' transitions
): void
Register PSO Mappings Separately
Use registerPSOStatusMapping() to add individual mappings without registering a new status:
import { registerPSOStatusMapping } from '@evershop/evershop/oms/services';
export default async () => {
registerPSOStatusMapping('my_gateway_captured', 'delivered', 'completed');
registerPSOStatusMapping('my_gateway_refunded', '*', 'closed');
};
Signature:
registerPSOStatusMapping(
paymentStatus: string | '*', // Payment status ID or '*' for any
shipmentStatus: string | '*', // Shipment status ID or '*' for any
orderStatus: string // Resulting order status
): void
Reading Status Lists
Retrieve all registered statuses at runtime:
import {
getOrderStatusList,
getPaymentStatusList,
getShipmentStatusList
} from '@evershop/evershop/oms/services';
const orderStatuses = getOrderStatusList();
const paymentStatuses = getPaymentStatusList();
const shipmentStatuses = getShipmentStatusList();
Alternative: JSON Configuration
You can also define statuses in config files. This is useful for store-level customization rather than extensions:
{
"oms": {
"order": {
"paymentStatus": {
"my_custom_status": {
"name": "Custom Status",
"badge": "attention"
}
},
"psoMapping": {
"my_custom_status:*": "processing"
}
}
}
}
Status Properties
| Property | Type | Description |
|---|---|---|
name | string | Display name in the admin panel (required) |
badge | string | Visual style: default, success, warning, attention, critical, destructive, outline (required) |
isDefault | boolean | Whether this is the initial status for new orders |
isCancelable | boolean | When true, entering this status can trigger payment cancellation logic |
next | string[] | (Order status only) Allowed transitions from this status |
Reacting to Payment Status Changes
Hook into changePaymentStatus to perform actions when a payment status changes (e.g., cancel an authorization):
import { hookAfter } from '@evershop/evershop/lib/util/hookable';
export default async () => {
hookAfter('changePaymentStatus', async (order, orderId, status) => {
if (status !== 'canceled') return;
if (order.payment_method !== 'my_gateway') return;
await myProvider.cancelPayment(orderId);
});
};
Updating Statuses Programmatically
Update Payment Status
import { updatePaymentStatus } from '@evershop/evershop/oms/services';
await updatePaymentStatus(orderId, 'paid', connection);
// This automatically triggers order status resolution
Update Shipment Status
import { updateShipmentStatus } from '@evershop/evershop/oms/services';
await updateShipmentStatus(orderId, 'shipped', connection);
// This automatically triggers order status resolution
The order_status_updated Event
When the order status changes, an order_status_updated event is emitted:
// Event data:
{
orderId: number;
before: string; // Previous order status
after: string; // New order status
}
You can subscribe to this event to trigger custom logic (e.g., send a notification email):
export default async function (data) {
if (data.after === 'completed') {
// Send order completion email
}
}
See Also
- Payment Method Development — How to build payment gateways
- Configuration Guide — OMS configuration reference
- Events and Subscribers — React to status changes
Support us
EverShop is an open-source project that relies on community support. If you find our project useful, please consider sponsoring us.