diff --git a/docs/specification/payment/extensions/ap2-mandates.md b/docs/specification/payment/extensions/ap2-mandates.md
index 1ba806d41..600f3395c 100644
--- a/docs/specification/payment/extensions/ap2-mandates.md
+++ b/docs/specification/payment/extensions/ap2-mandates.md
@@ -231,6 +231,13 @@ The checkout mandate **MUST** contain the full checkout response including the
`ap2.merchant_authorization` field. This creates a nested cryptographic binding
where the platform's signature covers the business's signature.
+When the Checkout aggregate total is provisional, that signed response includes
+both `totals[].amount_finality` and `payment.maximum_amount`. The Checkout
+mandate therefore records the amount shown to the Buyer and the maximum they
+approved. The Business MUST NOT authorize or collect more than that maximum
+under the mandate. A higher amount requires separate Buyer approval and cannot
+rely on the original mandate.
+
**Specification Boundary:** This extension defines *where* mandates are placed
in UCP requests and responses. The mandate credential structure (claims,
selective disclosure, key binding) is defined by the
diff --git a/docs/specification/payment/extensions/split-payments.md b/docs/specification/payment/extensions/split-payments.md
index 542bf32cb..b70fae73c 100644
--- a/docs/specification/payment/extensions/split-payments.md
+++ b/docs/specification/payment/extensions/split-payments.md
@@ -47,6 +47,10 @@ charged for each successfully-processed instrument in the same `amount`
field. Response `amount` is informational only; see "Response: Actual
Charges" below.
+Split Payments `amount` records the instrument's contribution to the Checkout.
+Order instruments use `authorized_amount` and `captured_amount` to report
+authorization and capture separately.
+
All `amount` values are expressed in `checkout.currency` minor units
(ISO 4217). Handlers using foreign-currency-denominated instruments
(e.g., a CAD gift card in a USD checkout) or non-currency instruments
@@ -284,14 +288,12 @@ Neither instrument includes `amount` — the business determines both.
"id": "pi_gc_1",
"handler_id": "example_handler_1",
"type": "gift_card",
- "credential": { "type": "gift_card", "token": "gc_abc123" },
"amount": 1000
},
{
"id": "pi_card_1",
"handler_id": "example_handler_1",
"type": "card",
- "credential": { "type": "card", "token": "tok_visa_xxxx" },
"amount": 4000
}
]
@@ -346,14 +348,12 @@ the rest.
"id": "pi_lp_1",
"handler_id": "example_handler_1",
"type": "loyalty",
- "credential": { "type": "loyalty", "token": "lp_abc123" },
"amount": 500
},
{
"id": "pi_card_1",
"handler_id": "example_handler_1",
"type": "card",
- "credential": { "type": "card", "token": "tok_visa_xxxx" },
"amount": 4500
}
]
@@ -409,21 +409,18 @@ credit card covers the remaining $45.
"id": "pi_gc_1",
"handler_id": "handler_gc",
"type": "gift_card",
- "credential": { "type": "gift_card", "token": "gc_abc123" },
"amount": 2500
},
{
"id": "pi_gc_2",
"handler_id": "handler_gc",
"type": "gift_card",
- "credential": { "type": "gift_card", "token": "gc_def456" },
"amount": 0
},
{
"id": "pi_card_1",
"handler_id": "handler_card",
"type": "card",
- "credential": { "type": "card", "token": "tok_visa_xxxx" },
"amount": 7500
}
]
@@ -454,14 +451,12 @@ per the atomic invariant):**
{
"id": "pi_gc_1",
"handler_id": "example_handler_1",
- "type": "gift_card",
- "credential": { "type": "gift_card", "token": "gc_abc123" }
+ "type": "gift_card"
},
{
"id": "pi_card_1",
"handler_id": "example_handler_1",
- "type": "card",
- "credential": { "type": "card", "token": "tok_visa_xxxx" }
+ "type": "card"
}
]
},
diff --git a/docs/specification/payment/extensions/terms.md b/docs/specification/payment/extensions/terms.md
index 6449b825a..9c84f4caa 100644
--- a/docs/specification/payment/extensions/terms.md
+++ b/docs/specification/payment/extensions/terms.md
@@ -535,6 +535,7 @@ disclosure moves with the term it governs:
{ "type": "total", "amount": 120000 }
],
"payment": {
+ "maximum_amount": 120000,
"accepted_term": {
"id": "pt_deposit_balance",
"title": "First night now, balance at check-in",
diff --git a/docs/specification/payment/guide.md b/docs/specification/payment/guide.md
index 111ebcc10..305e69ff6 100644
--- a/docs/specification/payment/guide.md
+++ b/docs/specification/payment/guide.md
@@ -574,13 +574,15 @@ Each variant has its own config schema tailored to its context:
| Schema | Description |
| :----------------------------------------------------------------------------------------- | :--------------------------------------------------------------- |
-| [`payment_instrument.json`](site:schemas/common/types/payment_instrument.json) | Base: id, handler_id, type, billing_address, credential, display |
+| [`payment_instrument.json`](site:schemas/common/types/payment_instrument.json) | Base: id, type, billing_address, credential, display |
| [`card_payment_instrument.json`](site:schemas/common/types/card_payment_instrument.json) | Extends base with display: brand, last_digits, expiry, card art |
UCP provides base schemas for universal payment instruments like `card`. Spec
authors **MAY** extend any of the base instruments to add handler-specific
display data or customize the credential reference. Handlers **MAY** define
multiple instrument types for different payment flows.
+Checkout adds `handler_id` and `selected` through
+`selected_payment_instrument`.
**Constraint Targets:**
diff --git a/docs/specification/reference.md b/docs/specification/reference.md
index 6b1fb7147..9c0e4d58d 100644
--- a/docs/specification/reference.md
+++ b/docs/specification/reference.md
@@ -25,6 +25,22 @@ within the UCP.
{{ auto_generate_schema_reference('.', 'reference', include_extensions=False) }}
+### Checkout Totals
+
+An ordered array of [Checkout Total](#checkout-total) entries.
+
+### Checkout Total
+
+{{ extension_schema_fields('../shopping/checkout.json#/$defs/checkout_total', 'reference') }}
+
+### Checkout Payment
+
+{{ extension_schema_fields('../shopping/checkout.json#/$defs/checkout_payment', 'reference') }}
+
+### Order Payment
+
+{{ extension_schema_fields('../shopping/order.json#/$defs/order_payment', 'reference') }}
+
## Type Schemas
{{ auto_generate_schema_reference('types', 'reference', include_extensions=False, base_dir='source/schemas/common') }}
@@ -35,6 +51,10 @@ within the UCP.
{{ extension_schema_fields('types/payment_instrument.json#/$defs/selected_payment_instrument', 'reference') }}
+### Order Payment Instrument {: #payment-instrument-order-payment-instrument }
+
+{{ extension_schema_fields('types/payment_instrument.json#/$defs/order_payment_instrument', 'reference') }}
+
### Pagination Request {: #pagination-request }
{{ extension_schema_fields('types/pagination.json#/$defs/request', 'reference') }}
diff --git a/docs/specification/shopping/checkout/index.md b/docs/specification/shopping/checkout/index.md
index 0aba1d4dd..a056ea4fd 100644
--- a/docs/specification/shopping/checkout/index.md
+++ b/docs/specification/shopping/checkout/index.md
@@ -1262,9 +1262,11 @@ this response root (e.g., `$.line_items[0]`). See
{{ schema_fields('types/message_warning', 'shopping/checkout') }}
-### Payment
+
-{{ schema_fields('payment', 'shopping/checkout') }}
+### Checkout Payment
+
+{{ extension_schema_fields('checkout.json#/$defs/checkout_payment', 'shopping/checkout') }}
#### Selected Payment Instrument
@@ -1282,9 +1284,87 @@ this response root (e.g., `$.line_items[0]`). See
{{ extension_schema_fields('capability.json#/$defs/response_schema', 'shopping/checkout') }}
-### Total {: #totals }
+
+
+### Checkout Totals
+
+`totals` is an ordered array of Checkout Total entries.
+
+#### Checkout Total
+
+{{ extension_schema_fields('checkout.json#/$defs/checkout_total', 'shopping/checkout') }}
+
+#### Final and Provisional Totals
+
+The aggregate Checkout total is the single `totals[]` entry whose `type` is
+`total`. It MAY include `amount_finality` as `final` or `provisional`; omission
+means `final`. Other total entries MUST NOT include `amount_finality`.
+
+Every Checkout sets the maximum amount the Buyer approves:
+
+* For a final total, the aggregate total is the approved maximum and
+ `payment.maximum_amount` MUST be omitted.
+* For a provisional total, `payment.maximum_amount` is required and MUST be
+ greater than or equal to the displayed aggregate total.
+
+JSON Schema cannot compare the two values. Implementations MUST ensure that
+`payment.maximum_amount` is at least the aggregate total.
+
+Before completing a provisional Checkout, the Platform MUST disclose that the
+amount may change up to `payment.maximum_amount` and obtain the Buyer's
+approval. Across all instruments, the Business MUST NOT authorize or collect
+more than that amount under the approval given at Checkout. Increasing the
+payment for a post-purchase Order change requires separate Buyer approval.
+
+`maximum_amount` applies to the Checkout as a whole rather than to each
+instrument. It is also separate from the amount a payment provider will still
+allow the Business to capture against an authorization. The sum of all
+historical authorization and capture records can be larger than
+`maximum_amount` because an authorization can be replaced or a reversed capture
+can be retried.
+
+Within UCP, the Business is responsible for enforcing the approved maximum. A
+payment provider may also enforce a limit passed to it by the Business, but UCP
+does not define that downstream exchange. The authorization and capture
+summaries on the Order are not enough to verify the limit: one reports current
+state and the other reports total activity. When AP2 is used, the Checkout
+mandate provides a signed record of the amount the Buyer approved.
+
+The Business MAY change the total and its finality while the Checkout remains
+open. Once Complete Checkout is accepted, the approved maximum no longer
+changes. The resulting Order MUST record that amount in
+`order.payment.maximum_amount`.
-{{ schema_fields('types/total_resp', 'shopping/checkout') }}
+For example, this Checkout can increase from $54.00 to no more than $60.00
+after completion:
+
+
+```json
+{
+ "ucp": {
+ "version": "{{ ucp_version }}",
+ "status": "success",
+ "payment_handlers": {}
+ },
+ "id": "chk_provisional_total",
+ "status": "ready_for_complete",
+ "currency": "USD",
+ "line_items": [ ... ],
+ "totals": [
+ { "type": "subtotal", "amount": 5000 },
+ { "type": "tax", "amount": 400 },
+ {
+ "type": "total",
+ "amount": 5400,
+ "amount_finality": "provisional"
+ }
+ ],
+ "links": [],
+ "payment": {
+ "maximum_amount": 6000
+ }
+}
+```
#### Rendering Contract
diff --git a/docs/specification/shopping/order/index.md b/docs/specification/shopping/order/index.md
index 76edff575..8e0b53897 100644
--- a/docs/specification/shopping/order/index.md
+++ b/docs/specification/shopping/order/index.md
@@ -26,7 +26,7 @@ it will be delivered, and what has happened since order placement.
### Key Concepts
-Orders have three main components:
+Orders have four main components:
**Line Items** — what was purchased at checkout:
@@ -39,6 +39,11 @@ Orders have three main components:
* **Expectations** — buyer-facing *promises* about when/how items will arrive
* **Events** (append-only log) — what actually happened (e.g. 👕 was shipped)
+**Payment** — what the Buyer approved and what the Business reports:
+
+* Records the maximum amount approved at Checkout
+* May include the amount authorized and captured for each instrument
+
**Adjustments** — post-order events independent of fulfillment:
* Typically money movements (refunds, returns, credits, disputes, cancellations)
@@ -100,6 +105,41 @@ Expectations can be split, merged, or adjusted post-order. For example:
(common examples: `processing`, `shipped`, `in_transit`, `delivered`,
`failed_attempt`, `canceled`, `undeliverable`, `returned_to_sender`)
+### Payment
+
+**Payment** records the payment instruments used for the Order and the maximum
+amount the Buyer approved at Checkout. The instruments may change after
+purchase, for example when an Order modification adds or replaces a payment
+method.
+
+Each instrument uses the shared Payment Instrument in its Order context:
+
+* `display` is required.
+* Checkout-only `handler_id` and `selected` fields are absent.
+* `credential` is request-only and is omitted from responses.
+* `authorized_amount` is the amount currently authorized, in the Order
+ currency's minor units. Released and expired authorizations are excluded;
+ remaining capture capacity is a separate concept.
+* `captured_amount` is the total amount successfully captured or collected, in
+ the Order currency's minor units, before refunds, credits, disputes, or
+ capture reversals. This measures capture rather than settlement or payout.
+
+Either amount may be omitted when it does not apply or is not reported.
+Platforms MUST accept `captured_amount` greater than `authorized_amount`; this
+can happen when capture above the authorized amount is supported or when an
+authorization is later released. Neither field sets a maximum for the
+instrument.
+
+Every Order includes `payment.maximum_amount`, which records the maximum amount
+the Buyer approved when completing Checkout. For a provisional Checkout total,
+it equals `checkout.payment.maximum_amount`; for a final total, it equals the
+aggregate Checkout total. It remains unchanged if the Buyer later approves an
+Order modification.
+
+Platforms MUST NOT compare `maximum_amount` with the sum of historical
+authorization or capture records. Replaced authorizations and retried captures
+can cause those sums to exceed the amount approved by the Buyer.
+
### Attribution
Businesses MAY surface a snapshot of the originating checkout's
@@ -133,6 +173,14 @@ fulfillment:
{{ schema_fields('order', 'shopping/order') }}
+### Order Payment
+
+{{ extension_schema_fields('order.json#/$defs/order_payment', 'shopping/order') }}
+
+### Order Payment Instrument
+
+{{ extension_schema_fields('../common/types/payment_instrument.json#/$defs/order_payment_instrument', 'shopping/order') }}
+
### Order Line Item
Line items reflect what was purchased at checkout and their current state.
@@ -277,6 +325,30 @@ Examples: `refund`, `return`, `credit`, `price_adjustment`, `dispute`,
}
]
},
+ "payment": {
+ "maximum_amount": 15342,
+ "instruments": [
+ {
+ "id": "pi_gift",
+ "type": "gift_card",
+ "captured_amount": 5000,
+ "display": {
+ "description": "Gift card",
+ "last_digits": "9821"
+ }
+ },
+ {
+ "id": "pi_card",
+ "type": "card",
+ "captured_amount": 10342,
+ "display": {
+ "brand": "visa",
+ "last_digits": "4242",
+ "description": "Visa ending in 4242"
+ }
+ }
+ ]
+ },
"adjustments": [
{
"id": "adj_1",
@@ -321,6 +393,7 @@ rounded to `119`, and the return credits `79 × 0.25 = 19.75`, rounded to
"checkout_id": "chk_bananas_1",
"permalink_url": "https://business.example.com/orders/bananas1",
"currency": "USD",
+ "payment": { "maximum_amount": 119 },
"line_items": [
{
"id": "li_bananas",
@@ -400,6 +473,7 @@ nominal steps `3 × 40 = 120`, settled `114`, delta `6` steps
"checkout_id": "chk_apples_1",
"permalink_url": "https://business.example.com/orders/apples1",
"currency": "USD",
+ "payment": { "maximum_amount": 240 },
"line_items": [
{
"id": "li_apples",
@@ -490,6 +564,7 @@ then holds exactly (`190 == 190`) and the line derives `fulfilled`:
"checkout_id": "chk_bananas_2",
"permalink_url": "https://business.example.com/orders/bananas2",
"currency": "USD",
+ "payment": { "maximum_amount": 158 },
"line_items": [
{
"id": "li_bananas",
diff --git a/docs/specification/shopping/order/mcp.md b/docs/specification/shopping/order/mcp.md
index ffd27f898..0ea706a6c 100644
--- a/docs/specification/shopping/order/mcp.md
+++ b/docs/specification/shopping/order/mcp.md
@@ -150,6 +150,7 @@ current-state snapshot of an order.
"checkout_id": "checkout_xyz789",
"permalink_url": "https://business.example.com/orders/abc123",
"currency": "USD",
+ "payment": { "maximum_amount": 4104 },
"line_items": [
{
"id": "li_shoes",
diff --git a/docs/specification/shopping/order/rest.md b/docs/specification/shopping/order/rest.md
index fbd7e6120..1a87e79d8 100644
--- a/docs/specification/shopping/order/rest.md
+++ b/docs/specification/shopping/order/rest.md
@@ -123,6 +123,7 @@ Returns the current-state snapshot of an order.
"checkout_id": "checkout_xyz789",
"permalink_url": "https://business.example.com/orders/abc123",
"currency": "USD",
+ "payment": { "maximum_amount": 4104 },
"line_items": [
{
"id": "li_shoes",
diff --git a/scripts/scaffolds/shopping_order_response.json b/scripts/scaffolds/shopping_order_response.json
index 4f631403d..3f90227f8 100644
--- a/scripts/scaffolds/shopping_order_response.json
+++ b/scripts/scaffolds/shopping_order_response.json
@@ -23,5 +23,8 @@
"totals": [
{ "type": "subtotal", "amount": 1000 },
{ "type": "total", "amount": 1000 }
- ]
+ ],
+ "payment": {
+ "maximum_amount": 1000
+ }
}
diff --git a/source/schemas/common/payment_split_payments.json b/source/schemas/common/payment_split_payments.json
index adcb77cef..5bc7044ff 100644
--- a/source/schemas/common/payment_split_payments.json
+++ b/source/schemas/common/payment_split_payments.json
@@ -12,7 +12,7 @@
"title": "Payment Instrument (Split Payments)",
"description": "Payment instrument extended with an optional per-instrument amount for split payments.",
"allOf": [
- { "$ref": "types/payment_instrument.json" },
+ { "$ref": "types/payment_instrument.json#/$defs/selected_payment_instrument" },
{
"type": "object",
"properties": {
diff --git a/source/schemas/common/payment_terms.json b/source/schemas/common/payment_terms.json
index f5e246d78..0c0b09861 100644
--- a/source/schemas/common/payment_terms.json
+++ b/source/schemas/common/payment_terms.json
@@ -74,8 +74,7 @@
"properties": {
"payment": {
"$ref": "#/$defs/order_payment",
- "description": "Payment details for the Order, including the accepted payment term.",
- "ucp_response": "optional"
+ "description": "Payment details for the Order, including the accepted payment term."
}
}
}
diff --git a/source/schemas/common/types/card_payment_instrument.json b/source/schemas/common/types/card_payment_instrument.json
index cf35f228b..4cc519106 100644
--- a/source/schemas/common/types/card_payment_instrument.json
+++ b/source/schemas/common/types/card_payment_instrument.json
@@ -23,7 +23,7 @@
},
"allOf": [
{
- "$ref": "payment_instrument.json"
+ "$ref": "payment_instrument.json#/$defs/base_payment_instrument"
},
{
"type": "object",
diff --git a/source/schemas/common/types/payment_instrument.json b/source/schemas/common/types/payment_instrument.json
index 830188577..cf0998bac 100644
--- a/source/schemas/common/types/payment_instrument.json
+++ b/source/schemas/common/types/payment_instrument.json
@@ -2,48 +2,53 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/common/types/payment_instrument.json",
"title": "Payment Instrument",
- "description": "The base definition for any payment instrument. It links the instrument to a specific payment handler.",
- "type": "object",
- "required": [
- "id",
- "handler_id",
- "type"
- ],
- "properties": {
- "id": {
- "type": "string",
- "description": "A unique identifier for this instrument instance. Typically assigned by the platform for instruments it collects. For a business-owned saved instrument returned on an identity-linked response, this identifier is assigned by the business; the platform MUST treat it as an opaque, business-scoped reference, and the business resolves it server-side when the buyer selects it."
- },
- "handler_id": {
- "type": "string",
- "description": "The unique identifier for the handler instance that produced this instrument. This corresponds to the 'id' field in the Payment Handler definition."
- },
- "type": {
- "type": "string",
- "description": "The broad category of the instrument (e.g., 'card', 'tokenized_card'). Specific schemas will constrain this to a constant value."
- },
- "billing_address": {
- "$ref": "postal_address.json",
- "description": "The billing address associated with this payment method."
- },
- "credential": {
- "$ref": "payment_credential.json"
- },
- "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."
- }
- },
- "additionalProperties": true,
+ "description": "Fields shared by payment instruments in Checkout and Order. Payment handlers add instrument-specific credential and display fields, while each capability adds the fields it needs for routing, selection, or reporting.",
+ "$ref": "payment_instrument.json#/$defs/base_payment_instrument",
"$defs": {
+ "base_payment_instrument": {
+ "type": "object",
+ "required": [
+ "id",
+ "type"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier for this instrument, assigned by the participant that introduces it. Identifiers assigned by another participant MUST be treated as opaque."
+ },
+ "type": {
+ "type": "string",
+ "description": "The broad category of the instrument (e.g., 'card', 'tokenized_card'). Specific schemas will constrain this to a constant value."
+ },
+ "billing_address": {
+ "$ref": "postal_address.json",
+ "description": "The billing address associated with this payment method."
+ },
+ "credential": {
+ "$ref": "payment_credential.json",
+ "description": "Credential used to execute payment. This field is request-only and MUST NOT be returned in responses.",
+ "ucp_response": "omit"
+ },
+ "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."
+ }
+ },
+ "additionalProperties": true
+ },
"selected_payment_instrument": {
"title": "Selected Payment Instrument",
- "description": "A payment instrument with selection state.",
+ "description": "A Checkout payment instrument with handler routing and selection state.",
"allOf": [
- { "$ref": "#" },
+ { "$ref": "payment_instrument.json#/$defs/base_payment_instrument" },
{
"type": "object",
+ "required": ["handler_id"],
"properties": {
+ "handler_id": {
+ "type": "string",
+ "description": "The unique identifier for the handler instance that produced this instrument. This corresponds to the `id` field in the Payment Handler definition."
+ },
"selected": {
"type": "boolean",
"description": "Whether this instrument is selected by the user."
@@ -51,6 +56,34 @@
}
}
]
+ },
+ "order_payment_instrument": {
+ "title": "Order Payment Instrument",
+ "description": "Payment instrument as returned on an Order, which may include current authorization and total captured amounts.",
+ "allOf": [
+ { "$ref": "payment_instrument.json#/$defs/base_payment_instrument" },
+ {
+ "type": "object",
+ "required": ["display"],
+ "properties": {
+ "authorized_amount": {
+ "$ref": "amount.json",
+ "description": "Amount currently authorized for this instrument, in the Order currency's minor units. Released and expired authorizations are excluded. Remaining capture capacity is a separate concept."
+ },
+ "captured_amount": {
+ "$ref": "amount.json",
+ "description": "Total amount successfully captured or collected for this instrument, in the Order currency's minor units, before refunds, credits, disputes, or capture reversals. This measures capture rather than settlement or payout."
+ }
+ },
+ "not": {
+ "anyOf": [
+ { "required": ["credential"] },
+ { "required": ["handler_id"] },
+ { "required": ["selected"] }
+ ]
+ }
+ }
+ ]
}
}
}
diff --git a/source/schemas/shopping/checkout.json b/source/schemas/shopping/checkout.json
index 983e42972..479109c36 100644
--- a/source/schemas/shopping/checkout.json
+++ b/source/schemas/shopping/checkout.json
@@ -4,6 +4,65 @@
"name": "dev.ucp.shopping.checkout",
"title": "Checkout",
"description": "Base checkout schema. Extensions compose onto this using allOf.",
+ "$defs": {
+ "checkout_total": {
+ "title": "Checkout Total",
+ "description": "Checkout pricing entry. Only the aggregate total may state whether its amount is final.",
+ "allOf": [
+ { "$ref": "../common/types/total.json" },
+ {
+ "type": "object",
+ "properties": {
+ "amount_finality": {
+ "type": "string",
+ "enum": ["final", "provisional"],
+ "description": "Whether the aggregate total may change after the Buyer completes the Checkout. Omission means `final`. When `provisional`, `payment.maximum_amount` is the most the Business may authorize or collect under the Buyer's approval of this Checkout.",
+ "ucp_request": "omit"
+ }
+ },
+ "if": {
+ "properties": {
+ "type": { "const": "total" }
+ },
+ "required": ["type"]
+ },
+ "else": {
+ "not": {
+ "required": ["amount_finality"]
+ }
+ }
+ }
+ ]
+ },
+ "checkout_totals": {
+ "title": "Checkout Totals",
+ "description": "Checkout pricing breakdown in which only the aggregate total may state whether its amount is final.",
+ "allOf": [
+ { "$ref": "../common/types/totals.json" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/$defs/checkout_total" }
+ }
+ ]
+ },
+ "checkout_payment": {
+ "title": "Checkout Payment",
+ "description": "Checkout payment details, including the maximum amount approved by the Buyer when the total is provisional.",
+ "allOf": [
+ { "$ref": "../common/types/payment.json" },
+ {
+ "type": "object",
+ "properties": {
+ "maximum_amount": {
+ "$ref": "../common/types/amount.json",
+ "description": "Maximum amount, across all payment instruments and in the Checkout currency's minor units, that the Business may authorize or collect under the Buyer's approval of this Checkout. Required for a provisional total and omitted for a final total, where the aggregate total is the approved maximum.",
+ "ucp_request": "omit"
+ }
+ }
+ }
+ ]
+ }
+ },
"type": "object",
"required": [
"ucp",
@@ -81,7 +140,7 @@
"ucp_request": "omit"
},
"totals": {
- "$ref": "../common/types/totals.json",
+ "$ref": "#/$defs/checkout_totals",
"description": "Different cart totals.",
"ucp_request": "omit"
},
@@ -127,7 +186,7 @@
"ucp_request": "omit"
},
"payment": {
- "$ref": "../common/types/payment.json",
+ "$ref": "#/$defs/checkout_payment",
"ucp_request": {
"create": "optional",
"update": "optional",
@@ -139,5 +198,38 @@
"description": "Details about an order created for this checkout session.",
"ucp_request": "omit"
}
- }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "totals": {
+ "contains": {
+ "properties": {
+ "type": { "const": "total" },
+ "amount_finality": { "const": "provisional" }
+ },
+ "required": ["type", "amount_finality"]
+ }
+ }
+ },
+ "required": ["totals"]
+ },
+ "then": {
+ "properties": {
+ "payment": {
+ "required": ["maximum_amount"]
+ }
+ },
+ "required": ["payment"]
+ },
+ "else": {
+ "properties": {
+ "payment": {
+ "not": { "required": ["maximum_amount"] }
+ }
+ }
+ }
+ }
+ ]
}
diff --git a/source/schemas/shopping/order.json b/source/schemas/shopping/order.json
index f5ade18a9..7e79da6ee 100644
--- a/source/schemas/shopping/order.json
+++ b/source/schemas/shopping/order.json
@@ -19,6 +19,25 @@
"description": "URL where merchant sends order lifecycle events (webhooks)."
}
}
+ },
+ "order_payment": {
+ "title": "Order Payment",
+ "description": "Payment details returned on the Order, including the maximum amount the Buyer approved at Checkout.",
+ "type": "object",
+ "required": ["maximum_amount"],
+ "properties": {
+ "instruments": {
+ "type": "array",
+ "items": {
+ "$ref": "../common/types/payment_instrument.json#/$defs/order_payment_instrument"
+ },
+ "description": "Payment instruments used on the Order."
+ },
+ "maximum_amount": {
+ "$ref": "../common/types/amount.json",
+ "description": "Maximum amount the Buyer approved when completing the Checkout, in the Order currency's minor units. For a provisional Checkout total, this equals `checkout.payment.maximum_amount`; for a final total, it equals the aggregate Checkout total. It remains unchanged if the Buyer later approves an Order modification."
+ }
+ }
}
},
"type": "object",
@@ -29,6 +48,7 @@
"permalink_url",
"line_items",
"fulfillment",
+ "payment",
"currency",
"totals"
],
@@ -80,6 +100,11 @@
},
"description": "Fulfillment data: buyer expectations and what actually happened."
},
+ "payment": {
+ "$ref": "#/$defs/order_payment",
+ "description": "Payment details returned on the Order. The instruments may change after purchase, but `maximum_amount` continues to record what the Buyer approved at Checkout.",
+ "ucp_request": "omit"
+ },
"adjustments": {
"type": "array",
"items": {