Skip to content

[B2BTEAM-3748] Accept optional priceToken on ItemInput (Pricing Fallback V2) - #219

Open
wender wants to merge 1 commit into
mainfrom
feat/B2BTEAM-3748_accept-price-token-on-item-input
Open

[B2BTEAM-3748] Accept optional priceToken on ItemInput (Pricing Fallback V2)#219
wender wants to merge 1 commit into
mainfrom
feat/B2BTEAM-3748_accept-price-token-on-item-input

Conversation

@wender

@wender wender commented Aug 6, 2026

Copy link
Copy Markdown

What problem is this solving?

Pricing Fallback V2 (B2BTEAM-3748, reference Slack thread): Intelligent Search now signs the price of each offer and returns it as priceToken, so a storefront can forward that signed price on add to cart and the platform can keep closing carts even while the Pricing system is down.

Everything upstream of this app is already in production:

Layer Status
intsch price signing (VTEX.Signer sidecar) ✅ 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 — exposes priceToken on the Offer type
PATCH /api/checkout/pub/orderForm/{id}/items ✅ already accepts priceToken per order item (confirmed by Guilherme Schirmer in the thread)
vtex.checkout-graphql (ItemInput) this PR — the field does not exist, so the storefront cannot send it

This app is the blocker of the whole chain. Because ItemInput has no priceToken, any storefront that forwards the token gets its variable rejected before the request reaches the resolver:

GraphQL error: Variable "$items" got invalid value
{ id: 1, index: 0, seller: "1", quantity: 1, options: [], priceToken: "eyJhbGciOiJFUzI1NiIs…" }
at "items[0]"; Field "priceToken" is not defined by type ItemInput.

That error was reproduced on b2bstoreqa with price signing enabled and is currently blocking, in parallel:

  • vtex.store-resources — the product query cannot even be prepared to expose the field usefully (store-resources PR)
  • Store Framework — vtex.add-to-cart-button, vtex.minicart, vtex.store-components, per the activity list mapped by Wisney Cardeal in the thread
  • B2B Suite — sku-list#17, quickorder#185

We are not the owners of this app, so this PR is offered as a starting point for the Checkout Experience team rather than something to merge as is — Thaynan Nunes scheduled a spike for the sprint starting 2026-08-03 precisely to size this change. If the direction is right, it is already validated end to end (see below).

What the change is:

  • graphql/types/Item.graphql: optional priceToken: String on input ItemInput, with a docstring explaining the semantics.
  • node/typings/global.d.ts: matching optional priceToken?: string on OrderFormItemInput.

No resolver change was needed, and that is deliberate:

  • addToCart builds its REST payload with items.map(({ options, index, uniqueId, ...rest }) => ...), so priceToken already flows through rest into checkout.addItemPATCH /orderForm/{id}/items, which supports the field.
  • updateItems strips only id, so it forwards the token as well.
  • Checkout.addItem types items as Omit<OrderFormItemInput, 'uniqueId' | 'index' | 'options'>, so the new field is included automatically.

Backwards compatibility. The field is optional and input-only: when a storefront does not send it, the payload reaching the checkout REST API is byte-for-byte identical to today, and no existing caller has to change. The builder classified the change by itself during vtex link:

New GraphQL route types or parameters have been added since the previously published version of the app.
New features: ItemInput.priceToken was added.

i.e. an additive change, publishable as a minor. The token is also deliberately not exposed on the Item output type — it is signed data that only needs to travel inbound.

How should this be manually tested?

Validated on b2bstoreqa / pricetoken with three linked apps: this one, vtex.store-resources (product query requesting the field) and vtex.sku-list (forwarding it on add to cart). Requires an account with price signing enabled on Intelligent Search — it is still behind a feature flag, and the search team enables it on request.

  1. Confirm the search returns the token: GET /api/intelligent-search/v1/product-search?an={account}items[].sellers[].commertialOffer.PriceToken.
  2. Add an item to the cart from a storefront that forwards the token, or send the mutation directly with items[0].priceToken.
  3. Before this change: the request fails with the validation error above. After it: the mutation succeeds and the item is added normally.
  4. Decoding the forwarded token shows claims bound to the item that was added — {"price":390,"priceWithoutDiscount":390,"seller":"1","id":"1","accountName":"b2bstoreqa","salesChannel":"1"}, valid for 30 minutes.
  5. Regression: repeat without priceToken in the payload and confirm the behaviour is unchanged.

Note that the fallback itself is only exercised during a Pricing outage, so nothing about the signed price is observable in the addToCart response or in the orderForm. Christian Mutti's guidance in the thread is that the real end-to-end test is to intentionally open the circuit with the Pricing and watch carts still closing — that part is out of reach for us here.

Checklist/Reminders

  • Updated README.md — not applicable, no documented behaviour changes for existing callers.
  • Updated CHANGELOG.md.
  • Linked this PR to a Jira story — B2BTEAM-3748 (B2B side of the initiative).
  • Updated/created tests — happy to add coverage to node/__tests__/items-mutations.test.ts if the team wants the pass-through pinned by a test; the current change is schema-only.
  • Deleted the workspace after merging this PR — the pricetoken workspace is ours and will be unlinked regardless of what happens to this PR.

Type of changes

✔️ Type of Change
_ Bug fix
✔️ New feature
_ Breaking change
_ Technical improvements

Notes

Two things worth deciding with the initiative owners rather than in this PR:

  • Whether the token should ever be required. Both Christian Mutti and Guilherme Schirmer stated in the thread that it must stay optional — it is only used during an incident, and an add to cart without a token must keep working. This PR follows that.
  • Observability. There is no way to confirm from the API surface that a token arrived. The plan mentioned in the thread is metrics (a metrics PR on intsch, plus "instrument add-to-cart metrics with and without token" on the Store Framework list). If this app should emit anything on its side, that is a natural follow-up and we did not presume it here.

Pricing Fallback V2: let storefronts send the signed price returned by the
search along with the item, so the checkout REST API can close the cart
with that price while the Pricing system is unavailable.

The field is optional and needs no resolver change: `addToCart` strips
only `options`, `index` and `uniqueId` before calling
`checkout.addItem`, and `updateItems` strips only `id`, so `priceToken`
already reaches `PATCH /orderForm/{id}/items` through the rest spread -
and `PATCH .../items` already supports it. When the storefront does not
send the field, the payload reaching checkout is byte-for-byte the same as
before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wender
wender requested a review from a team as a code owner August 6, 2026 16:37
@vtex-io-ci-cd

vtex-io-ci-cd Bot commented Aug 6, 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

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