Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 231 additions & 1 deletion docs/specification/checkout-rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,230 @@ Follow-up calls after initial `fulfillment` data to update selection.
}
```

#### Update Payment

Prior to completing checkout, the Platform may provide the Business with selected payment instrument hints. These hints allow the Business to apply to the checkout session the expected benefits the selected payment instrument qualifies the Buyer for.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should come at this explanation as a feature of payment handlers, NOT from the perspective of a black hole platforms can leverage for out-of-band solutions.

As in, my payment handler lets me negotiate available qualifiers, and exposes that context to the agent to submit to the platform. It just so happens to give platforms and businesses an easy path to do out-of-band context sharing, but IMO we shouldn't be authoring this from that perspective.

Copy link
Author

Choose a reason for hiding this comment

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

I understand this POV. See #214 (comment) . Are you okay with us moving forward with this for now?


=== "Request"

```json
PUT /checkout-sessions/{id} HTTP/1.1
UCP-Agent: profile="https://platform.example/profile"
Content-Type: application/json

{
"id": "chk_123456789",

Choose a reason for hiding this comment

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

The Update Payment example uses PUT /checkout-sessions/{id} and includes "id": "chk_123456789" in the body, but the spec section doesn’t state what happens if the path {id} and body id differ. Add a normative rule: either (a) body id MUST match path {id} and mismatches MUST be rejected with a specific error, or (b) body id is ignored and the path is authoritative.

Copy link
Author

Choose a reason for hiding this comment

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

Great call-out. Because this PR feat did not introduce that potential conflict (i.e. there are other Update docs with this issue), I propose we create a separate issue to address that. Thoughts?

Thinking through the solution, it's probably to return a 400 or 422. And this would then be captured under https://ucp.dev/2026-01-23/specification/checkout-rest/#status-codes .

Choose a reason for hiding this comment

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

Sounds good. Please create a separate issue.

"buyer": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe"
},
"line_items": [
{
"item": {
"id": "item_123",
"title": "Red T-Shirt",
"price": 2500
},
"id": "li_1",
"quantity": 2
}
],
"fulfillment": {
"methods": [
{
"id": "shipping_1",
"type": "shipping",
"line_item_ids": ["item_123"],
"selected_destination_id": "dest_home",
"destinations": [
{
"id": "dest_home",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"selected_option_id": "express"
}
]
}
]
},
"payment": {
"instruments": [
{
"id": "pi_gpay_5678",
"handler_id": "gpay_1234",
"type": "card",
"selected": true,
"display": {
"brand": "mastercard",
"last_digits": "5678",
"rich_text_description": "Google Pay •••• 5678"
},
"eligibility_hints": [
"com.example.tender_a"
]
}
]
}
}
```

=== "Response"

```json
HTTP/1.1 200 OK
Content-Type: application/json

