Skip to content

docs: document business-owned response values#519

Open
karangoel16 wants to merge 3 commits into
Universal-Commerce-Protocol:mainfrom
karangoel16:feat/saved-payment-instruments-extension
Open

docs: document business-owned response values#519
karangoel16 wants to merge 3 commits into
Universal-Commerce-Protocol:mainfrom
karangoel16:feat/saved-payment-instruments-extension

Conversation

@karangoel16

@karangoel16 karangoel16 commented Jun 15, 2026

Copy link
Copy Markdown

Motivation

Review feedback clarified that saved instruments are already possible to surface through existing UCP response entities, especially payment.instruments[]. The gap is that UCP does not clearly document the mechanic of businesses returning business-owned user state, rather than only echoing values supplied by the platform.

This PR now repurposes the original saved-instruments proposal into a documentation clarification:

  • businesses may populate existing response fields from authenticated user state
  • saved payment instruments should use existing payment.instruments[]
  • buyer and fulfillment data follow the same general pattern where the base capability supports them
  • no new dev.ucp.shopping.saved_instruments extension is introduced

Proposal

Add a new docs page, Business-Owned Response Values, explaining that UCP response objects may include values supplied by the business from its own user state when the request is user-authenticated through Identity Linking.

The page includes:

  • platform authentication vs. user authorization framing
  • guidance for saved payment instruments in payment.instruments[]
  • a sample signed, user-authenticated checkout request
  • a sample checkout response with a display-safe saved Shop Pay instrument
  • implementation guidance for opaque business-scoped IDs, ownership checks, and data minimization

Key design choices

  • Use existing base entities. Saved instruments are represented in payment.instruments[], not a new payment.saved_instruments[] field.
  • Docs-only. This PR removes the proposed saved-instruments extension schema and does not add new JSON schema fields.
  • Identity-linked response pattern. The business returns user-owned values only when the request carries appropriate user authorization.
  • Opaque business-owned references. Returned IDs are scoped to the business and resolved by the business server-side.
  • No generic tokenized-node abstraction yet. The broader abstraction for marking any tree node as a tokenized/reference value is left for a future proposal.

What this PR ships

  • docs/specification/business-owned-response-values.md — new docs page for business-owned response values
  • docs/specification/checkout.md — payment section links to the new guidance
  • mkdocs.yml — nav/search entry for the new page
  • Removes the previously proposed docs/specification/saved-instruments.md
  • Removes the previously proposed source/schemas/shopping/saved_instruments.json

Sample shape

Business returns a saved payment method through existing payment.instruments[]:

{
  "payment": {
    "instruments": [
      {
        "id": "saved_shop_pay_4242",
        "handler_id": "shop_pay_1234",
        "type": "shop_pay",
        "selected": true,
        "display": {
          "description": "Saved Shop Pay account",
          "email": "buyer@example.com",
          "last_digits": "4242"
        }
      }
    ]
  }
}

The id is an opaque business-owned reference for the authenticated user. The platform renders/selects it; the business verifies ownership and resolves it server-side.

Follow-ups intentionally not in this PR

  • A generic reference/tokenized-node abstraction for marking any response node as an opaque business-owned reference.
  • Standard metadata for saved/default/managed lifecycle state across response entities.
  • Saved addresses or saved instrument management APIs.

Category (Required)

  • Documentation: Updates to README, or documentations regarding schema or capabilities.

Checklist

  • I have followed the Contributing Guide (Conventional Commits title, no breaking changes).
  • I have updated the documentation.
  • My changes pass all local linting and formatting checks. I ran whitespace validation via git diff --check; I did not run the full docs build locally.
  • I have added tests that prove my fix is effective or that my feature works. Docs-only clarification; no behavior tests applicable.
  • (For Core/Capability) I have included/updated the relevant JSON schemas. No schema change in the repurposed PR.

Discussion welcome on

  1. Whether the new page belongs under Checkout Capability or a more general Core/Overview section.
  2. Whether the sample should use Shop Pay, card, or a more generic handler.
  3. Whether to add a short note to Cart and Fulfillment docs now, or keep this PR focused on checkout/payment examples.
  4. How to scope the follow-up generic reference/tokenized-node abstraction.

@google-cla

google-cla Bot commented Jun 15, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@karangoel16 karangoel16 force-pushed the feat/saved-payment-instruments-extension branch from 87da12d to be1335c Compare June 16, 2026 00:26
…_instruments)

Adds a response-only extension that surfaces the authenticated user's
saved payment instruments on cart and checkout responses under
`payment.saved_instruments[]`. Identity-gated by a new
`dev.ucp.shopping.saved_instruments:read` scope listed in
`identity_linking.config.scopes`. Mirrors the loyalty extension pattern.

Key design choices:
- Response-only (`ucp_request: omit`) — flows merchant → platform only
- No credential on the wire: schema enforces `"credential": { "not": {} }`.
  Platforms select via `payment.instruments[].id`; the business mints a
  checkout-bound TokenCredential per its declared payment handler.
- Per-instrument `default: true` flag instead of inferring from list order
- Business-scoped IDs; cross-merchant portability explicitly out of scope
@karangoel16 karangoel16 force-pushed the feat/saved-payment-instruments-extension branch from be1335c to 3ff52fe Compare June 16, 2026 01:10
Comment thread docs/specification/saved-instruments.md Outdated
* **Expiration awareness:** Surface "your saved card expires next month"
inline with cart/checkout.

