Skip to content

Add managed Commerce carts and checkout web components - #1448

Open
wcole1-godaddy wants to merge 22 commits into
mainfrom
codex/managed-commerce-elements
Open

Add managed Commerce carts and checkout web components#1448
wcole1-godaddy wants to merge 22 commits into
mainfrom
codex/managed-commerce-elements

Conversation

@wcole1-godaddy

@wcole1-godaddy wcole1-godaddy commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Websites need a consistent way to manage multi-product carts and one-time purchases without rebuilding cart state or assembling React providers. This adds @godaddy/commerce, a framework-independent library over the existing Commerce APIs and an application's OAuth client. Shoppers review a managed cart drawer, then continue to hosted checkout.

  • Declares React and React DOM as 18.x || 19.x peers, with React 19 retained for development, so npm consumers share their host renderer.
  • Keeps typed quantities local until blur or Enter, preserving edits through pending mutations and restoring authoritative values after success or failure.
  • Adds gddy-add-to-cart, gddy-cart-button, gddy-buy-now, and gddy-payment-button, with optional React bindings and a headless client.
  • Creates a draft on the first add, adds the selected SKU ID and quantity using server-resolved pricing, and reuses the draft for later additions and cart checkout. Manages scoped persistence, serialized mutations, and server-provided totals. Buy now and application-resolved standalone charges preserve the saved cart.
  • Provides a compact right-side drawer with product images and fallback placeholders, complete variant/add-on selections, quantity and removal controls, a collapsed promo form, a fixed checkout footer, and the teal GoDaddy mark.
  • Redirects cart checkout, Buy now, and standalone payments to the hosted session URL. Removes inline checkout from the managed package and releases transient session state during handoff so failed navigation or browser Back does not leave the cart locked. Navigation does not establish payment success or clear the saved cart.
  • Adds a browser-compatible @godaddy/react/client export. Supporting @godaddy/react changes include embedded completion callbacks, session-scoped authentication storage, and style/portal scoping for that component's consumers; the managed Commerce elements use hosted checkout.
  • Documents the integration contract for developers and coding agents, including OAuth, attributes, SSR, customization, and authoritative payment confirmation. Includes changesets for both packages.

Validation of the latest changes:

  • Commerce package: 34 tests across five files, typecheck, Biome check, build, and git diff --check passed.
  • A separate React 18.3.1 consumer installed local package tarballs and verified shared React/ReactDOM resolution, useCart, the cart button, drawer rendering, and typed quantity commits. The regular Commerce suite runs on React 19.
  • Eight GraphQL operations and 26 freshly captured requests passed document validation and variable coercion against checked-in schemas. Orders and Checkout were also checked against the schema files in checkout-api main at 0d8c7d5e80c2413937f92c536c339266599a9655.
  • Coverage includes SKU additions, quantity changes/removal, discounts, all three hosted checkout inputs, request headers, appearance conversion, image fallbacks, selected variants, and navigation failure recovery.
  • The earlier full monorepo build/test run passed with 576 React tests, 19 Commerce tests, and 25 app-connect tests. That full run predates the latest Commerce changes; the targeted results above cover this update.

This remains a draft and the npm package is unreleased. The protected checkout repository owns CDN bundling, infrastructure, and release automation; this PR does not deploy a CDN release. Schema verification used synthetic data and snapshot contracts, including a downstream Orders snapshot. Real-store authorization, deployed API compatibility, and live payments remain untested. Draft creation still sends accepted but deprecated zero-valued totals; this is a non-blocking schema finding.

@changeset-bot

changeset-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d98fb13

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

This PR includes changesets to release 4 packages
Name Type
@godaddy/commerce Patch
@godaddy/react Patch
commerce-elements Patch
nextjs 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

@wcole1-godaddy
wcole1-godaddy marked this pull request as ready for review September 8, 2026 18:45
@wcole1-godaddy
wcole1-godaddy requested a review from a team as a code owner September 8, 2026 18:45
wcole1-godaddy and others added 18 commits September 9, 2026 15:50
- Hydrate once via a shared ready() promise that does not take the cross-tab write lock; mutations sync the saved ID and read the cart once inside the lock, with the SKU lookup in parallel
- Resolve the OAuth token before locking so a slow token callback cannot block other tabs
- Derive snapshot.status from pending/error; drop the unread checkoutSource field and the dead CART_CREATE_FAILED guard
- Release a saved cart that Commerce reports as not found via a GraphQL error
- Resolve relative return/success URLs against the page and raise CommerceError('INVALID_URL') from one shared helper used by the client and redirect
- Elements: replace localName checks with waitsForCart/badge hooks; render the cart count in ::part(count) so it survives slotted label text
- React: translate disabled/className into attributes so React 18 hosts work; useCart exposes the session as checkout, stable actions, and startCheckout
- Drawer: cache currency formatters and fall back on invalid locales; share itemCount
- Tests for hydration/locking, token-before-lock, not-found release, URL handling, derived status, cart badge, invalid hosted URL, and React attribute/action behavior

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A plain Vite page that configures @godaddy/commerce against a real Commerce
environment, renders the add-to-cart, cart, and buy-now elements, and shows
useCart() state. A dev-only Vite middleware performs the OAuth client-credentials
grant server-side and hands the page a short-lived token for checkout.

