B2B Checkout Rules Bypassed on Credit Card Path
Halden Supply runs B2B checkout for 850 customer accounts. Business rules: a purchase order number is mandatory on orders over $2,500; accounts with a credit hold must not submit; tax-exempt accounts must have a certificate on file. The current customization is a User Event beforeSubmit script on Sales Order that throws an error when a rule fails. Symptoms: customers see a generic 'There was an error processing your order' message and the cart is sometimes emptied; analytics show 18% abandonment at the review step; a sample of 40 orders found six without a PO number, all submitted by customers using the 'Pay by Credit Card' path; the same script also fires for orders created by the sales team in the UI, blocking legitimate rush orders. The checkout uses the standard SSP checkout application. There is a sandbox refresh in two weeks and a hard launch in five weeks.
1. Why does the User Event alone fail as a checkout control? What likely explains the six orders without a PO number?
Read the worked answer
Timing and feedback. A User Event on Sales Order is authoritative at record submission, but it runs late in the checkout flow. By then the customer has already chosen a payment method and may have been sent to payment authorization. Throwing an error at that point produces a generic failure and can leave the cart in an inconsistent state, which matches the emptied-cart symptom. The script has no structured way to tell the frontend which field failed or how to correct it.
Coverage gap. The six orders all came through the credit card path. The likely cause is a field-mapping gap: the PO number was stored in a checkout-specific field and copied to the Sales Order transaction body only on the invoice path. On the credit card path the checkout extension did not persist it, so the User Event saw an empty field and should have blocked the order. The fact that it did not suggests the rule was conditional on a payment-method check or a field that is absent on that path, or the script's condition treated the missing value as 'not applicable'. This is conjecture that must be confirmed by reproducing the credit card flow in a sandbox; the important point is that validating a field that was never mapped is not validation.
Wrong audience. The same script also blocks sales-team orders, proving that the rule is not scoped to the checkout channel. A control that fires for every channel without distinguishing web orders from assisted orders creates operational risk and encourages staff to bypass it.
Conclusion. The User Event is a necessary backstop, not a complete checkout control. It arrives too late for good UX and does not guarantee that the checkout extension captured the required data.
2. Where should each rule be enforced, and how should errors be returned to the shopper? Describe the layers and the order of enforcement.
Read the worked answer
Three layers. Enforce UX validation in the checkout frontend, authoritative checkout validation in the checkout SSP application's server-side submit path, and invariant enforcement in a Sales Order User Event for non-checkout channels. The frontend layer gives immediate field-level messages; the SSP layer blocks before payment authorization and preserves the cart; the User Event protects API, imports, and admin-created orders.
Rule order. Validate the purchase order presence and threshold first, then credit hold, then tax certificate. All three should run before any payment authorization, because authorizing a card for an order that will be rejected creates a refund or void problem. The checkout server should re-read the customer's current credit status rather than trusting a value cached in the browser session.
Error contract. Return a structured error object with a rule code, field identifier, and human-readable message, for example PO_REQUIRED on the PO field. The frontend maps that to the field and keeps the cart intact. A generic exception is not acceptable because it gives the shopper no recovery path. Include a correlation ID so support can connect the shopper's message to the server log.
Persistence. Save the PO number and certificate reference to transaction body fields on the Sales Order, with the same mapping on every payment path. The User Event then validates the persisted value, not a checkout-only transient. The User Event should also run a channel check so that assisted orders can use a documented override with permission and reason, while web orders cannot bypass the rule.
3. How should the team protect sales-team orders and validate the fix before the five-week launch?
Read the worked answer
Protect assisted orders. Branch on execution context, order source, or a dedicated channel field rather than on customer identity. For assisted orders, allow an override only with a permission and a required reason field, and record the override in the order. Do not simply exclude the sales role, because a web order placed by a salesperson for a customer should still follow the web rules. Test both directions: a web order must fail without a PO, and an assisted order with an approved override must succeed.
Reproduce first. In the sandbox, reproduce the credit card path end to end with a test account over the threshold and no PO. Confirm whether the field is missing from the order, whether the User Event condition is bypassed, and whether a payment authorization occurs before the error. Fix the mapping before adding more validation, or the new validation will fail for the same reason.
Test matrix. Cover invoice and credit card, PO below and above the threshold, credit hold true and false, tax-exempt with valid, expired, and missing certificate, and both a customer role and an administrator role. Include a negative payment test: confirm that an invalid order does not leave an authorized transaction. Check that the cart survives each validation error.
Launch controls. Use a feature flag to enable the new checkout validation for a small group of accounts first. Monitor abandonment at the review step, validation error codes, and orders created without a PO. A drop in abandonment with no rule bypasses is the success signal; a rise in one error code may indicate a misleading message rather than a correct block.