Skip to content

feat(apps-nuvemshop): minicart cart loader and typed checkout errors - #632

Merged
JonasJesus42 merged 1 commit into
mainfrom
JonasJesus42/nuvemshop-cart
Oct 7, 2026
Merged

JonasJesus42 merged 1 commit into
mainfrom
JonasJesus42/nuvemshop-cart

Conversation

@JonasJesus42

@JonasJesus42 JonasJesus42 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

What

The minicart data for @decocms/apps-nuvemshop, using the Storefront API.

The cart stays in the browser (useCart, localStorage). Checkout goes through POST /checkouts. We don't use the theme's classic /comprar/ endpoint: after about 15 carts created in a few minutes, Nuvemshop returned 403 on every /comprar/ from that IP for over 40 minutes. A server-side proxy would send every buyer through the Worker's egress IPs, so one spike could block add-to-cart for the whole store. Details are in the apps-nuvemshop skill.

  • loaders/cart turns the client items into minicart lines: name, variant, image, price, list price, subtotal and maxQuantity, plus totals and savings.

    • Products are fetched by ids, up to 30 per call, through the existing SWR cache.
    • Quantities are clamped to stock, because POST /checkouts rejects a line above it with 422 checkout_rejected.
    • Missing, hidden and sold-out variants go to unavailable and are left out of the totals.
  • createCheckout now returns { error, message } instead of throwing. The success shape ({ checkoutUrl }) is unchanged.

    API response error
    422 coupon_rejected coupon_rejected
    422 checkout_rejected out_of_stock
    404 resource_not_found unavailable
    400 invalid_request invalid

    NuvemshopApiError carries the API's status and code.

  • Hooks: fetchCartDetails and useCartDetails() back the minicart; checkout() throws a CheckoutError with the code, so the UI can show the right message.

  • There is no coupon preview before checkout, because the API has none. The checkout validates the coupon.

Stacked on #631.

Verified

  • 103 tests pass; tsc, biome and skills:check are clean.
  • Live against the demo store:
    • a 50-unit line is clamped to the variant's stock of 17, and the same line without the clamp is rejected as out_of_stock;
    • a promotional item shows R$169.90 against a list price of R$199.90;
    • a removed variant lands in unavailable;
    • an invalid coupon returns coupon_rejected;
    • the created checkout's subtotal and total (R$3,698.10) equal the loader's subtotal.

🤖 Generated with Claude Code


Summary by cubic

Adds a minicart loader and typed checkout errors to @decocms/apps-nuvemshop, moving checkout to the Storefront API instead of the classic /comprar/ endpoint, which IP-blocks server-side proxies after a handful of carts.

  • loaders/cart resolves client-held useCart items into minicart lines (name, variant, image, prices, subtotal) with totals and savings, fetching products by ids, batched ≤30 per call and cached.
  • Quantities are clamped to stock so POST /checkouts doesn't reject a line with 422 checkout_rejected; missing, hidden, and sold-out variants move to unavailable and are excluded from totals.
  • createCheckout now returns { error, message } on failures instead of throwing, mapping API codes to typed errors (coupon_rejected, out_of_stock, unavailable, invalid); the success shape { checkoutUrl } is unchanged and NuvemshopApiError carries the status and code.
  • New fetchCartDetails/useCartDetails hooks back the minicart; checkout() throws a CheckoutError carrying the code so the UI can react.
  • There is no coupon preview before checkout because the API has none.

Written for commit e0cca9a. Summary will update on new commits.

Review in cubic Turn on auto-fix

@JonasJesus42
JonasJesus42 force-pushed the JonasJesus42/nuvemshop-account branch from 49db110 to e0fff42 Compare October 7, 2026 01:20
The cart stays client-side (useCart) and goes through the Storefront API —
the classic /comprar/ endpoint IP-blocks a server-side proxy after a few
carts, so it's not used.

- loaders/cart: resolves the client items into minicart lines (name,
  variant, image, price/list price, subtotal) and totals/savings, fetching
  the products by ids (≤30 per call, cached). Quantities are clamped to
  stock because POST /checkouts rejects a line above it (422
  checkout_rejected); missing and sold-out variants go to `unavailable`.
- createCheckout: returns { error, message } for coupon_rejected,
  out_of_stock (checkout_rejected), unavailable (404) and invalid (400)
  instead of throwing; success shape { checkoutUrl } is unchanged.
  NuvemshopApiError carries the API status/code.
- hooks: fetchCartDetails + useCartDetails for the minicart;
  checkout() throws CheckoutError(code).

Live: a 50-unit line is clamped to the variant's 17, the unclamped one is
rejected as out_of_stock, an invalid coupon maps to coupon_rejected, and the
created checkout's total equals the loader's subtotal.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@JonasJesus42
JonasJesus42 changed the base branch from JonasJesus42/nuvemshop-account to main October 7, 2026 01:20
@JonasJesus42
JonasJesus42 requested a review from a team October 7, 2026 01:20
@JonasJesus42
JonasJesus42 force-pushed the JonasJesus42/nuvemshop-cart branch from f767ad2 to e0cca9a Compare October 7, 2026 01:20
@JonasJesus42
JonasJesus42 merged commit 4c333b4 into main Oct 7, 2026
2 checks passed
@JonasJesus42
JonasJesus42 deleted the JonasJesus42/nuvemshop-cart branch October 7, 2026 01:20
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 7.77.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant