Skip to content

[B2BTEAM-3748] Request priceToken on the product query (Pricing Fallback V2) - #199

Open
wender wants to merge 1 commit into
masterfrom
feat/B2BTEAM-3748_expose-price-token-on-product-query
Open

[B2BTEAM-3748] Request priceToken on the product query (Pricing Fallback V2)#199
wender wants to merge 1 commit into
masterfrom
feat/B2BTEAM-3748_expose-price-token-on-product-query

Conversation

@wender

@wender wender commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request?

Request the signed price (priceToken) on the product query, so that the apps which read the product context can forward it on add to cart.

  • New react/fragments/priceToken.graphqlfragment PriceTokenFragment on Offer { priceToken }
  • react/queries/product.gql spreads it inside sellers { commertialOffer { … } }
  • CHANGELOG.md entry under Unreleased

The field is not added to CommertialOfferFragment, and that is the whole design of this PR — see below.

What problem is this solving?

Pricing Fallback V2 (B2BTEAM-3748, reference Slack thread): Intelligent Search now signs the price of each offer, and a storefront that forwards that signed price on add to cart lets the platform keep closing carts while the Pricing system is down. Portal already does this; Store Framework and FastStore are the remaining consumers (FastStore shipped it in 4.5.0).

This app is the first hop of the Store Framework chain: vtex.store/react/ProductContext.js consumes vtex.store-resources/QueryProduct, so whatever this query does not request never reaches vtex.product-context — and therefore never reaches any storefront app that wants to forward the token. It is item 1 of the activity list Wisney Cardeal mapped in the thread ("propagate priceToken in the store-resources queries").

Dependencies, so it is clear where this sits:

Layer Status
intsch price signing ✅ production since 2026-07-15, behind a per-account feature flag
vtex.search-resolver@1.106.0 / vtex.search-graphql@0.72.0 ✅ deployed 2026-07-20 — this is what makes priceToken resolvable here
vtex.store-resources this PR
vtex.checkout-graphql (ItemInput.priceToken) checkout-graphql PRrequired before this is useful, otherwise forwarding the token makes the mutation fail
Storefront consumers vtex.add-to-cart-button, vtex.minicart, vtex.store-components (SF); sku-list#17, quickorder#185 (B2B Suite)

We are not the owners of this app — this comes from the B2B side, which hit the gap while implementing the B2B Suite part, and is offered as a ready starting point rather than something to merge as is. It should not be merged before checkout-graphql accepts the field, otherwise any consumer that starts forwarding the token breaks its own add to cart.

Why a separate opt-in fragment instead of CommertialOfferFragment: that fragment is spread into 8 queries, and one of them — recommendationShelf.gql — resolves through @context(provider: "vtex.recommendation-graphql"), which declares its own Offer type:

type Offer {
  Installments: [Installment]
  Price: Float
  ListPrice: Float
  spotPrice: Float
  PriceWithoutDiscount: Float
  AvailableQuantity: Float
  Tax: Float
  taxPercentage: Float
  teasers: [Teaser]
  discountHighlights: [Discount]
  giftSkuIds: [String]
  RewardValue: Float
  PriceValidUntil: String
}

It has every field the shared fragment asks for and no priceToken, so adding the field there would make that document invalid against that provider. With an opt-in fragment, each query asks for the token only where its provider can resolve it, and nothing else in the app changes.

Scope kept deliberately small. Only product.gql opts in, because that is what feeds vtex.product-context and what the add-to-cart flows read. The other search-backed queries (productSearchV2/V3, products, productSuggestions, sponsoredProducts, productRecommendations) can opt in with a one-line spread when their consumers need it — we left them out on purpose, since the token is ~450 bytes per seller and shelf/search responses would grow (and get cached) for consumers that do not use it yet. kitItems was left out for the same reason.

How should this be manually tested?

Requires an account with price signing enabled on Intelligent Search (still behind a feature flag; the search team enables it on request) and search-graphql@0.72.0 or newer.

  1. vtex link this app and run the product query for any SKU — items[].sellers[].commertialOffer.priceToken should come back populated.
  2. Decode it and confirm the claims match the SKU, seller, price and sales channel of that offer.
  3. On an account without price signing, confirm the query still resolves with priceToken: null (the field exists in the schema, the value is simply absent).
  4. Confirm the recommendation shelf keeps working — it is the query that motivated the separate fragment.

Validated on b2bstoreqa / workspace pricetoken, with this app linked next to vtex.checkout-graphql (patched) and vtex.sku-list: the token reached vtex.product-context, the SKU list forwarded it on addToCart, and the cart was closed normally. Before the checkout-graphql change, the same flow failed with Field "priceToken" is not defined by type ItemInput — which is exactly why the two PRs have to land in that order.

Screenshots or example usage

Product query response on b2bstoreqa with the patched fragment (token truncated):

{
  "sellerId": "1",
  "commertialOffer": {
    "Price": 599,
    "priceToken": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjZkYTc3YzQ4…"
  }
}

Decoded claims — bound to the SKU, seller, price and sales channel of that very offer, valid for 30 minutes:

{ "price": 59900, "priceWithoutDiscount": 59900, "seller": "1", "id": "77777000", "accountName": "b2bstoreqa", "salesChannel": "1" }

Types of changes

  • Bug fix (a non-breaking change which fixes an issue)
  • New feature (a non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Requires change to documentation, which has been updated accordingly.

Pricing Fallback V2: expose the signed price returned by the search on
the product query, so the apps that read the product context can forward
it as `priceToken` on add to cart and Checkout can close the cart while
the Pricing is unavailable.

The field is added through a separate opt-in `PriceTokenFragment` instead
of `CommertialOfferFragment`, because that fragment is also spread into
`recommendationShelf.gql`, whose provider `vtex.recommendation-graphql`
declares its own `Offer` type without the field - adding it there would
invalidate that document on every account. Each query now opts in, so no
existing consumer changes and no other provider is asked for a field it
cannot resolve.

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

vtex-io-ci-cd Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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

vtex-io-docs-bot Bot commented Aug 6, 2026

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

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