Amounts and orders Required
Move orders through a fixed set of states
Give orders a fixed list of states and allowed transitions in one place, and refuse any other change with a conditional database update.
How to do it
A common set is awaiting payment, processing, paid, fulfilled, partially refunded, refunded, cancelled and payment failed. Write the transitions as data, as in states as types. Apply each one as an update that names the expected current state, as in conditional writes. Then two events cannot both apply. Store each transition with its time and cause, such as the provider event ID.
Example
const TRANSITIONS = {
awaiting_payment: ['processing', 'paid', 'payment_failed', 'cancelled'],
processing: ['paid', 'payment_failed'],
paid: ['fulfilled', 'partially_refunded', 'refunded'],
fulfilled: ['partially_refunded', 'refunded'],
partially_refunded: ['partially_refunded', 'refunded'],
}
async function moveOrder(db, orderId, from, to, cause) {
if (!TRANSITIONS[from]?.includes(to)) throw new Error(`Order cannot go from ${from} to ${to}`)
const result = await db.query('UPDATE orders SET state = $1 WHERE id = $2 AND state = $3', [to, orderId, from])
if (result.rowCount === 0) return false
await db.query('INSERT INTO order_transitions (order_id, from_state, to_state, cause) VALUES ($1, $2, $3, $4)', [orderId, from, to, cause])
return true
}
TRANSITIONS = {
"awaiting_payment": {"processing", "paid", "payment_failed", "cancelled"},
"processing": {"paid", "payment_failed"},
"paid": {"fulfilled", "partially_refunded", "refunded"},
"fulfilled": {"partially_refunded", "refunded"},
"partially_refunded": {"partially_refunded", "refunded"},
}
def move_order(tx, order_id: str, from_state: str, to_state: str, cause: str) -> bool:
if to_state not in TRANSITIONS.get(from_state, set()):
raise ValueError(f"Order cannot go from {from_state} to {to_state}")
result = tx.execute("UPDATE orders SET state = %s WHERE id = %s AND state = %s", (to_state, order_id, from_state))
if result.rowcount == 0:
return False
tx.execute(
"INSERT INTO order_transitions (order_id, from_state, to_state, cause) VALUES (%s, %s, %s, %s)",
(order_id, from_state, to_state, cause),
)
return True
How to check
A test tries every pair of states. Only the listed transitions succeed. Two identical paid events sent at once change the order once.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers payments playbook: https://agentwranglers.se/playbook/payments/amounts-and-orders/payment-state-machine.