Skip to content

Checkout Workflow Documentation

This document describes the createOrderAtomicWorkflow that processes customer orders in the AzharStore system.

Overview

The checkout workflow is an atomic transaction that processes orders through 8 sequential steps. Each step validates data, performs operations, and defines compensation logic for rollback in case of failure.

Workflow Steps

Step 1: validateStockStep

Purpose: Validates that all requested product variants have sufficient inventory.

Input

{
  items: Array<{
    variant_id: string
    quantity: number
  }>
}

Process - Queries Medusa inventory service for each variant - Compares requested quantity with available quantity - Throws error if any variant has insufficient stock

Output - Returns the validated items array

Error Handling - Throws structured error: { code: "INSUFFICIENT_STOCK", variant_id, requested, available } - No compensation (validation step)

Example Error

{
  "code": "INSUFFICIENT_STOCK",
  "variant_id": "variant_123",
  "requested": 5,
  "available": 2
}


Step 2: createCartStep

Purpose: Creates a new Medusa cart with synthetic email from phone number.

Input

{
  region_id: string
  email: string
}

Process - Creates a cart in Medusa - Uses synthetic email format: {phone_number}@azharstore.local - Associates cart with the specified region

Output - Returns the created cart object

Compensation - Deletes the created cart if workflow fails


Step 3: addLineItemsStep

Purpose: Adds all line items to the cart.

Input

{
  cart_id: string
  items: Array<{
    variant_id: string
    quantity: number
  }>
}

Process - Iterates through items array - Adds each item to the cart using Medusa cart service

Output - Returns the cart ID

Compensation - Handled by cart deletion in Step 2


Step 4: updateCartAddressStep

Purpose: Maps customer address fields to Medusa address format and updates cart.

Input

{
  cart_id: string
  customer: {
    name: string
    phone_number: string
    town: string
    address_road: string
    address_home: string
    address_block: string
  }
}

Field Mapping | Frontend Field | Medusa Field | |----------------|--------------| | name | first_name, last_name (split on space) | | phone_number | phone | | town | city | | address_road | address_1 | | address_home | address_2 | | address_block | postal_code | | (hardcoded) | country_code = "BH" |

Process - Splits customer name into first_name and last_name - Maps all address fields - Updates both shipping_address and billing_address

Output - Returns the cart ID

Compensation - None (address update is part of cart state)


Step 5: addShippingMethodStep

Purpose: Adds appropriate shipping method based on delivery type.

Input

{
  cart_id: string
  delivery_area_id: string
  shipping_method: "delivery" | "pick_up"
}

Process - If shipping_method === "pick_up": - Finds shipping option with price = 0 - Adds that option to cart - If shipping_method === "delivery": - Uses delivery_area_id as shipping option ID - Adds that option to cart

Output - Returns the cart ID

Compensation - Removes shipping method from cart if workflow fails


Step 6: createPaymentSessionStep

Purpose: Creates a payment session using the manual payment provider.

Input

{
  cart_id: string
}

Process - Calls Medusa cart service to create payment session - Uses manual provider (cash on delivery)

Output - Returns the cart ID

Compensation - Deletes payment session if workflow fails


Step 7: completeCartStep

Purpose: Completes the cart, converting it to an order.

Input

{
  cart_id: string
}

Process - Calls Medusa cart completion - Converts cart to order - Captures payment (for manual provider)

Output - Returns the created order object

Compensation - Cancels and archives the created order - Restores stock (if applicable)


Step 8: capturePaymentStep

Purpose: Captures the payment (for manual provider, this is automatic on completion).

Input

{
  order_id: string
  payment_id: string
}

Process - For manual payment provider, capture happens automatically on cart completion - This step is a placeholder for custom capture logic if needed

Output - Returns the order ID

Compensation - Voids the captured payment if workflow fails


Workflow Diagram

┌─────────────────────────────────────────────────────────────┐
│                    createOrderAtomicWorkflow                │
└─────────────────────────────────────────────────────────────┘
                    ┌──────────────────┐
                    │  validateStock   │
                    │    (Step 1)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │   createCart     │
                    │    (Step 2)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │  addLineItems    │
                    │    (Step 3)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │ updateCartAddr   │
                    │    (Step 4)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │addShippingMethod │
                    │    (Step 5)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │createPaymentSess │
                    │    (Step 6)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │  completeCart    │
                    │    (Step 7)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │ capturePayment   │
                    │    (Step 8)       │
                    └──────────────────┘
                    ┌──────────────────┐
                    │   Return Order   │
                    └──────────────────┘

Usage

Running the Workflow

The workflow is called from the POST /store/orders endpoint:

const { result: order } = await createOrderAtomicWorkflow(req.scope).run({
  input: {
    region_id,
    phone_number,
    customer,
    items,
    delivery_area_id,
    shipping_method,
  },
})

Input Schema

{
  region_id: string           // Medusa region ID
  phone_number: string        // Customer phone number
  customer: {
    name: string              // Full name
    phone_number: string      // Phone number
    town: string              // City/town
    address_road: string      // Street address line 1
    address_home: string      // Street address line 2
    address_block: string     // Block/area code
  }
  items: Array<{
    variant_id: string        // Product variant ID
    quantity: number          // Quantity (>= 1)
  }>
  delivery_area_id: string    // Shipping option ID
  shipping_method: string     // "delivery" or "pick_up"
}

Output Schema

{
  order: {
    id: string
    email: string
    status: string
    items: Array<{
      id: string
      variant_id: string
      quantity: number
      unit_price: number
      total: number
    }>
    shipping_address: {
      first_name: string
      last_name: string
      phone: string
      city: string
      address_1: string
      address_2: string
      postal_code: string
      country_code: string
    }
    billing_address: {...}
    total: number
    created_at: string
  }
}

Debugging

Common Issues

1. Insufficient Stock Error - Symptom: Workflow fails with INSUFFICIENT_STOCK error - Cause: Requested quantity exceeds available inventory - Fix: Check inventory levels in Medusa admin dashboard

2. Cart Creation Failure - Symptom: Workflow fails at Step 2 - Cause: Invalid region_id or database connection issue - Fix: Verify region exists in Medusa, check database connection

3. Shipping Method Not Found - Symptom: Workflow fails at Step 5 - Cause: Invalid delivery_area_id or no pickup option available - Fix: Check shipping options in Medusa admin dashboard

4. Payment Session Failure - Symptom: Workflow fails at Step 6 - Cause: Manual payment provider not configured - Fix: Ensure manual payment provider is enabled in medusa-config.ts

Debugging in Medusa Dashboard

  1. Navigate to http://localhost:9000/app
  2. Go to Orders section
  3. Check for failed orders or incomplete carts
  4. Review order details for error messages

Checking Workflow Logs

Workflow errors are logged to the Medusa backend logs:

docker compose logs -f medusa

Testing the Workflow

Use the POST /store/orders endpoint with test data:

curl -X POST http://localhost:9000/store/orders \
  -H "Content-Type: application/json" \
  -d '{
    "region_id": "reg_123",
    "phone_number": "+97312345678",
    "customer": {
      "name": "Test Customer",
      "phone_number": "+97312345678",
      "town": "Manama",
      "address_road": "Test Street",
      "address_home": "Building 1",
      "address_block": "123"
    },
    "items": [
      {
        "variant_id": "variant_123",
        "quantity": 1
      }
    ],
    "delivery_area_id": "ship_123",
    "shipping_method": "delivery"
  }'

Compensation Strategy

The workflow uses Medusa's compensation mechanism to ensure data consistency:

  • Step 2: Deletes cart on failure
  • Step 5: Removes shipping method on failure
  • Step 6: Deletes payment session on failure
  • Step 7: Cancels order and restores stock on failure
  • Step 8: Voids payment on failure

If any step fails, the compensation functions for previous successful steps are executed in reverse order, ensuring the system returns to its original state.

Performance Considerations

  • The workflow is atomic and runs synchronously
  • Each step makes database calls
  • Total execution time depends on database latency
  • Consider adding caching for inventory checks if performance issues arise

Security Notes

  • Phone numbers are used to generate synthetic emails
  • No sensitive customer data is stored beyond what Medusa requires
  • All operations are validated by Medusa's built-in services
  • Manual payment provider does not process actual payments