A standalone capability that allows enumeration and management of saved

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove this

@karangoel16

Copy link
Copy Markdown
Author

Hi @raginpirate, @amithanda ,

wanted your opinion on the following, since I saw that you both have been actively contributing in this area of identity linking :)

cc: @jamesandersen

@jamesandersen

jamesandersen commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

@karangoel16 thanks for raising this PR proposing a way for users to tap into one of the key benefits of "logging into" the business - accessing saved payment methods to reduce checkout friction! This is directly referenced as one of the possible benefits ("saved addresses and credentials") of the identity linking capability and #358 also raises a very similar proposal.

With the recent identity linking updates in #354 landed, it feels like a good time to dig into the details for saved payment methods ... and potentially other checkout data as the docs allude to.

A few initial comments here:

  • This PR proposes a new dev.ucp.shopping.saved_instruments extension for checkout. However, I'm curious if we can simply re-use the ucp.payment.instruments. In fact, while the docs aren't real clear on this yet, the create checkout REST example does this very thing: returns a shop_pay instrument related to the shop pay handler
    • @raginpirate may be the best to confirm if this is an intended use of ucp.payment.instruments e.g. surface business-saved instruments to the platform for user selection and not only echo back details of platform-supplied instruments
    • If this is the right approach, it may warrant some discussion about some kind of standard payment handler / instrument schema for saved payment methods that can be reused across businesses so each business doesn't need to create a new handler just to represent an abstraction of a payment method it already has on file for which the only work on the platform side is presenting instances to the user for selection.
  • @amithanda / @igrigorik for the buyer contact info ... it seems that - subject to a dev.ucp.shopping.checkout:read scope via identity-linking - the business could populate ucp.buyer from it's own persisted buyer data on a create checkout response. Thoughts?
    • Saved shipping addresses (ucp.fulfillment.methods) feel potentially more tricky ...and probably best handled in a separate PR

@amithanda amithanda added the TC review Ready for TC review label Jun 26, 2026
@raginpirate

Copy link
Copy Markdown
Contributor

👍 You've got that exactly right @jamesandersen, saved instruments are already possible to surface back through UCP and that is intentional: same pattern for fulfillment, buyer, really all base entities of the protocol.

And you bring up a secondary point about specifically how we consider simplifying saved entities in the UCP spec when the platform really does not require knowing how to handle the spec of the instrument to use it (its already got its credential). I don't think some saved instrument payment handler is the right direction: I think theres actually a greater, and simpler, abstraction that UCP is missing to denote any node in the tree as "tokenized" (a lot like our tokenized credential) and drop-in an id + description of what it is when the platform doesn't need to know its full spec context. @rosew, our lead on payments platform at Shopify, was actually working on an internal proposal of this problem as it matters a lot for us thinking about PII shared for these kinds of stored entities. Lets see if we can accelerate that!

In the meantime, I do think this PR highlights that in general UCP does not document the mechanic of surfacing business-owned values rather than simply what the platform supplies. It could be worth repurposing this first PR to tackle that gap!

@karangoel16 karangoel16 changed the title feat: add Saved Payment Instruments extension (dev.ucp.shopping.saved_instruments) docs: document business-owned response values Jun 26, 2026
@karangoel16 karangoel16 force-pushed the feat/saved-payment-instruments-extension branch from 7854804 to dea0d34 Compare June 26, 2026 19:33
@karangoel16

karangoel16 commented Jun 26, 2026

Copy link
Copy Markdown
Author

@jamesandersen @raginpirate one protocol improvement surfaced while looking at the saved payment instrument example:

Today payment.instruments[] can represent both credential-bearing instruments and display-safe references to business-owned saved instruments. A platform can infer some state from whether credential is present, but it cannot reliably distinguish all cases in a machine-readable way:

  • saved business-owned reference: no credential, id resolves server-side
  • selected instrument that still needs credential acquisition: no credential yet
  • instrument with an attached credential/token: credential present

It may be worth adding an explicit enum in a future protocol revision, for example:

"credential_state": "reference"

with values like:

["reference", "required", "present"]

That would make the saved instrument case clear without relying on opaque id naming like saved_shop_pay_4242, and it would let platforms render/route payment flows more deterministically.

Please do let me know thoughts on the same ?

@jamesandersen

Copy link
Copy Markdown
Contributor

make the saved instrument case clear without relying on opaque id naming like saved_shop_pay_4242

Yep, trying to infer saved state from an id alone would certainly be a bad idea. Something explicit like a credential_state makes a lot more sense to help platforms distinguish from instruments already available vs those they may initiate per the payment handler configuration.

a greater, and simpler, abstraction that UCP is missing to denote any node in the tree as "tokenized"

...however, if I'm tracking @raginpirate accurately, I think he's prompting something that would be a bit more generic and thus applicable not only to credentials but also to other checkout PII that the business could surface to the platform in an identity-linked session

@rosew, our lead on payments platform at Shopify, was actually working on an internal proposal of this problem as it matters a lot for us thinking about PII shared for these kinds of stored entities. Lets see if we can accelerate that!

I look forward to this!

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

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants