E-Commerce Order
This example demonstrates a complete e-commerce order flow with shopping cart, checkout, payment, and inventory management.
Single Machine Version
A simplified version with one machine handling the order:
flow
machine: @order
scenario: add to cart
given:
@customer is logged in
cart is empty
on :add_item from @customer (api)
$items: array adds $item
$total: number increases by $item.price
expect:
= $items contains $item
on :remove_item from @customer (api)
$items removes $item
$total decreases by $item.price
expect:
= $total is correct
scenario: successful checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on :checkout from @customer (api)
? cart is valid
$order_id becomes uuid()
emit :payment_request to @payment with:
| field | value | validation |
| order_id | $order_id | required, string, valid uuid |
| amount | $total | required, number, greater than 0 |
order moves to #awaiting_payment
reply 201 created with:
| id | $order_id |
| status | current_state |
| total | $total |
otherwise
reply 400 bad request with:
| error | "CART_INVALID" |
| message | "Cart validation failed" |
expect:
= order is in #awaiting_payment
= reply status is 201
= reply.id equals $order_id
on :validation_failed
reply 400 with:
| errors | $errors |
on :payment_success from @payment
order moves to #paid
emit async :reserve_stock to @inventory
expect:
= order is in #paid
on :stock_reserved from @inventory
order moves to #fulfilled
emit :order_confirmed to @customer
send confirmation email
expect:
= order is in #fulfilled
= @customer received :order_confirmed
scenario: out of stock checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on :checkout from @customer (api)
? cart is valid
emit :payment_request to @payment with:
| field | value |
| order_id | $order_id |
| amount | $total |
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
emit async :reserve_stock to @inventory
on :out_of_stock from @inventory
emit :refund_request to @payment
order moves to #cancelled
emit :order_cancelled to @customer
expect:
= order is in #cancelled
= @customer received :order_cancelled
scenario: payment failed checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on :checkout from @customer (api)
? cart is valid
emit :payment_request to @payment with:
| field | value |
| order_id | $order_id |
| amount | $total |
order moves to #awaiting_payment
on :payment_failed from @payment
order moves to #payment_failed
emit :payment_error to @customer with:
| field | value |
| error | $error_message |
expect:
= order is in #payment_failed
= @customer received :payment_error
scenario: empty cart checkout
given:
@customer is logged in
cart is empty
on :checkout from @customer (api)
? cart is empty
reply 400 bad request with:
| error | "CART_EMPTY" |
| message | "Cart is empty" |
expect:
= reply status is 400
= reply.error equals "CART_EMPTY"Multi-Machine System
A production-ready version with separate machines for each domain:
flow
system: e-commerce checkout
machine: @order
scenario: order lifecycle
given:
@customer is logged in
cart has items
$total: number is 1200
on :checkout from @customer (api)
? cart is valid
emit :payment_request to @payment with:
| field | value | validation |
| order_id | $order_id | required, string, valid uuid |
| amount | $total | required, number, greater than 0 |
order moves to #awaiting_payment
expect:
= order is in #awaiting_payment
on :validation_failed
reply 400 with:
| errors | $errors |
on :payment_success from @payment
order moves to #paid
emit async :reserve_stock to @inventory with:
| field | value |
| order_id | $order_id |
| items | $items |
expect:
= order is in #paid
on :payment_failed from @payment
order moves to #payment_failed
emit :payment_error to @customer
on :stock_reserved from @inventory
order moves to #fulfilled
emit :order_confirmed to @customer
expect:
= order is in #fulfilled
= @customer received :order_confirmed
on :out_of_stock from @inventory
emit :refund_request to @payment
order moves to #cancelled
emit :order_cancelled to @customer
expect:
= order is in #cancelled
= @customer received :order_cancelled
machine: @payment
scenario: payment processing
on :payment_request from @order
process card with $amount
? card is valid
$transaction_id: string becomes "TXN-12345"
emit :payment_success to @order with:
| field | value |
| transaction_id | $transaction_id |
?
$error_message: string becomes "Card declined"
emit :payment_failed to @order with:
| field | value |
| error | $error_message |
on :refund_request from @order
process refund
emit :refund_complete to @order
machine: @inventory
scenario: stock management
on :reserve_stock from @order
? stock is available
reserve items
emit :stock_reserved to @order
?
emit :out_of_stock to @orderState Diagram
Lane Diagram
Testing Scenarios
Successful Checkout
flow
scenario: successful checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on :checkout from @customer (api)
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :stock_reserved from @inventory
order moves to #fulfilled
expect:
= order is in #fulfilled
= @customer received :order_confirmedPayment Failure
flow
scenario: payment fails
given:
@customer is logged in
cart has items
payment will fail
on :checkout from @customer (api)
order moves to #awaiting_payment
on :payment_failed from @payment
order moves to #payment_failed
expect:
= order is in #payment_failed
= @customer received :payment_errorOut of Stock
flow
scenario: out of stock after payment
given:
@customer is logged in
cart has items
inventory is empty
on :checkout from @customer (api)
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :out_of_stock from @inventory
order moves to #cancelled
expect:
= order is in #cancelled
= :refund_request was emitted to @payment
= @customer received :order_cancelledRefund Flow
flow
scenario: process refund
given:
order is in #fulfilled
$days_since_purchase: number is 5
on :request_refund from @customer (api)
? days since purchase < 7
? order total < 100
instant refund to wallet
order moves to #refunded
? order total >= 100
process credit card refund
order moves to #refund_pending
? days since purchase < 30
? customer has premium status
partial refund (75%)
order moves to #partially_refunded
?
partial refund (50%)
order moves to #partially_refunded
? days since purchase < 90
store credit only
$credit_amount becomes $order_total
emit :credit_issued to @customer
order moves to #credit_issued
otherwise
refund denied
emit :refund_rejected to @customer
with:
| reason | "Refund period expired" |
on :refund_processed from @payment
emit :refund_confirmation to @customer
order moves to #refunded
expect:
= @customer received notification