Documents that Commerce rejects localhost return/success URLs, so local runs
must override them with public URLs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Drawer: replace hard-coded surface, text, control, and border colors with
  --gddy-surface/--gddy-text/--gddy-subtle/--gddy-border/--gddy-muted/
  --gddy-font/--gddy-color-scheme (same defaults); stop redefining theme
  variables on .gddy-drawer so :root values from the host page win
- Buttons: chrome driven by --gddy-button-{background,border,shadow,padding,
  min-height,weight}, --gddy-font, badge colors; built-in hover/press
  feedback; `flat` attribute for a solid fill
- Client: optional createSession(input) delegate creates the hosted session on
  the merchant's server so no Commerce token reaches the browser; scope check
  tolerates omitted fields on server responses
- README: styling section with the variable table, parts, flat, and a shadcn
  token-bridge example; document createSession
- Example: appearance panel emits variables instead of ::part rules, adds
  drawer surface controls and a Dark preset; guard configureCommerce on HMR

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The stepper container owns the corner radius (capped at 14px) and clips its
  children; the inner +/- buttons are square, so the hover fill follows the
  container instead of drawing its own rounded shape
- Footer logo uses GoDaddy Everyday Teal 400 (#1BDBDB) per brand guidelines

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Whether a store accepts codes depends on the discounts GPA being enabled, and
there is no storefront-safe way to read that yet; the storefront API also
accepts unknown codes silently. Drop the form and its styles until a capability
lookup exists. The headless applyDiscount API is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Use a 64px image (matching the rendered img size) and stack heading, options,
and controls with a fixed 8px gap instead of bottom-aligning controls to an
80px min-height, so lines without variants have no dead space.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
addLineItemBySkuId stores the name we pass; sku.name is the catalog slug
(the-structured-blazer-m) while hosted checkout shows the label. Prefer the
label so the drawer and hosted checkout agree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The image column stretches to the content height (56px without variants, 80px
with one variant row), so its top meets the title and its bottom meets the
controls on every line. The price shares the title's 20px line height.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Stretching the image column to the content height distorted product art on
lines with variants. The thumbnail is a fixed 80px square again and the line
content centers against it, so plain lines sit balanced and one-variant lines
fill the row edge to edge.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A long product name now truncates with an ellipsis after two lines instead of
growing the row without limit. The full name stays available through the title
attribute and the control labels. Line content hangs from the top of the row
next to the thumbnail, so a tall line grows downward instead of floating the
image to the middle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each element tracks its own click action and reflects it as a busy attribute.
While busy, the label hides and a ring in ::part(spinner) takes its place, so
the button keeps its size; the button reads as working rather than unavailable.
Buy now and Pay now stay busy after a successful redirect and clear when the
page is restored from the back/forward cache. The drawer's checkout button
gets the same ring next to its updating text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Only the line items carry separators now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rows are set apart by their padding and thumbnails alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The gaps below the divider were 19, 10, 12, 17 and 13px. They are now 16px
around each block with 4px between the subtotal and its note.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
createSession is now the supported checkout path. It receives a SessionRequest
that includes the payment reference for pay(), so the server prices and creates
the session and the browser never holds a Commerce OAuth token. getAccessToken
still works but throws unless dangerouslyAllowBrowserToken is set, because any
token that can create a session can also act on the merchant's orders.

The commerce-elements example swaps its dev token endpoint for a
/api/commerce/checkout route that calls createCheckoutSession on the server
side of Vite. Docs, changeset, and tests follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pnpm build:cdn produces cdn/: a classic-script loader, commerce.js, that
imports a content-hashed runtime chunk relative to its own URL, plus hashed
immutable chunks for the drawer (with its own React), the redirect helper, and
the stylesheet, and a manifest with version, commit, and file list. The runtime
registers the elements, exposes window.GddyCommerce with release info, applies
window.gddyCommerceConfig, links the stylesheet, and fires gddy:ready.

cdn:serve serves cdn/ locally under /v1/ with production headers for chunks and
no-store for the loader. The commerce-elements example gains cdn.html, a plain
HTML storefront that loads the runtime from a script tag. The checkout
repository deploys the build from a git ref, so component updates ship without
an npm release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The drawer padded with var(--gddy-gutter) and nothing else, so it only had a
gutter when the page defined the variable, as the example's theme panel does.
Every use now falls back to 16px like the other tokens, and the README lists
the variable.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The runtime entry, loader, and CDN build move to the protected checkout
repository, which builds them from a git ref of this package. The README keeps
the browser contract (classic-script entry, window.GddyCommerce, gddy:ready,
gddy:error, immutable chunks) so both sides implement the same thing. The
example's cdn.html stays as a consumer of whatever URL VITE_COMMERCE_CDN_URL
names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

2 participants