Skip to content

[Feat]: Allow tender-based discounts via payment instrument qualifiers in Checkout Session Update #137

@ACSchil

Description

@ACSchil

Is your feature request related to a problem? Please describe.

I’m working on the REST binding + Checkout Update flow and I don’t see a place for a Platform to indicate “what tender subtype was selected” in a way that a Business can use for tender-based discounts.

Concretely: as the Business, I want to offer discounts based on a card subtype / BIN group (e.g., certain debit/commercial/store-card programs). In many tokenized flows, the Business may not have access to PAN/BIN, so the Platform (or handler) is the party that can detect the subtype early.

Today, Checkout Update Request includes payment as a Payment Update Request, but that object is basically just selected_instrument_id + instruments[]. The base card instrument fields (brand/last4/etc.) don’t include a “subtype” or “qualifier” concept. As a result, there isn’t a standard way for the Platform to signal “selected tender qualifies for discount bucket X”.

Refs: https://ucp.dev/specification/reference/ (Checkout Update Request, Payment Update Request, Card Payment Instrument)

Describe the solution you'd like

Add a small, generic mechanism for the Platform to pass a coarse, business-defined indicator alongside the selected payment instrument during PUT /checkout-sessions/{id}.

One possible shape (example only):

  • Add an optional field on Payment Instrument (or Payment Instrument Base) like:
    • qualifiers: string[] (namespaced values, coarse buckets only), or
    • qualifiers: Array<{ issuer: string, value: string }> for explicit namespacing.

Example usage (JSON payload):

{
  "payment": {
    "selected_instrument_id": "pi_123",
    "instruments": [
      {
        "id": "pi_123",
        "handler_id": "com.example.pay",
        "type": "card",
        "brand": "visa",
        "last_digits": "4242",
        "qualifiers": [
          "com.target.tender.bin_group:debit_promo_a"
        ]
      }
    ]
  }
}

Important constraints I think we should document if we do this:

  • This is a coarse eligibility indicator (no raw BINs, no PAN fragments).
  • Values should be namespaced to avoid collisions.
  • Business remains authoritative: qualifier is an input signal, not a guarantee. At the time of the /complete call, a business may reject the call if the tender does not qualify.

Also: we should plan/spec the out-of-band coordination story: how the Business shares BIN ranges and the expected qualifier values to the Platform (and how often this changes, versioning, etc.). It can be “out of scope” for UCP to standardize the sharing protocol, but the spec should at least acknowledge it and set expectations.

Describe alternatives you've considered

  1. Use the Discounts extension discounts.codes as the indicator (Platform submits a pre-arranged code like com.target.tender.debit_promo_a).
    Pros: no schema change.
    Cons: “codes” reads like coupon/user-input semantics; tender eligibility feels like a different axis.

  2. Put subtype in handler-specific instrument schemas.
    Pros: doesn’t require changing core schemas.
    Cons: not interoperable; Business has to implement per-handler logic and it’s hard to make this a consistent capability across the ecosystem.

  3. Don’t send anything; Business figures it out.
    In practice this breaks down in tokenized flows where Business doesn’t have BIN visibility.

Additional context

Why I think this belongs in Checkout Update:

  • The Business often needs to reflect the discount in totals before “Complete Checkout”.
  • The Platform is already calling Update Checkout as the buyer selects fulfillment/payment.

If maintainers think this should be an extension instead of core, I’m happy to move the proposal in that direction — but I’d still like a standard place + pattern so multiple businesses/platforms don’t invent incompatible one-offs.

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions