Skip to content

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
    $items: array adds $item
    $total: number increases by $item.price

    expect:
      = $items contains $item

  on> :remove_item from @customer
    $items removes $item
    $total decreases by $item.price

  expect:
    = $total is correct

scenario: checkout

  given:
    @customer is logged in
    cart contains:
      | product | price |
      | Laptop  | 1200  |
    $total: number is 1200

  on> :checkout from @customer
    ? cart is valid
      emit :payment_request to @payment
        with:
          | order_id | $order_id |
          | amount   | $total    |
      order moves to #awaiting_payment
    ? cart is empty
      emit :error to @customer
        with:
          | message | "Cart is empty" |

    expect:
      = order is in #awaiting_payment

  on :payment_success from @payment
    order moves to #paid
    emit :reserve_stock to @inventory

    expect:
      = order is in #paid

  on :payment_failed from @payment
    order moves to #payment_failed
    emit :payment_error to @customer
      with:
        | error | $error_message |

  on :stock_reserved from @inventory
    order moves to #fulfilled
    emit :order_confirmed to @customer
    send confirmation email

    expect:
      = order is in #fulfilled

  on :out_of_stock from @inventory
    emit :refund_request to @payment
    order moves to #cancelled
    emit :order_cancelled to @customer

  expect:
    = order is in #fulfilled
    = @customer received :order_confirmed

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
      ? cart is valid
        emit :payment_request to @payment
          with:
            | order_id | string | $order_id |
            | amount   | number | $total    |
        order moves to #awaiting_payment

      expect:
        = order is in #awaiting_payment

    on :payment_success from @payment
      order moves to #paid
      emit :reserve_stock to @inventory
        with:
          | 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

    on :out_of_stock from @inventory
      emit :refund_request to @payment
      order moves to #cancelled

    expect:
      = order is in #fulfilled
      = @customer received :order_confirmed

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:
            | transaction_id | $transaction_id |
      ?
        $error_message: string becomes "Card declined"
        emit :payment_failed to @order
          with:
            | 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 @order

State Diagram

                        @order

┌───────────────────────────────────────────────────────────┐
│                                                           │
│  ┌────────┐   :add_item    ┌────────┐   :checkout         │
│  │ #empty │ ──────────────>│ #cart  │──────────────────┐  │
│  └────────┘               └───┬────┘                   │  │
│       ▲                       │                        │  │
│       │                  :clear                        │  │
│       └───────────────────────┘                        │  │
│                                                        │  │
│                                                        ▼  │
│                                           ┌────────────────────┐
│                                           │ #awaiting_payment  │
│                                           └─────────┬──────────┘
│                                                     │
│                                         ┌───────────┴───────────┐
│                                         │                       │
│                                  :payment_success        :payment_failed
│                                         │                       │
│                                         ▼                       ▼
│                                   ┌──────────┐          ┌────────────────┐
│                                   │  #paid   │          │ #payment_failed│
│                                   └────┬─────┘          └────────────────┘
│                                        │
│                           ┌────────────┴────────────┐
│                           │                         │
│                    :stock_reserved            :out_of_stock
│                           │                         │
│                           ▼                         ▼
│                   ┌────────────┐           ┌────────────┐
│                   │ #fulfilled │           │ #cancelled │
│                   └────────────┘           └────────────┘
│                                                           │
└───────────────────────────────────────────────────────────┘

Lane Diagram

┌─────────────┬─────────────┬─────────────┬─────────────┐
│  @customer  │   @order    │  @payment   │ @inventory  │
├─────────────┼─────────────┼─────────────┼─────────────┤
│             │             │             │             │
│ :checkout   │             │             │             │
│ ────────────┼──>          │             │             │
│             │             │             │             │
│             │ validates   │             │             │
│             │ cart        │             │             │
│             │             │             │             │
│             │ :payment_   │             │             │
│             │ request     │             │             │
│             │ ────────────┼──>          │             │
│             │             │             │             │
│             │             │ processes   │             │
│             │             │ card        │             │
│             │             │             │             │
│             │ :payment_   │             │             │
│             │ success     │             │             │
│             │ <───────────┼─────────────│             │
│             │             │             │             │
│             │ :reserve_   │             │             │
│             │ stock       │             │             │
│             │ ────────────┼─────────────┼──>          │
│             │             │             │             │
│             │             │             │ checks      │
│             │             │             │ stock       │
│             │             │             │             │
│             │ :stock_     │             │             │
│             │ reserved    │             │             │
│             │ <───────────┼─────────────┼─────────────│
│             │             │             │             │
│ :order_     │             │             │             │
│ confirmed   │             │             │             │
│ <───────────┼─────────────│             │             │
│             │             │             │             │
└─────────────┴─────────────┴─────────────┴─────────────┘

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
    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_confirmed

Payment Failure

flow
scenario: payment fails

  given:
    @customer is logged in
    cart has items
    payment will fail

  on> :checkout from @customer
    order moves to #awaiting_payment

  on :payment_failed from @payment
    order moves to #payment_failed

  expect:
    = order is in #payment_failed
    = @customer received :payment_error

Out of Stock

flow
scenario: out of stock after payment

  given:
    @customer is logged in
    cart has items
    inventory is empty

  on> :checkout from @customer
    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_cancelled

Refund Flow

flow
scenario: process refund

  given:
    order is in #fulfilled
    $days_since_purchase: number is 5

  on> :request_refund from @customer

    ? 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

Released under the MIT License.