Order State Machine Design 2026: Digital Goods में order lifecycle modelling
Order state machine किसी भी B2B fulfilment का heart है। सही design system को observable, debuggable, और audit-friendly बनाता है। Bad design impossible states और race conditions पैदा करता है जिन्हें महीनों तक chase करना पड़ता है।
FoxReload के Real Order States
FoxReload API के actual states (polling से देखे जाते हैं):
enum FoxReloadOrderStatus {
ACTIVE = 'active', // created, payment का wait
PAID = 'paid', // payment received
PROCESSING = 'processing', // supplier से codes fetch हो रहे हैं
COMPLETED = 'completed', // success — items[].externalData[] में codes
CANCELLED = 'cancelled', // terminal — cancelReason देखें
FAILED = 'failed', // terminal — items[].error देखें
}
// cancelReason values:
type CancelReason = 'payment_failure' | 'payment_expiration' | 'user_request' | null;
Allowed transitions:
active → paid | cancelled
paid → processing | cancelled | failed
processing → completed | failed
completed → (terminal)
cancelled → (terminal)
failed → (terminal)
FoxReload में webhooks नहीं हैं। State transitions जानने के लिए GET /api/orders/{id} poll करें।
आपके अपने System का FSM
आपके platform पर FoxReload states को mirror करें:
enum OrderState {
PENDING = 'pending', // customer ने order initiate किया
PAYMENT_PENDING = 'payment_pending', // payment flow में
FULFILLING = 'fulfilling', // FoxReload processing में
DELIVERED = 'delivered', // codes customer को दिखाए
CANCELLED = 'cancelled', // terminal
FAILED = 'failed', // terminal
}
Invariants
ALTER TABLE orders ADD CONSTRAINT chk_delivered_has_codes
CHECK (state != 'delivered' OR delivered_at IS NOT NULL);
ALTER TABLE orders ADD CONSTRAINT chk_cancelled_reason
CHECK (state != 'cancelled' OR cancel_reason IS NOT NULL);
XState या enum+switch?
Small/medium projects के लिए enum + switch काफी है:
async function transition(order: Order, event: OrderEvent): Promise<Order> {
const next = nextState(order.state, event.type);
if (!next) throw new Error(`invalid transition ${order.state} + ${event.type}`);
return db.tx(async (tx) => {
const updated = await tx.orders.update(order.id, {
state: next,
version: order.version + 1, // optimistic lock
}, { where: { version: order.version } });
await tx.audit.insert({
event_id: randomUUID(),
order_id: order.id,
from_state: order.state,
to_state: next,
actor: event.actor,
timestamp: new Date(),
});
return updated;
});
}
Complex flows (multi-supplier, partial-delivery) के लिए XState visual diagram और hierarchical states देता है।
Implementation comparison
| Approach | LoC | Visualisable | Type-safe | Performance |
|---|---|---|---|---|
| Boolean flags | 50 | No | No | Best |
| Enum + switch | 150 | No | Strong | Best |
| XState | 200+ | Yes (Inspector) | Strong | Good |
| Temporal workflow | 500+ | Yes | Strong | Good (async) |
Polling से FoxReload State Sync
async function syncOrderState(foxreloadOrderId: string) {
const resp = await fetch(
`https://public-api.foxreload.com/api/orders/${foxreloadOrderId}`,
{ headers: { 'X-API-Key': process.env.FOXRELOAD_KEY! } }
);
const order = await resp.json();
if (order.status === 'completed') {
const codes = order.items.flatMap((i: any) => i.externalData ?? []);
await transition(localOrder, { type: 'DELIVERED', codes });
} else if (order.status === 'cancelled' || order.status === 'failed') {
await transition(localOrder, {
type: 'FAILED',
reason: order.cancelReason ?? order.items.map((i: any) => i.error).join('; '),
});
}
}
CTA
FoxReload API GET /api/orders/{id} से current state और GET /api/orders/ list से history expose करता है — इसे अपने local FSM से sync रखें। Access पाएं।
