How I Redesigned a Checkout Flow to Reduce User Friction
Every step in a B2C purchase funnel is moving users toward one moment: the payment. The homepage builds desire. The product page builds confidence. The cart confirms intent. By the time someone opens checkout, the persuasion work is mostly done.
That is what makes checkout friction so expensive. Every drop-off at checkout is a qualified buyer walking away. Not someone who was browsing, someone who had already decided to buy.
When we audited the checkout on this project, it had a specific kind of friction: it was asking users for information in the wrong order. This is the second post in the series; the first covered the homepage redesign.
What the old flow was doing
The existing checkout was two steps with a full page reload in between. Shipping details on step one. Payment on step two.
When we opened the step one audit, here is what the user saw in order: a full shipping address form, a coupon input, a fulfillment selector (delivery or pickup), a date selector, and multiple red policy blocks.
The fulfillment selector came after the address form. Which means pickup users were completing a full delivery address before anyone asked how they wanted their order.
That was the first problem. There were more.
The order summary on step one showed $0.00 overnight shipping before a valid address had been entered. On a cart worth over $2,000, showing an unverified total trains users that the numbers cannot be trusted.
Step one also had four separate red policy blocks in the primary task path: an "IMPORTANT MESSAGE," a delivery information block, a shipping rules block, and carrier delay warnings. All visible on page load. On mobile, users had to scroll through all of it before reaching the Next button.
On step two, Place Order appeared above the order summary on mobile. Users could tap Pay before scrolling down to verify their line items and total.
PayPal was buried below the Place Order button and a visible hCaptcha widget. Wallet users had to scroll past the primary commit action to find their payment method.
Discount codes were split across steps: coupon on step one, gift card on step two. A user applying a gift card would reach step two and wonder if they had missed something.
The dependency chain
The core problem was ordering. Fulfillment mode is not just another form field. It determines what address fields are needed, what delivery dates are available, what shipping costs apply, and what billing requirements follow. In the old flow, it appeared after the address. The form was asking users to answer question B before question A had been resolved.
This is an information architecture problem. When UI order contradicts the dependency chain, users either get confused or enter data the system cannot yet process correctly. The old flow had both: pickup users filling irrelevant fields, and shipping totals shown before the system had enough information to calculate them.
The redesign started by mapping the actual dependency chain and working backwards from it. Fulfillment mode drives everything. It goes first.
What we changed
Fulfillment first
The first interactive element on the redesigned checkout is a fulfillment selector: two equal-width card toggles, delivery or in-store pickup, with a gold border on the selected state. Delivery shows an address form and a delivery date picker. Pickup shows a date picker and an embedded map. No delivery address appears on the pickup path.
This is not a cosmetic change. The fulfillment choice gates every field that follows it. The UI order now matches the business logic.
Single-page, progressive checkout
The redesigned checkout is a single page. No page reload, no step tabs, no context switch between filling an address and entering payment. Sections become active as prerequisites are met. There is no "you missed something on step one" moment on a separate page.
Single-page progressive checkout reduces cognitive overhead in a specific way: the user never loses their mental model. They are always on checkout, not on step one of three. The layout does not change between filling an address and paying. Edits to earlier fields do not require navigating back to a previous step.
Payment section gated with visible locked state
The payment section is visible from the start of the checkout page but locked until fulfillment details are complete. The locked state shows the card fields dimmed with a message: "Complete your delivery details to continue to payment."
This does two things. It sets expectations early: users can see the available payment options (PayPal, Google Pay, Apple Pay, credit card) before they finish the form. And it eliminates the silent failure pattern where users reach a submit point and get an error they cannot trace back to a specific missing field. The gate is visible. The reason is stated. There is no surprise.
This applies Nielsen's first heuristic: visibility of system status. Users always know what state the checkout is in and what they need to do next.
Express checkout up top
PayPal Express appears immediately after the fulfillment selector, before the address form, with a clear "Or enter details manually" divider below it. Wallet users can complete checkout without filling a single form field.
In the old flow, PayPal was a radio option below the Place Order button on step two. Users who preferred PayPal had to scroll past the primary commit action to find it. The redesign treats all payment paths as peers and surfaces the fastest one first.
Honest shipping row
The old checkout showed "$0.00 overnight shipping" before any address had been entered or validated. The new checkout shows "Enter delivery address" on the shipping row until a real address is synced. The total reflects what is actually known, not what might be true if the address qualifies.
For a cart at this price point, this is a trust decision as much as a UX decision. Totals that change without explanation after an address is entered erode confidence at the worst possible moment.
Policy moved from ambient to contextual
The old step one had four red blocks of policy text in the primary task path, all visible on page load. The redesign moves policy to the point where it is relevant.
Delivery scheduling rules (Tuesday to Friday only, cutoff times, order estimates not guarantees) appear in a guide panel beside the date picker when the user opens the date selector. They read the policy while making the decision it applies to. The same information, at the right moment, without alarm color.
Shipping pricing rules (free shipping threshold, geographic exclusions) appear as a tooltip on the shipping row in the order summary. Accessible on demand, not in the primary task path.
The substance of the policies did not change. The placement and tone did. Moving policy from ambient anxiety to contextual education is the difference between a checkout that feels like a legal disclaimer and one that feels like a guided process.
The date picker enforces rules in the control
In the old flow, the date picker allowed users to select any date, including dates the business could not fulfill. The error surfaced on submit.
The redesigned date picker greys out unavailable days: Sundays, Mondays, Saturdays, dates outside the 15-day window, and dates that fall before the order cutoff. Users cannot select an invalid date. They do not need to be warned about it because they cannot choose it.
This is error prevention rather than error recovery. The calendar shows the rules spatially rather than describing them in text. A user reads "Tuesday to Friday delivery only" in the guide on the left while seeing the other days already greyed out on the right. Recognition and enforcement at the same moment.
Mobile: sticky footer
On mobile, a sticky footer bar persists at the bottom of the screen throughout the checkout. It shows the current total on the left and Place Order on the right. Tapping the total expands a price breakdown: subtotal, shipping, any applied discounts, and the final total.
The expanded view shows the breakdown, not the line items. At the commit point, the only question a user needs to answer is whether the total is correct. They already reviewed their items in the cart. Re-surfacing line items here would invite re-evaluation and pull users back into decision mode at exactly the moment they should be completing a transaction.
In the old flow, Place Order was mid-page on step two, and the order summary appeared below it. Users could tap Pay before scrolling to verify their total. The sticky bar keeps the current total and the commit action together, always visible, always in the thumb zone.
Bot protection
The old checkout had a visible hCaptcha widget placed below Place Order. An "I am human" checkbox in the critical commit path. The redesign uses Google reCAPTCHA v3. Unlike a checkbox, v3 requires no user interaction at all. It runs entirely in the background, assigns a risk score, and only flags sessions that look like bots. The badge it normally renders in the corner can be hidden via CSS, which we did, but Google's policy requires you to keep the text "This site is protected by reCAPTCHA" visible in the flow. We included that as a small inline note near the Place Order button. Bot protection without putting a barrier at the exact moment someone is trying to pay.
The visual system
The same color system from the homepage carries through here. The before checkout used green for Place Order and red for trust badges and policy text. Three competing high-attention colors in the commit zone.
The redesign uses one primary action color. Black for Place Order. Gold for selection states: the active fulfillment card, the selected calendar date, the free shipping progress bar. Green for confirmed states only: applied voucher, free shipping unlocked. Red does not appear in checkout except for inline form validation errors, where it belongs.
The visual result is a checkout that reads as guided completion rather than risk disclosure. The constraints are enforced in the controls. The reassurance is in the typography and spacing. The color does one job per token and no job is doubled.
The return user path
Everything above describes the first-time or guest checkout journey. For a logged-in user with a saved address and a saved payment method, most of it collapses.
On checkout load, we pre-select their default delivery address, default to the delivery fulfillment option, and pre-select their default card. The delivery default was a deliberate choice based on existing site data: pickup usage was a small fraction of orders, so we optimized the default state for the majority path. If they have no coupon to apply, checkout is a single action: review the pre-filled summary and tap Place Order. If they want to apply a voucher, it becomes two steps.
This is the same principle Amazon has built checkout around for years: for a returning customer, checkout should be a confirmation, not a re-entry. The information they gave you the first time should carry forward. Every field a returning user has to fill in again is friction you chose not to eliminate.
Checkout friction usually gets treated as a UI problem: inconsistent styling, unclear labels, too many fields. Those things matter, but they are usually symptoms.
The more useful question is sequence. In what order does a user actually need to make decisions to complete a purchase? Delivery preference depends on address. Delivery time depends on fulfillment method. Order total depends on both. When the interface reflects that dependency chain, users stop hitting dead ends. The constraints become guidance rather than friction.
If your checkout has similar sequencing problems or you are rebuilding on headless, let's talk about how I can help. Reach me on LinkedIn or at karishmagarg.dev@gmail.com.