Skip to content

[B2BTEAM-3732] Forward priceToken on addToCart (Pricing Fallback V2) - #185

Draft
wender wants to merge 3 commits into
masterfrom
feature/B2BTEAM-3732_forward-price-token-on-add-to-cart
Draft

[B2BTEAM-3732] Forward priceToken on addToCart (Pricing Fallback V2)#185
wender wants to merge 3 commits into
masterfrom
feature/B2BTEAM-3732_forward-price-token-on-add-to-cart

Conversation

@wender

@wender wender commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do? *

Pricing Fallback V2 (B2BTEAM-3732): captures the signed price returned by the search 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.

  • react/queries/product.gql and react/queries/productsByCategory.gql now request commertialOffer { priceToken } under sellers.
  • AutocompleteBlock captures the token of the default seller both on onSelect (single-SKU product) and on selectSku (SKU switch), and sends it on callAddUnitToCart.
  • CategoryBlock keeps a priceTokens map by SKU, filled when the quantity is set, and sends it on callAddToCart.

Note on the field name: the raw Catalog Search API exposes it as PriceToken (PascalCase), but search-graphql/search-resolver expose it as priceToken, which is what this code reads.

Two deliberate choices:

  • The token is read from the seller that is actually sent. In CategoryBlock the seller sent to the cart may be the sellerDefault or the first one in the list (pre-existing fallback), so the token is looked up by the already resolved sellerId instead of assuming the default. The token signs accountName + skuId + price + seller + salesChannel, so a token taken from a different seller would not validate against the item we send.
  • priceToken is only added to the payload when the search returns one (conditional spread), keeping it strictly optional, as agreed in the thread: an add to cart must never be blocked by a missing token, since the token only matters during a Pricing incident.

How to test it? *

Requires an account where the price signing feature flag is enabled on the Intelligent Search — b2bstoreqa already has it. Confirm with:

GET api.vtexcommercestable.com.br/api/intelligent-search/v1/product-search?an=b2bstoreqa

Depends on the Checkout apps — see Related to / Depends on.

  1. vtex link the app and open a page with quickorder.autocomplete.
  2. Select a product and inspect the product query response — sellers[].commertialOffer.priceToken should be present.
  3. Add it to the cart and inspect the addToCart mutation payload — the item should carry priceToken alongside id, quantity and seller. To tell our request apart from the PDP button in the Network tab: this app sends only { items }, while vtex.add-to-cart-button also sends marketingData and allowedOutdatedData.
  4. For a multi-SKU product, switch the SKU tag before adding and confirm the token belongs to the newly selected SKU.
  5. Repeat on a page with quickorder.category, including a product whose seller comes from the fallback (no sellerDefault), and confirm the token matches the seller that was sent.
  6. Regression: on an account without the flag enabled, both blocks must keep working with no priceToken in the payload.

End-to-end validation of the fallback itself can only be done by intentionally opening the circuit with the Pricing and checking that orders still close, as pointed out in the thread.

Describe alternatives you've considered, if any. *

  • Fetching the token in a separate, error-tolerant query, so a schema mismatch could not break the product lookup. Dropped in favour of the simpler final shape, since the field is now exposed on search-graphql.
  • Gating the field behind a public app setting. Dropped to avoid shipping a setting that would have to be removed later.

Related to / Depends on *

Search side is done: search-graphql@0.72.0 and search-resolver@1.106.0 were deployed on 2026-07-20, exposing priceToken. On the Intelligent Search the price signing is still behind a feature flag, enabled only for test accounts.

Blocked on vtex-apps/checkout-graphql#219 — do not merge before it ships. ItemInput on vtex.checkout-graphql has no priceToken, confirmed at runtime: sending it fails the mutation with Field "priceToken" is not defined by type ItemInput. Since the search already returns a token on flagged accounts, merging this first would break add to cart. checkout-graphql#219 proposes the field as optional, with the resolver untouched — the rest-spread already forwards it to PATCH /orderForm/{id}/items. The same dependency blocks the equivalent work on Store Framework (store-resources, add-to-cart-button, minicart, store-components) and on vtex-apps/sku-list (B2BTEAM-3748).

Out of scope, to be handled in a follow-up: TextAreaBlock and UploadBlock resolve SKUs through this app's own skuFromRefIds resolver (node/resolvers/search/index.ts), which relies on stockkeepingunitidsbyrefids + Checkout simulation — neither returns the token. Covering them needs a Catalog Search API call in node, a new field on ItemsSeller (graphql/types/Refids.graphql), a new outbound-access policy and propagation through ReviewBlock.

Worth confirming with the Checkout team: the token is valid for 30 minutes and cannot be renewed. In CategoryBlock it is captured when the category is opened, so a user browsing for a long time may send an expired token.

Pricing Fallback V2: fetch the signed price (commertialOffer.PriceToken)
in the search-graphql product queries and forward it as priceToken in the
addToCart payload, so the Checkout can close the cart while the Pricing is
unavailable.

Covers AutocompleteBlock and CategoryBlock. TextAreaBlock and UploadBlock
resolve SKUs through the app's own skuFromRefIds resolver, which has no
access to the token, and are left for a follow-up.

The token is only added to the payload when the search actually returns
one, keeping the payload unchanged while the field is not exposed yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@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 😞

@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 🎉🎉

The raw Catalog Search API exposes the field as PriceToken, but
search-graphql/search-resolver expose it as priceToken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.claude/ holds per-developer Claude Code settings (settings.local.json),
which should not be versioned.

Co-Authored-By: Claude Opus 5 <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.

1 participant