ESHOPMAN Workflow Alert: Preventing Orphaned Orders with Undefined IDs in Cart Completion
Understanding a Critical ESHOPMAN Workflow Challenge: Preventing Unpaid Orders
In the world of headless commerce, especially with platforms like ESHOPMAN, robust workflow management is paramount. ESHOPMAN, built on Node.js/TypeScript and seamlessly integrated with HubSpot CMS for storefront deployment, relies on sophisticated workflows to manage everything from cart completion to order fulfillment. Recently, our community identified a critical scenario within ESHOPMAN's core workflow engine that demands attention from developers and merchants alike: the potential for orders to be created without associated payments due to an intermittent race condition during cart completion.
The Intermittent Issue: Undefined Order IDs and Orphaned Orders
The problem manifests when ESHOPMAN's completeCartWorkflow.runAsStep(), a critical component for finalizing customer purchases, occasionally returns an { id: undefined } value to its parent workflow. This happens even when the underlying order record has been successfully created. If the parent workflow subsequently encounters a failure, the nested cart completion workflow's compensation process fails to trigger correctly, leaving a live order in the ESHOPMAN Admin API without any payment capture. This scenario not only impacts revenue but also creates a poor customer experience, as their payment might be authorized but never captured, leading to confusion.
This race condition is particularly observed when product variants are configured with manage_inventory: false. While intermittent, occurring in a small percentage of transactions, its impact is significant when it does happen.
Deep Dive into ESHOPMAN's Workflow Engine
Our analysis points to a specific timing window within ESHOPMAN's Node.js/TypeScript workflow engine. When manage_inventory: false, an internal step (analogous to reserveInventoryStep) exits synchronously without any I/O yield. This creates a race between the workflow's checkpoint saving mechanism and the outer locking process. The system's mergeCheckpoints function, under specific conditions, can overwrite a live 'invoking' or 'waiting' step state with an older 'idle' state. This premature state update causes the main execution loop to exit early, critically skipping the payment authorization step and subsequent wrapper steps.
As a result, the parent workflow receives an { id: undefined } for the completed cart. When the parent workflow then fails, the nested completeCartWorkflow remains in a 'waiting_to_compensate' state, preventing the order from being correctly rolled back or deleted. This leaves the merchant with an active order in their ESHOPMAN Admin API that has no associated payment.
Consider the following typical ESHOPMAN workflow structure:
createWorkflow("create-vendor-orders", (input) => {
acquireLockStep({ key: input.cart_id, timeout: 2, ttl: 120 })
// … custom checks …
const completed = completeCartWorkflow.runAsStep({ input: { id: input.cart_id } })
const { data: order } = getOrderDetailWorkflow.runAsStep({ input: { order_id: completed.id, fields: [...] } })
// … custom steps, then capture payment …
releaseLockStep({ key: input.cart_id })
})In the problematic scenario, completed.id would be undefined, causing the subsequent getOrderDetailWorkflow to fail, but the order itself might persist without payment.
Immediate Workaround for ESHOPMAN Developers
Until a permanent fix is deployed by the ESHOPMAN team, developers can implement a robust workaround to mitigate this risk. This involves taking control of the payment authorization process and implementing a post-failure cleanup mechanism:
- Pre-authorize Payment: Before initiating
completeCartWorkflow, explicitly call ESHOPMAN's payment module to authorize the payment session. This ensures that a payment hold is in place before any order is created. - Post-failure Order Cleanup: Implement a compensation or cleanup step in your custom workflow. After any failure, identify and cancel any orders associated with the cart that do not have a captured payment. This can be done by checking the
order_cartlink and examining the output of the nested workflow'screate-ordersstep.
This proactive approach ensures that no order is created without a held payment, and any orphaned orders are promptly identified and cancelled, maintaining data integrity and customer trust within your ESHOPMAN-powered storefront.
ESHOPMAN Team's Commitment
The ESHOPMAN team is actively investigating this critical bug to provide a comprehensive and permanent solution. We encourage developers to stay updated with ESHOPMAN's official announcements and version releases for the fix.
This insight highlights the complexities of distributed systems and the importance of robust error handling in headless commerce platforms like ESHOPMAN. By understanding these nuances, developers can build more resilient and reliable storefronts on HubSpot CMS.