{
"ucp": {
"version": "2026-01-11",
"capabilities": {
"dev.ucp.shopping.checkout": [
{"version": "2026-01-11"}
]
},
"payment_handlers": {
"com.google.pay": [
{
"id": "gpay_1234",
"version": "2026-01-11",
"config": {
"allowed_payment_methods": [
{
"type": "CARD",
"parameters": {
"allowed_card_networks": ["VISA", "MASTERCARD", "AMEX"]
}
}
]
}
}
]
}
},
"id": "chk_123456789",
"status": "ready_for_complete",
"currency": "USD",
"line_items": [
{
"id": "li_1",
"item": {
"id": "item_123",
"title": "Red T-Shirt",
"price": 2500
},
"quantity": 2,
"totals": [
{"type": "subtotal", "amount": 5000},
{"type": "total", "amount": 5000}
]
}
],
"buyer": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe"
},
"totals": [
{
"type": "subtotal",
"amount": 5000
},
{
"type": "tax",
"amount": 400
},
{
"type": "total",
"amount": 5400
}
],
"links": [
{
"type": "terms_of_service",
"url": "https://merchant.com/terms"
}
],
"fulfillment": {
"methods": [
{
"id": "shipping_1",
"type": "shipping",
"line_item_ids": ["item_123"],
"selected_destination_id": "dest_home",
"destinations": [
{
"id": "dest_home",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["item_123"],
"selected_option_id": "express",
"options": [
{
"id": "standard",
"title": "Standard Shipping",
"description": "Arrives in 5-7 business days",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express Shipping",
"description": "Arrives in 2-3 business days",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
},
"payment": {
"instruments": [
{
"id": "pi_gpay_5678",
"handler_id": "gpay_1234",
"type": "card",
"selected": true,
"display": {
"brand": "mastercard",
"last_digits": "5678",
"rich_text_description": "Google Pay •••• 5678"
},
"eligibility_hints": [
"com.example.tender_a"
]
}
]
}
}
```

### Complete Checkout

If businesses have specific logic to enforce field existence in `buyer` and
Expand Down Expand Up @@ -730,6 +954,9 @@ place to set these expectations via `messages`.
"card_art": "https://cart-art-1.html",
"description": "Google Pay •••• 5678"
},
"eligibility_hints": [
"com.example.tender_a"
],
"billing_address": {
"street_address": "123 Main St",
"address_locality": "Anytown",
Expand Down Expand Up @@ -892,7 +1119,10 @@ place to set these expectations via `messages`.
"brand": "mastercard",
"last_digits": "5678",
"rich_text_description": "Google Pay •••• 5678"
}
},
"eligibility_hints": [
"com.example.tender_a"
]
}
]
}
Expand Down
13 changes: 7 additions & 6 deletions docs/specification/checkout.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,13 @@ ELSE IF requires_buyer_review is not empty
Standard errors are standardized error codes that platforms are expected to
handle with specific, appropriate UX rather than generic error treatment.

| Code | Description |
| :----------------------- | :------------------------------------------------------------------------- |
| `out_of_stock` | Specific item or variant is unavailable |
| `item_unavailable` | Item cannot be purchased (e.g. delisted) |
| `address_undeliverable` | Cannot deliver to the provided address |
| `payment_failed` | Payment processing failed |
| Code | Description |
|:---------------------------|:------------------------------------------------------|
| `out_of_stock` | Specific item or variant is unavailable |
| `item_unavailable` | Item cannot be purchased \(e\.g\. delisted\) |
| `address_undeliverable` | Cannot deliver to the provided address |
| `payment_failed` | Payment processing failed |
| `invalid_eligibility_hint` | Submitted payment is not eligible for hinted benefits |

Businesses **SHOULD** mark standard errors with `severity: recoverable` to
signal that platforms should provide appropriate UX (out-of-stock messaging,
Expand Down
28 changes: 28 additions & 0 deletions docs/specification/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,34 @@ within UCP: **Negotiation**, **Acquisition**, and **Completion**.
2. **Acquisition (Platform ↔ Payment Credential Provider):** The platform executes the handler's logic. This happens client-side or agent-side, directly with the payment credential provider (e.g., exchanging credentials for a network token). The business is not involved, ensuring raw data never touches the business's frontend API.
3. **Completion (Platform → Business):** The platform submits the opaque credential (token) to the business. The business uses it to capture funds via their backend integration with the payment credential provider.

### Payment Eligibility Hints

Prior to completing checkout, the Platform **MAY** provide the Business with selected payment instrument *hints*. These hints allow the Business to apply to the checkout session the expected benefits the selected payment instrument qualifies the Buyer for. This gives the user a more accurate preview of the final order total before they commit to the purchase.

**Example Benefits:**

- Co-branded Card Perks (e.g., instant discounts for using a specific brand's card).
- Issuer-Funded Promotions (e.g., statement credits).
- FX Fee Waivers for international transactions.
- Promotional APRs or specialized financing terms.
- Logistical Upgrades (e.g., expedited shipping for premium cardholders).

Payment instruments **MAY** include an `eligibility_hints` array: opaque, namespaced strings that hint benefit eligibility associated with the selected instrument. The meaning of `eligibility_hints` values, and how they are derived, is communicated between the Business and Platform out of band (for example, via offline agreement on BIN ranges or program identifiers).

**eligibility_hints Strings:**

- `eligibility_hints` strings **SHOULD** use reverse-domain naming to avoid collisions.
- `eligibility_hints` strings **MUST NOT** contain sensitive payment attributes such as PAN, BIN, PII, or user-unique identifiers.
- `eligibility_hints` strings **SHOULD** be coarse-grained program identifiers.

**eligibility_hints Semantics:**

- `eligibility_hints` are hints and **MUST NOT** be treated as proof of eligibility.
- The Business **MUST NOT** grant final or irreversible benefits solely due to `eligibility_hints`.
- The Business **MUST** determine benefits eligibility from the completion payment instrument and credential, not from `eligibility_hints`.
- The Business **SHOULD** return an error, using the [`invalid_eligibility_hint`](checkout.md#standard-errors), during checkout completion if the provided `eligibility_hints` do not match the final payment instrument's eligibility.
- When receiving `invalid_eligibility_hint`, the Platform **SHOULD** update `eligibility_hints`, and **MUST** present the user with an opportunity to review benefits changes (e.g. discounts, totals, etc.).
Copy link
Contributor

Choose a reason for hiding this comment

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

So does this mean, the platform should ask the user to select a different payment instrument or remove the eligibility_hint from their request and use the same instrument or are there other options for the platform?
I am trying to understand what are the valid responses from Platform once Business returns an error "invalid_eligibility_hint" and should we update the documentation to be clearer?


### Payment Handlers

Payment Handlers are **specifications** (not entities) that define how payment
Expand Down
3 changes: 2 additions & 1 deletion source/schemas/shopping/types/error_code.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"out_of_stock",
"item_unavailable",
"address_undeliverable",
"payment_failed"
"payment_failed",
"invalid_eligibility_hint"
]
}
9 changes: 9 additions & 0 deletions source/schemas/shopping/types/payment_instrument.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@
"display": {
"type": "object",
"description": "Display information for this payment instrument. Each payment instrument schema defines its specific display properties, as outlined by the payment handler."
},
"eligibility_hints": {
"type": "array",
"description": "Opaque, namespaced eligibility hint strings from the Platform that hint to the Business the benefits to apply at checkout time, based on the selected payment instrument.",
"uniqueItems": true,
"items": {
"type": "string",
"maxLength": 256
}
}
},
"additionalProperties": true,
Expand Down