Skip to content

[B2BTEAM-3748] Forward priceToken on addToCart (Pricing Fallback V2) - #17

Open
wender wants to merge 1 commit into
masterfrom
feature/B2BTEAM-3748_forward-price-token-on-add-to-cart
Open

[B2BTEAM-3748] Forward priceToken on addToCart (Pricing Fallback V2)#17
wender wants to merge 1 commit into
masterfrom
feature/B2BTEAM-3748_forward-price-token-on-add-to-cart

Conversation

@wender

@wender wender commented Jul 30, 2026

Copy link
Copy Markdown

What does this PR do? *

Pricing Fallback V2 (B2BTEAM-3748): captures the signed price returned by the search (items[].sellers[].commertialOffer.PriceToken), read here from vtex.product-context, and forwards it as priceToken in the addToCart payload, so the Checkout can close the cart with that price even while the Pricing is unavailable.

  • catalogItemToCart.ts reads commertialOffer.PriceToken from the seller of the SKU being added and returns it as priceToken on the mapped cart item.
  • AddToCartButton.tsx (adjustItemsForMutationInput) sends priceToken in the vtex.checkout-resources mutation body, alongside id/index/seller/quantity/options.
  • CommercialOffer gains an optional PriceToken and OrderFormItemInput an optional priceToken.
  • Unit tests for the token resolution in mapCatalogItemToCart.

Two deliberate choices:

  • The token is read from the seller and the SKU that are actually sent. BuyButton/Wrapper.tsx resolves selectedSeller from productContext.selectedItem.sellers[0], while the item sent to the cart is built from the selectedItem prop — which, when the button is rendered per row by SkuBuyButton, is that row's SKU, not the product context one. So instead of reading the token straight off selectedSeller, it is looked up inside selectedItem.sellers by the already resolved sellerId. This way the token can never sign the price of a different SKU/seller than the one added to the cart (if no match is found, no token is sent). The pre-existing seller/price/listPrice resolution is left untouched.
  • priceToken is only added to the payload when the search returns one (conditional spread). While the field is not exposed, the payload is exactly the same as today, and the same applies if ItemInput on vtex.checkout-graphql does not accept priceToken yet.

Unlike quickorder#185, this app does not own the product query — it only consumes vtex.product-context — so nothing here breaks on accounts where the field is missing, and this PR is safe to merge before the field is exposed (it simply stays a no-op until then).

How to test it? *

Requires an account where CommertialOffer.PriceToken is exposed by vtex.search-graphql and the feature flag is enabled (e.g. storeframework), and a theme whose product query requests commertialOffer { PriceToken } so the field reaches vtex.product-context.

  1. vtex link the app and open a page with sku-list and its buy button.
  2. Inspect the product query response — items[].sellers[].commertialOffer.PriceToken should be present.
  3. Add a SKU to the cart and inspect the addToCart mutation payload — the item should carry priceToken alongside id, index, seller, quantity and options.
  4. With a multi-SKU list, add a different row and confirm the token belongs to that row's SKU/seller (or is absent, rather than borrowed from another SKU).
  5. Regression: on an account/theme without the field exposed, add to cart must keep working with no priceToken in the payload.
  6. cd react && yarn test — the mapCatalogItemToCart specs cover token present, token absent, and seller mismatch.

Describe alternatives you've considered, if any. *

  • Reading the token directly from selectedSeller. Simpler, but on the per-row SkuBuyButton usage selectedSeller comes from the product context item while the SKU sent comes from the row, so it could sign another SKU's price.
  • Fixing the selectedSeller resolution in Wrapper.tsx to always derive from the selectedItem prop. That would also correct price/listPrice/seller for the per-row case, but it is a behaviour change well beyond this ticket and deserves its own investigation.

Related to / Depends on *

  • Depends on CommertialOffer.PriceToken being exposed on search-resolver/search-graphql and reaching vtex.product-context (not the case yet, per Christian Mutti on 17/07 in the reference thread). Until then this is inert — mergeable, but not verifiable end to end.
  • Sibling implementation for the same initiative: quickorder#185 (B2BTEAM-3732).
  • Open question for the Checkout team (same as in quickorder): the token is valid for 30 minutes, cannot be renewed, and the search response is cached (~10 min API Cache + GraphQL cache), so part of the validity may already be consumed when it reaches the front end. Worth confirming whether an expired token is ignored (falling back to the Pricing) or returns an error.
  • Note: yarn lint already fails on master (prettier/prettier parsing errors on the optional chaining in SkuHighlights.tsx and SkuSpecifications.tsx, with the pinned Prettier 1.x). Unrelated to this PR — tsc --noEmit, the tests and ESLint on the touched files all pass.

Pricing Fallback V2: read the signed price (commertialOffer.PriceToken)
from the product context and forward it as priceToken in the addToCart
payload, so the Checkout can close the cart while the Pricing is
unavailable.

The token is read from the seller entry of the very SKU sent on
addToCart, since it signs that seller's price for that item, and it is
only added to the payload when the search actually returned one, keeping
the payload unchanged while search-graphql/product-context do not expose
the field yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vtex-io-docs-bot

Copy link
Copy Markdown

Beep boop 🤖

I noticed you didn't make any changes at the docs/ folder

  • There's nothing new to document 🤔
  • I'll do it later 😞

In order to keep track, I'll create an issue if you decide now is not a good time

  • I just updated 🎉🎉

@vtex-io-ci-cd

vtex-io-ci-cd Bot commented Jul 30, 2026

Copy link
Copy Markdown

Hi! I'm VTEX IO CI/CD Bot and I'll be helping you to publish your app! 🤖

Please select which version do you want to release:

  • Patch (backwards-compatible bug fixes)

  • Minor (backwards-compatible functionality)

  • Major (incompatible API changes)

And then you just need to merge your PR when you are ready! There is no need to create a release commit/tag.

  • No thanks, I would rather do it manually 😞

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