Skip to content

Order Processing Workflow

This example shows how to build an e-commerce order processing workflow, including validation, parallel payment and inventory checks, shipping, and confirmation.

  1. Define Workflow with Retry Policy

    Start by defining the workflow and a default retry policy for its steps.

    import * as v from '@identity-flow/sdk/valibot';
    import { NonRetryableError, defineWorkflow } from '@identity-flow/sdk';
    // Assuming Valibot for schema
    // Define an input schema for the order
    const OrderInputSchema = v.object({
    orderId: v.string(),
    customerId: v.string(),
    items: v.array(v.object({ productId: v.string(), quantity: v.number() })),
    totalAmount: v.number(),
    shippingAddress: v.object({ street: v.string(), city: v.string(), zip: v.string() }),
    });
    export default defineWorkflow(
    'order-processing-example',
    {
    schema: OrderInputSchema,
    retries: { limit: 3, delay: '30 seconds', backoff: 'exponential' },
    },
    async (flow) => {
    // Workflow steps will go here
    },
    );
  2. Validate Order Details

    Perform initial validation on the order. flow.params is already validated by the workflow schema, but you might have custom business rule checks.

    // Inside the async (flow) => { ... }
    const validatedOrder = await flow.do('validate order business rules', async () => {
    if (!flow.params.items?.length) {
    // Throw a NonRetryableError if it's a business rule violation that shouldn't be retried.
    throw new NonRetryableError('Order must contain items');
    }
    // Perform other business rule validations if necessary...
    console.log('Order business rules validated for:', flow.params.orderId);
    return flow.params; // Return the validated (or transformed) order data
    });
  3. Process Payment & Check Inventory (Parallel)

    Use Promise.all to execute payment processing and inventory checking concurrently for efficiency.

    // Inside the async (flow) => { ... }
    const [paymentResult, inventoryStatus] = await Promise.all([
    flow.do('process payment', async () => {
    console.log('Processing payment for order:', validatedOrder.orderId);
    // Replace with actual payment gateway integration
    return processPayment(validatedOrder.customerId, validatedOrder.totalAmount);
    }),
    flow.do('check inventory', async () => {
    console.log('Checking inventory for order:', validatedOrder.orderId);
    // Replace with actual inventory check logic
    return checkInventoryAvailability(validatedOrder.items);
    }),
    ]);
    // Handle payment failure
    if (!paymentResult.success) {
    flow.error('Payment failed for order:', validatedOrder.orderId, paymentResult.failureReason);
    // Optionally, trigger a compensation logic, like voiding an authorization
    throw new NonRetryableError(`Payment failed: ${paymentResult.failureReason}`);
    }
  4. Arrange Shipping

    If payment and inventory checks are successful, proceed to arrange shipping.

    // Inside the async (flow) => { ... }
    await flow.do('arrange shipping', async () => {
    if (!inventoryStatus.available) {
    // Handle out-of-stock scenario - this might involve notifications or backorder logic
    flow.warn('Items not available for order:', validatedOrder.orderId);
    throw new NonRetryableError('Items not available for shipping.');
    }
    console.log('Arranging shipping for order:', validatedOrder.orderId);
    // Replace with actual shipping arrangement logic
    return arrangeShipment(validatedOrder.shippingAddress, validatedOrder.items);
    });
  5. Send Order Confirmation

    Finally, send a confirmation to the customer.

    // Inside the async (flow) => { ... }
    await flow.do('send order confirmation', async () => {
    console.log('Sending confirmation for order:', validatedOrder.orderId);
    // Replace with actual notification logic
    await sendConfirmationEmail(validatedOrder.customerId, validatedOrder.orderId, { paymentResult, inventoryStatus });
    });
    return {
    orderId: validatedOrder.orderId,
    status: 'ORDER_PROCESSING_COMPLETED',
    paymentId: paymentResult.transactionId
    };
import * as v from '@identity-flow/sdk/valibot';
import { NonRetryableError, defineWorkflow } from '@identity-flow/sdk';
// --- Mock external functions for demonstration ---
async function processPayment(customerId: string, amount: number) {
console.log(`Processing payment of ${amount} for customer ${customerId}`);
// Simulate payment gateway call
if (Math.random() < 0.1) return { success: false, failureReason: 'Insufficient funds' };
return { success: true, transactionId: 'txn-' + Math.random().toString(36).substring(7) };
}
async function checkInventoryAvailability(items: any[]) {
console.log(
'Checking inventory for items:',
items.map((i) => i.productId),
);
// Simulate inventory check
return { available: Math.random() > 0.05 }; // 95% chance items are available
}
async function arrangeShipment(address: any, items: any[]) {
console.log('Arranging shipment to:', address.city, 'for items:', items.length);
}
async function sendConfirmationEmail(customerId: string, orderId: string, details: any) {
console.log(`Sending order confirmation for ${orderId} to customer ${customerId}`);
}
// --- End mock functions ---
const OrderInputSchema = v.object({
orderId: v.string(),
customerId: v.string(),
items: v.array(v.object({ productId: v.string(), quantity: v.number() })),
totalAmount: v.number(),
shippingAddress: v.object({ street: v.string(), city: v.string(), zip: v.string() }),
});
export default defineWorkflow(
'order-processing-example',
{ schema: OrderInputSchema, retries: { limit: 3, delay: '30 seconds', backoff: 'exponential' } },
async (flow) => {
flow.log('Starting order processing for:', flow.params.orderId);
const validatedOrder = await flow.do('validate order business rules', async () => {
if (!flow.params.items?.length) {
throw new NonRetryableError('Order must contain items');
}
flow.log('Order business rules validated for:', flow.params.orderId);
return flow.params;
});
const [paymentResult, inventoryStatus] = await Promise.all([
flow.do('process payment', async () => {
flow.log('Processing payment for order:', validatedOrder.orderId);
return processPayment(validatedOrder.customerId, validatedOrder.totalAmount);
}),
flow.do('check inventory', async () => {
flow.log('Checking inventory for order:', validatedOrder.orderId);
return checkInventoryAvailability(validatedOrder.items);
}),
]);
if (!paymentResult.success) {
flow.error('Payment failed for order:', validatedOrder.orderId, paymentResult.failureReason);
throw new NonRetryableError(`Payment failed: ${paymentResult.failureReason}`);
}
flow.log(
'Payment successful for order:',
validatedOrder.orderId,
'TxnID:',
paymentResult.transactionId,
);
await flow.do('arrange shipping', async () => {
if (!inventoryStatus.available) {
flow.warn('Items not available for order:', validatedOrder.orderId);
throw new NonRetryableError('Items not available for shipping.');
}
flow.log('Arranging shipping for order:', validatedOrder.orderId);
return arrangeShipment(validatedOrder.shippingAddress, validatedOrder.items);
});
flow.log('Shipping arranged for order:', validatedOrder.orderId);
await flow.do('send order confirmation', async () => {
flow.log('Sending confirmation for order:', validatedOrder.orderId);
await sendConfirmationEmail(validatedOrder.customerId, validatedOrder.orderId, {
paymentResult,
inventoryStatus,
});
});
flow.log('Order confirmation sent for:', validatedOrder.orderId);
return {
orderId: validatedOrder.orderId,
status: 'ORDER_PROCESSING_COMPLETED',
paymentId: paymentResult.transactionId,
};
},
);