Skip to content

Natural add-to-cart flow (return-to-origin + cart summary + flashes) - #3

Merged
DanMat merged 1 commit into
mainfrom
feat/natural-cart-flow
Sep 3, 2026
Merged

Natural add-to-cart flow (return-to-origin + cart summary + flashes)#3
DanMat merged 1 commit into
mainfrom
feat/natural-cart-flow

Conversation

@DanMat

@DanMat DanMat commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Makes the shopping flow natural: add-to-cart returns you to the page you were on (server-composed, allow-listed origin — never an echoed path, per ADR 0026), with an ?added= flash. Adds a read-only cart summary for the header count pill, and ?notice= messages on failures.

What changed

  • StorefrontCart: return-to-origin (return=shop|product + filter fields), failure notices (unavailable/expired/empty/stock), summary() (count = Σ qty; never mints a cart).
  • StorefrontResolver: added resolved via CatalogReadPort->get (canonical {sku,name}, active-only, never reflects raw input), validated notice enum, cart_summary closure (section pages only).
  • Default templates: hidden return fields, flash bar, is-added state.

Security (reviewed green before build)

  • Open-redirect closed: target is fixed /shop / /shop/{rawurlencode(sku)} / /cart + http_build_query (URL-encodes everything). Regression tests included.
  • Cache-safety: the cart count is passed only into section PageViews, never the path-cached content pages — so a count can't leak across visitors.
  • added/notice never reflect raw request input (lookup + enum), and are escaped on render.

Checks

PHPStan (max) + phpunit (23 tests) + cs-fixer all green.

🤖 Generated with Claude Code

…ashes

Add-to-cart now redirects BACK to the page you were on (a server-composed,
allow-listed origin from the form's `return`=shop|product + filter fields —
never an echoed path/URL, per ADR 0026), with an `?added=` flash, so browsing
stays natural instead of bouncing to /cart every time.

- StorefrontCart: return-to-origin in mutating(); `?notice=` on failures
  (unavailable/expired) and checkout failures (empty/stock/expired); a
  read-only summary() (count = Σ line qty, total) that never mints a cart.
- StorefrontResolver: resolves `added` via CatalogReadPort->get (canonical
  {sku,name}, active-only — never reflects the raw query), a validated `notice`
  enum, and a `cart_summary` closure for the header pill (section pages only).
- Default templates: hidden return fields, flash bar, is-added state.

Security (reviewed green): open-redirect closed by fixed path prefixes +
http_build_query; count is section-page-only so it can't be baked into a
path-cached page; added/notice never reflect raw input. Regression tests added.

PHPStan max + phpunit (23) + cs-fixer green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DanMat
DanMat merged commit 7472668 into main Sep 3, 2026
2 checks passed
@DanMat
DanMat deleted the feat/natural-cart-flow branch September 3, 2026 13:27
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.

1 participant