Skip to content

fix(checkout) Default to free shipping when a free rate is available - #1505

Merged
wcole1-godaddy merged 5 commits into
godaddy:mainfrom
anam-godaddy:fix/AIROBUILD-7255-free-shipping-default
Oct 20, 2026
Merged

wcole1-godaddy merged 5 commits into
godaddy:mainfrom
anam-godaddy:fix/AIROBUILD-7255-free-shipping-default

Conversation

@anam-godaddy

@anam-godaddy anam-godaddy commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Hosted checkout did not pre-select free shipping when a free rate was available (AIROBUILD-7255).

Cause. ShippingMethodForm auto-applies a rate with matchedMethod || firstMethod. matchedMethod is the form value or the draft order's shippingLines.requestedService. So any paid rate that was already applied — auto-applied earlier, or already on the draft order when checkout loads — stays selected over a $0 rate. The cheapest rate is only chosen when nothing is selected yet. A fresh checkout with no shipping line already worked.

This shows up when:

  • the draft order already has a paid shipping line (for example, the shopper returns to checkout), or
  • a paid rate was auto-applied and a $0 rate appears later (for example, after an address change).

Fix. The auto-apply now chooses, in order:

  1. A rate the shopper clicked in this checkout visit, if it is still offered.
  2. A $0 rate. A $0 rate already on the draft order is kept, so two free rates do not cause an extra apply.
  3. The previous behavior: the existing rate if still offered, else the cheapest.

When the free auto-apply fails, the form shows the rate the draft order still has, instead of leaving the free radio checked. The processed state is not reset, so the failed rate is not retried in a loop.

Known limits

  • The shopper's click is held in component memory. After a page reload or a return from a payment redirect, a paid rate the shopper chose earlier is switched to free if a free rate is offered.
  • If the shopper's picked rate disappears (address change) and later comes back, it is selected again.
  • Free shipping given as a coupon on a paid rate is not affected, because the rates stay paid.
  • Rates are not refetched when the subtotal changes. Refetch shipping on coupon application #1441 covers that.

Changeset

  • Changeset added (docs) — @godaddy/react patch

Test Plan

New file checkout-free-shipping-default.test.tsx:

Test Before fix After fix
Draft order has a paid line, a $0 rate is offered → free is checked and applied once fails passes
Paid rate auto-applied, then a $0 rate appears → free is applied once fails passes
Switch from an auto-applied paid rate to free → the order discount code is re-applied fails passes
Draft order has one free rate, another free rate sorts first → kept, no apply fails* passes
Free auto-apply fails → the paid rate on the draft order is shown fails* passes
No shipping line → free is applied (control) passes passes
Shopper clicked a paid rate, rates refresh with a free rate → paid rate kept passes passes

* Fails when only that part of the fix is removed.

Changed checkout-free-order.test.tsx: "switches to FreePaymentForm when selecting a free shipping rate…" reached "Pay now" only because the paid line stuck while a free rate was offered. It now clicks the paid rate first, then free, and still checks the switch to the free-order form.

  • npx vitest run src/components/checkout in packages/react: 741 passed
  • tsc --noEmit and Biome: clean

🤖 Generated with Claude Code

@anam-godaddy
anam-godaddy requested a review from a team as a code owner October 15, 2026 18:46
@changeset-bot

changeset-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f3ea791

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@godaddy/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@anam-godaddy anam-godaddy changed the title Default to free shipping when a free rate is available fix(checkout) Default to free shipping when a free rate is available Oct 15, 2026

@sbolinger-godaddy sbolinger-godaddy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major found, approving. A couple of small non-blocking notes below.

What I validated:

  • Read the diff along with the existing ShippingMethodForm effect, shouldApplyShippingMethod, and filterAndSortShippingMethods. The free rate only wins when the shopper hasn't picked a rate. A $0 rate already on the order is kept, so there's no extra apply. The free-shipping minimum-order rule still filters $0 rates before this logic runs.
  • A failed free-rate apply doesn't retry in a loop, because the processed state still holds the free rate. The click error path also restores the previous selection.
  • In checkout-api, there's no server-side default shipping line, so a paid line on the order at load always came from the client. That fits the explanation of the bug.
  • The GoDaddy express wallet sends its shipping lines in the confirm request rather than changing the draft order, so this change doesn't override a wallet choice.
  • vitest run src/components/checkout in packages/react: 741 passed.

Non-blocking:

  • The changeset says a pick is kept "for the rest of the checkout visit," but switching from Shipping to Local pickup and back also drops it. main behaves the same, since switching to pickup clears the shipping line, so it isn't a regression. Rewording the changeset would make it accurate.
  • If product wants a paid pick to survive a payment-redirect return, saving the clicked rate in sessionStorage (keyed by session ID) would be a cheap follow-up.

anam-godaddy and others added 3 commits October 16, 2026 09:50
The shipping method effect kept any already-applied rate that was still
listed, so a paid rate that was auto-applied or already on the draft order
won over a $0 rate. Prefer the shopper's explicit pick from this mount,
then a $0 rate, then the existing behavior.

AIROBUILD-7255

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prefer a $0 rate already on the draft order over another $0 rate that
sorts first, so no extra apply is sent. When the auto-apply of a free
rate fails, show the rate the draft order still has instead of leaving
the free radio checked.

AIROBUILD-7255

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pickup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anam-godaddy
anam-godaddy force-pushed the fix/AIROBUILD-7255-free-shipping-default branch from 1ff03da to a33afd2 Compare October 16, 2026 17:08

@wcole1-godaddy wcole1-godaddy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Merged main in (no conflicts); react typecheck + all tests pass locally.

Non-blocking notes:

  • Resetting the shopper's paid choice on reload is a reasonable trade-off given the draft order can't tell an auto-applied rate from a picked one, and it's called out in the changeset.
  • shipping-method.tsx: the free check uses cost?.value === 0 while filterAndSortShippingMethods sorts with cost?.value || 0, so a rate with no cost would sort first as free but not be selected as free. A shared isFreeRate helper would keep them aligned.

@wcole1-godaddy
wcole1-godaddy merged commit c564b12 into godaddy:main Oct 20, 2026
3 checks passed
@anam-godaddy
anam-godaddy deleted the fix/AIROBUILD-7255-free-shipping-default branch October 20, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants