diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt index 1f228c0f6..7d4ef268d 100644 --- a/.cspell/custom-words.txt +++ b/.cspell/custom-words.txt @@ -81,6 +81,7 @@ amortiguación atok backorder barcodes +cartebancaire checkout cimd credentialization diff --git a/docs/documentation/schema-authoring.md b/docs/documentation/schema-authoring.md index b94ec28aa..eabaeb20c 100644 --- a/docs/documentation/schema-authoring.md +++ b/docs/documentation/schema-authoring.md @@ -126,7 +126,7 @@ Define payment handler configurations in `ucp.payment_handlers{}` registries. - **Top-level fields**: `$schema`, `$id`, `title`, `description`, `name`, `version`, `available_instruments` - **Variants**: `platform_schema`, `business_schema`, `response_schema` - **Instance `id`**: Required to distinguish multiple configurations of the same handler -- **`available_instruments`**: Optional. Array of supported instrument types with type-specific constraints (e.g., brands for credit cards). When absent, the handler places no restrictions — it supports the full set of instrument types defined by its handler schema. +- **`available_instruments`**: Optional. Array of supported instrument types, each with an optional Constraint Expression over type-specific members (e.g., `brand` for credit cards). When absent, the handler places no restrictions — it supports the full set of instrument types defined by its handler schema. Examples: `com.google.pay`, `dev.shopify.shop_pay`, `dev.ucp.processor_tokenizer` diff --git a/docs/specification/overview/index.md b/docs/specification/overview/index.md index dc2498578..edb2fd372 100644 --- a/docs/specification/overview/index.md +++ b/docs/specification/overview/index.md @@ -244,6 +244,14 @@ that member is present, so a branch pinning a discriminator through discriminator in the branch's `required` makes the branch match only the shape it describes. +The grammar is defined independently of the object it is bound to. Request +Constraints bind it to objects in the next request and add `path`; other UCP +schemas reuse it where a declaration already identifies the object it +constrains, such as +[`available_instruments[].constraints`](site:schemas/shopping/types/available_payment_instrument.json), +whose object is the `constraint_target` declared by the instrument schema for +that entry's `type`. + ### Path Every `request_constraints` value has exactly one effective path. When `path` @@ -550,8 +558,10 @@ payment policy. #### Alternative verification requirements on a submitted credential A Business accepts more than one credential shape and requires different -verification data for each. In this example, a raw PAN must carry a `cvc`, and a -network token must carry a `cryptogram` with its `eci_value`: +verification data for each. In this example, a PAN must carry a `cvc`, and a +network token must carry a `cryptogram` with its `eci_value`. Each credential +family is its own schema, so every branch discriminates on the credential's own +`type` and no rule has to branch on a sibling field: ```json @@ -565,12 +575,12 @@ network token must carry a `cryptogram` with its `eci_value`: "credential": { "anyOf": [ { - "properties": {"card_number_type": {"const": "fpan"}}, - "required": ["card_number_type", "cvc"] + "properties": {"type": {"const": "pan"}}, + "required": ["cvc"] }, { - "properties": {"card_number_type": {"const": "network_token"}}, - "required": ["card_number_type", "cryptogram", "eci_value"] + "properties": {"type": {"const": "network_token"}}, + "required": ["cryptogram", "eci_value"] } ] } @@ -583,20 +593,26 @@ network token must carry a `cryptogram` with its `eci_value`: One path selects the submitted instrument, and one Object Constraint describes it. The sibling `required` applies to every matching instrument; the `anyOf` branches then apply to the nested `credential` object, which must satisfy at -least one. Each branch pins `card_number_type` and also names it in `required`, -so a branch matches only the credential shape it describes. A raw PAN without a -`cvc` fails, as does a network token missing its `eci_value`. +least one. Each branch pins `type` with `const`, so a branch matches only the +credential family it describes; +[`payment_credential.json`](site:schemas/shopping/types/payment_credential.json) +already requires `type` on every credential, so no branch has to name it in +`required`. A [PAN credential](site:schemas/shopping/types/pan_credential.json) +without a `cvc` fails, as does a [network +token](site:schemas/shopping/types/network_token_credential.json) missing its +`eci_value`. Because every branch pins the discriminator, the branch set also closes the -accepted values. A `dpan` credential is valid under -[`card_credential.json`](site:schemas/shopping/types/card_credential.json) but -satisfies neither branch, so this Business does not accept it at this path. A -Business that later accepts a new variant adds a branch for it. +accepted credential families. A handler +[token credential](site:schemas/shopping/types/token_credential.json) is a valid +credential at this position but satisfies neither branch, so this Business does +not accept it at this path. A Business that later accepts another family adds a +branch for it. Two separately targeted constraints cannot express this rule. Request Constraints conjoin, so one value requiring `cvc` and another requiring `cryptogram` would require both. Discriminating through the path filter instead -— selecting `fpan` credentials in one value and `network_token` credentials in +— selecting `pan` credentials in one value and `network_token` credentials in another — moves conditional logic into the selector, which paths do not carry. ## Actions @@ -1356,7 +1372,7 @@ Businesses publish their profile at `/.well-known/ucp`. An example: { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex"] } } } } ], @@ -1517,7 +1533,7 @@ example: "spec": "https://example.com/specs/payments/processor_tokenizer-payment", "schema": "https://example.com/schemas/payments/delegate-payment.json", "available_instruments": [ - {"type": "card", "constraints": {"brands": ["visa", "mastercard"]}} + {"type": "card", "constraints": {"properties": {"brand": {"enum": ["visa", "mastercard"]}}}} ] } ] @@ -2709,7 +2725,7 @@ request a challenge. { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -2854,6 +2870,10 @@ Most platform implementations can **avoid PCI-DSS scope** by: - Forwarding credentials without the ability to use them directly - Using PSP tokenization payment handlers where raw credentials never pass through the platform +- Presenting pre-provisioned card network tokens + (`network_token_credential.json`) rather than an FPAN — the platform never + shares the underlying account number, and the token is unusable without a + matching cryptogram #### Business Scope diff --git a/docs/specification/payment/examples/encrypted-credential-payment-handler.md b/docs/specification/payment/examples/encrypted-credential-payment-handler.md index 58779ce83..1aaffb150 100644 --- a/docs/specification/payment/examples/encrypted-credential-payment-handler.md +++ b/docs/specification/payment/examples/encrypted-credential-payment-handler.md @@ -121,11 +121,11 @@ Businesses advertise the platform's handler. The `business_id` field identifies the business, which the platform uses to look up the correct public key for encryption. -The only supported instrument schema is [CardPaymentInstrument](site:schemas/shopping/types/card_payment_instrument.json), the only supported checkout credential schema is `EncryptedCredential`, and the only supported source credential schema is [CardCredential](site:schemas/shopping/types/card_credential.json). +The only supported instrument schema is [CardPaymentInstrument](site:schemas/shopping/types/card_payment_instrument.json), the only supported checkout credential schema is `EncryptedCredential`, and the only supported source credential schema is [PanCredential](site:schemas/shopping/types/pan_credential.json). **Note:** The `EncryptedCredential` shape would be formally defined in the handler's schema (referenced via the `schema` field in the handler declaration). -**Note:** `CardCredential` contains raw PANs. For card credentials, the +**Note:** `PanCredential` contains raw PANs. For card credentials, the platform's vaulting service must be **PCI DSS compliant** when handling these credentials. Businesses receive only encrypted payloads but must be PCI DSS compliant once they decrypt card credentials locally. Other credential types @@ -157,7 +157,7 @@ have their own compliance requirements. { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -195,7 +195,7 @@ The response config includes information about the encryption used. { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -269,7 +269,7 @@ registry using `platform_config`. { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex", "discover"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex", "discover"] } } } } ], diff --git a/docs/specification/payment/examples/platform-tokenizer-payment-handler.md b/docs/specification/payment/examples/platform-tokenizer-payment-handler.md index 67a1aa751..3043c0e4b 100644 --- a/docs/specification/payment/examples/platform-tokenizer-payment-handler.md +++ b/docs/specification/payment/examples/platform-tokenizer-payment-handler.md @@ -176,7 +176,7 @@ platform's handler specification (referenced via `spec`) documents the `/detokenize` endpoint URL exposed by the platform's **payment credential provider**. -The handler accepts [CardCredential](site:schemas/shopping/types/card_credential.json) for tokenization and produces [TokenCredential](site:schemas/shopping/types/token_credential.json) for checkout. +The handler accepts [PanCredential](site:schemas/shopping/types/pan_credential.json) and [NetworkTokenCredential](site:schemas/shopping/types/network_token_credential.json) for tokenization and produces [TokenCredential](site:schemas/shopping/types/token_credential.json) for checkout. **Note:** The result of `/detokenize` contains **sensitive payment data**. Both the sender (platform's credential provider) and receiver @@ -208,7 +208,7 @@ credential type (e.g., PCI DSS for cards). { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -244,7 +244,7 @@ The response config includes runtime token lifecycle information. { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -342,7 +342,7 @@ registry using `platform_config`. { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex", "discover"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex", "discover"] } } } } ], diff --git a/docs/specification/payment/examples/processor-tokenizer-payment-handler.md b/docs/specification/payment/examples/processor-tokenizer-payment-handler.md index deac8e020..bc5252186 100644 --- a/docs/specification/payment/examples/processor-tokenizer-payment-handler.md +++ b/docs/specification/payment/examples/processor-tokenizer-payment-handler.md @@ -123,7 +123,7 @@ The handler's specification (referenced via the `spec` field) documents the { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex"] } } } } ], @@ -158,7 +158,7 @@ The response config includes runtime information about what's available for this { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex"] } } } } ], @@ -205,7 +205,7 @@ business's configuration. "id": "processor_tokenizer", "version": "{{ ucp_version }}", "available_instruments": [ - {"type": "card", "constraints": {"brands": ["visa", "mastercard", "amex"]}} + {"type": "card", "constraints": {"properties": {"brand": {"enum": ["visa", "mastercard", "amex"]}}}} ], "config": { "environment": "production", diff --git a/docs/specification/payment/guide.md b/docs/specification/payment/guide.md index 0aaaecbb0..e37564584 100644 --- a/docs/specification/payment/guide.md +++ b/docs/specification/payment/guide.md @@ -190,10 +190,33 @@ and desired configuration. restrictions on instrument types or constraints — it supports the full set of instrument types defined by its handler schema. When present, it narrows the advertised types and/or applies additional constraints (e.g., limiting card -brands to `["visa", "mastercard"]`). In a Business profile and authoritative +brands to `visa` and `mastercard`). In a Business profile and authoritative response, array order communicates preferred instrument presentation, earliest first. +Each entry narrows along two axes. Both are +[Constraint Expressions](../overview/index.md#constraint-expression), so one +evaluator runs both; they differ only in the object they describe: + +```text +available_instruments[] +├── type selects an instrument schema +├── constraints describes that schema's `constraint_target` +└── ucp.request_constraints describes the request that submits it, via `path` +``` + +`constraints` admits no `path`. Its object is standing, not selected: the +instrument schema for the entry's `type` declares it as `$defs/constraint_target`, +and keys in `properties` name that object's members. The target holds what the +Business derives rather than receives — for cards, `brand`, read from the account +number and surviving tokenization — which is why those values cannot be expressed +as requirements on request data. + +`ucp.request_constraints` carries everything that *is* on the wire: which +instrument fields are required, which credential types are accepted, and which +fields each of those credential types must carry. Use field requirements rather +than handler-specific booleans for modeled data. + When an authoritative response includes `ucp.request_constraints` on an available instrument, the Business **MUST** include an explicit `path` because the available instrument's response Normalized Path does not identify submitted @@ -203,6 +226,48 @@ to submitted `handler_id` and the available instrument's `type` to submitted Payment-handler and instrument specifications define any stronger association the query needs. See [Request Constraints](../overview/index.md#request-constraints). +One path selects the submitted instrument, and one Constraint Expression +describes it. Requirements shared by every instrument of the type are sibling +members; requirements that differ per credential family are `anyOf` branches on +the nested `credential` object. Splitting the credential families into distinct +schemas is what gives each branch a stable discriminator. + + +```json +{ + "id": "processor_tokenizer_1234", + "version": "{{ ucp_version }}", + "available_instruments": [ + { + "type": "card", + "constraints": { "properties": { "brand": { "enum": ["visa", "mastercard"] } } }, + "ucp": { + "request_constraints": { + "path": "$['payment']['instruments'][?@['handler_id'] == 'processor_tokenizer_1234' && @['type'] == 'card']", + "required": ["billing_address", "credential"], + "properties": { + "billing_address": { "required": ["postal_code", "address_country"] }, + "credential": { + "anyOf": [ + { "properties": { "type": { "const": "pan" } }, "required": ["cvc"] }, + { "properties": { "type": { "const": "network_token" } } } + ] + } + } + } + } + } + ] +} +``` + +The sibling members apply to every card instrument from this handler. The +branches accept either a PAN credential carrying a `cvc` or a network token, +which `network_token_credential.json` already requires to carry a `cryptogram`; +a credential of any other type satisfies neither branch. Declared constraints +are the upfront minimum; dynamic requirements still use recoverable errors and +[`message_error.path`](site:schemas/common/types/message_error.json). + --- #### Handler Declaration Variants @@ -230,7 +295,7 @@ and typically includes different configuration: { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -254,7 +319,7 @@ and typically includes different configuration: { "type": "card", "constraints": { - "brands": ["visa", "mastercard", "amex", "discover"] + "properties": { "brand": { "enum": ["visa", "mastercard", "amex", "discover"] } } } } ], @@ -276,7 +341,7 @@ and typically includes different configuration: { "type": "card", "constraints": { - "brands": ["visa", "mastercard"] + "properties": { "brand": { "enum": ["visa", "mastercard"] } } } } ], @@ -316,9 +381,9 @@ authoritative value returned in the `response_schema`. | Source | `available_instruments` | | :----- | :---------------------- | -| Platform profile | `[{type: "card", constraints: {brands: ["visa", "mastercard", "amex", "discover"]}}]` | -| Business profile | `[{type: "card", constraints: {brands: ["visa", "mastercard", "amex"]}}]` | -| **Response (resolved)** | `[{type: "card", constraints: {brands: ["visa", "mastercard", "amex"]}}]` | +| Platform profile | `[{type: "card", constraints: {properties: {brand: {enum: ["visa", "mastercard", "amex", "discover"]}}}}]` | +| Business profile | `[{type: "card", constraints: {properties: {brand: {enum: ["visa", "mastercard", "amex"]}}}}]` | +| **Response (resolved)** | `[{type: "card", constraints: {properties: {brand: {enum: ["visa", "mastercard", "amex"]}}}}]` | In this example, the business's PSP is not configured for Discover, so Discover is excluded from the response even though the platform supports it. @@ -517,21 +582,20 @@ 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. -**Available Instrument Schemas:** +**Constraint Targets:** -Each instrument schema defines its own `available_*` variant in `$defs` that -specifies what constraints are valid for that instrument type. For example, +An instrument schema declares what its availability constraints may name in +`$defs/constraint_target`: a plain object of members and their types, never +carried in a payload. The handler's `payment_instrument` set binds each `type` +to the schema that owns its target. [`card_payment_instrument.json`](site:schemas/shopping/types/card_payment_instrument.json) -defines `available_card_payment_instrument` with a `brands` constraint. - -| Schema | Constraints | -| :----------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | -| [`available_payment_instrument.json`](site:schemas/shopping/types/available_payment_instrument.json) | Base: type, constraints (open object) | -| `card_payment_instrument.json#/$defs/available_card_payment_instrument` | Extends base with `constraints.brands` for card networks | - -Handlers reference these instrument-defined schemas when declaring -`available_instruments`. The **instrument schema authors** define what -constraints are meaningful (e.g., `brands` for cards), and **platforms/businesses** use this to advertise what they support (e.g., `["visa", "mastercard"]`). +declares `brand` as a string, so +`{ "properties": { "brand": { "enum": ["visa", "mastercard"] } } }` names a +declared member and pins it to string values. The base Payment Handler validates +the grammar, so a malformed `constraints` fails everywhere; the target supplies +the meaning, so naming an undeclared member or pinning the wrong type is +reportable without the base dispatching on `type`. A handler extending an +instrument **SHOULD** extend that instrument's target rather than restate it. **Example `types/tokenizer_instrument.json`**: @@ -544,24 +608,17 @@ constraints are meaningful (e.g., `brands` for cards), and **platforms/businesse "description": "Card-based payment instrument for com.example.tokenizer.", "$defs": { - "available_tokenizer_card": { - "title": "Available Tokenizer Card", - "description": "Card instrument availability with tokenizer-specific constraints.", + "constraint_target": { + "title": "Tokenizer Card Constraint Target", + "description": "Extends the card target with tokenizer-specific members.", "allOf": [ - { "$ref": "https://ucp.dev/{{ ucp_version }}/schemas/shopping/types/card_payment_instrument.json#/$defs/available_card_payment_instrument" }, + { "$ref": "https://ucp.dev/{{ ucp_version }}/schemas/shopping/types/card_payment_instrument.json#/$defs/constraint_target" }, { "type": "object", "properties": { - "type": { "const": "tokenizer_card" }, - "constraints": { - "type": "object", - "properties": { - "tokenization_types": { - "type": "array", - "items": { "type": "string" }, - "description": "Supported tokenization types (e.g., ['network_token', 'merchant_token'])." - } - } + "tokenization_type": { + "type": "string", + "description": "How the credential was tokenized (e.g., 'network_token', 'merchant_token')." } } } @@ -626,10 +683,12 @@ constraints are meaningful (e.g., `brands` for cards), and **platforms/businesse **Base Credential Schemas:** -| Schema | Description | -| :------------------------------------------------------------------------------- | :---------------------------- | -| [`payment_credential.json`](site:schemas/shopping/types/payment_credential.json) | Base: type discriminator only | -| [`token_credential.json`](site:schemas/shopping/types/token_credential.json) | Token: type + token string | +| Schema | Description | +| :------------------------------------------------------------------------------------------- | :------------------------------------------ | +| [`payment_credential.json`](site:schemas/shopping/types/payment_credential.json) | Base: type discriminator only | +| [`token_credential.json`](site:schemas/shopping/types/token_credential.json) | Token: type + token string | +| [`pan_credential.json`](site:schemas/shopping/types/pan_credential.json) | Raw FPAN, verified with `cvc`. Source only. | +| [`network_token_credential.json`](site:schemas/shopping/types/network_token_credential.json) | Network token, verified with `cryptogram` | UCP provides base schemas for universal payment credentials. Authors **MAY** extend these schemas to include handler-specific credential context. Handlers diff --git a/docs/specification/payment/tokenization.md b/docs/specification/payment/tokenization.md index a99f3ee40..baea16c65 100644 --- a/docs/specification/payment/tokenization.md +++ b/docs/specification/payment/tokenization.md @@ -57,17 +57,23 @@ Tokenization handlers transform credentials between source and checkout forms: | | source_ | | checkout_ | | | | credentials | What goes IN | credentials | | | | |<--------------- | | | -| | * card/fpan | | What comes OUT | | -| | * card/dpan | ----->| * token | | +| | * pan | | What comes OUT | | +| | * network_token | ----->| * token | | | | | | | | | +-----------------+ +-------------------------+ | | | +-------------------------------------------------------------------------+ ``` -Tokenization handlers accept source credentials (e.g., card with FPAN) and +Tokenization handlers accept source credentials (e.g., a PAN credential) and produce checkout credentials (e.g., tokens). +A network token does not always need this round-trip. A handler **MAY** accept +`network_token_credential.json` as a source credential for tokenization, or +**MAY** accept it directly as a checkout credential, since the token is +unusable without a matching cryptogram. The handler's specification **MUST** +state which of the two it accepts. + ### Token Lifecycle Tokens move through distinct phases. Your handler specification must document @@ -123,8 +129,7 @@ Content-Type: application/json { "credential": { - "type": "card", - "card_number_type": "fpan", + "type": "pan", "number": "4111111111111111", "expiry_month": 12, "expiry_year": 2026, @@ -174,8 +179,7 @@ Authorization: Bearer {caller_access_token} ```json { - "type": "card", - "card_number_type": "fpan", + "type": "pan", "number": "4111111111111111", "expiry_month": 12, "expiry_year": 2026, @@ -235,7 +239,7 @@ When publishing your handler, your specification document **MUST** include: | Instrument | Source Credentials | Checkout Credentials | | :--------- | :--------------------------- | :------------------- | -| `card` | `card` (fpan, network_token) | `token` | +| `card` | `pan`, `network_token` | `token` | **Token Lifecycle:** Single-use (invalidated after detokenization) diff --git a/docs/specification/shopping/checkout/mcp.md b/docs/specification/shopping/checkout/mcp.md index a511d30d1..34a7f3085 100644 --- a/docs/specification/shopping/checkout/mcp.md +++ b/docs/specification/shopping/checkout/mcp.md @@ -67,7 +67,7 @@ Businesses advertise MCP transport availability through their UCP profile at "spec": "https://example.vendor.com/specs/delegate-payment", "schema": "https://example.vendor.com/schemas/delegate-payment-config.json", "available_instruments": [ - {"type": "card", "constraints": {"brands": ["visa", "mastercard"]}} + {"type": "card", "constraints": {"properties": {"brand": {"enum": ["visa", "mastercard"]}}}} ], "config": {} } diff --git a/source/handlers/tokenization/openapi.json b/source/handlers/tokenization/openapi.json index 3a5f29859..e60d6a1ea 100644 --- a/source/handlers/tokenization/openapi.json +++ b/source/handlers/tokenization/openapi.json @@ -30,12 +30,11 @@ } }, "examples": { - "card_credential_agent": { - "summary": "Agent tokenizing on behalf of merchant", + "pan_credential_agent": { + "summary": "Agent tokenizing a PAN credential on behalf of merchant", "value": { "credential": { - "type": "card", - "card_number_type": "fpan", + "type": "pan", "number": "4111111111111111", "expiry_month": 12, "expiry_year": 2026, @@ -50,12 +49,11 @@ } } }, - "card_credential_merchant": { - "summary": "Merchant tokenizing for themselves", + "pan_credential_merchant": { + "summary": "Merchant tokenizing a PAN credential for themselves", "value": { "credential": { - "type": "card", - "card_number_type": "fpan", + "type": "pan", "number": "4111111111111111", "expiry_month": 12, "expiry_year": 2026, @@ -67,17 +65,17 @@ } } }, - "card_credential_network_token": { - "summary": "Card credential (network token)", + "network_token_credential_agent": { + "summary": "Agent tokenizing a network token credential", "value": { "credential": { - "type": "card", - "card_number_type": "network_token", - "number": "4111111111111111", + "type": "network_token", + "number": "5204240000004242", "expiry_month": 12, "expiry_year": 2026, "cryptogram": "gXc5UCLnM6ckD7pjM1TdPA==", "eci_value": "07", + "token_requestor_id": "12345678901", "name": "Jane Doe" }, "binding": { @@ -179,11 +177,10 @@ "$ref": "../../schemas/shopping/types/payment_credential.json" }, "examples": { - "card_credential_fpan": { - "summary": "Card credential (FPAN)", + "pan_credential": { + "summary": "PAN credential", "value": { - "type": "card", - "card_number_type": "fpan", + "type": "pan", "number": "4111111111111111", "expiry_month": 12, "expiry_year": 2026, @@ -191,16 +188,16 @@ "name": "Jane Doe" } }, - "card_credential_network_token": { - "summary": "Card credential (network token)", + "network_token_credential": { + "summary": "Network token credential", "value": { - "type": "card", - "card_number_type": "network_token", - "number": "4111111111111111", + "type": "network_token", + "number": "5204240000004242", "expiry_month": 12, "expiry_year": 2026, "cryptogram": "gXc5UCLnM6ckD7pjM1TdPA==", "eci_value": "07", + "token_requestor_id": "12345678901", "name": "Jane Doe" } } diff --git a/source/schemas/shopping/types/available_payment_instrument.json b/source/schemas/shopping/types/available_payment_instrument.json index cf847b355..9bb8b2c76 100644 --- a/source/schemas/shopping/types/available_payment_instrument.json +++ b/source/schemas/shopping/types/available_payment_instrument.json @@ -11,10 +11,8 @@ "description": "The instrument type identifier (e.g., 'card', 'gift_card'). References an instrument schema's type constant." }, "constraints": { - "type": "object", - "additionalProperties": true, - "description": "Constraints on this instrument type. Structure depends on instrument type and active capabilities.", - "minProperties": 1 + "$ref": "../../common/types/constraint_expression.json", + "description": "A Constraint Expression describing the instrument this entry makes available. Keys in `properties` name members of the `constraint_target` declared by the instrument schema for this `type`. Requirements on submitted request data belong in `ucp.request_constraints` instead." } } } diff --git a/source/schemas/shopping/types/card_credential.json b/source/schemas/shopping/types/card_credential.json index 0d08cc790..4c87b1077 100644 --- a/source/schemas/shopping/types/card_credential.json +++ b/source/schemas/shopping/types/card_credential.json @@ -2,7 +2,8 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://ucp.dev/schemas/shopping/types/card_credential.json", "title": "Card Credential", - "description": "A card credential containing sensitive payment card details including raw Primary Account Numbers (PANs). This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites.", + "description": "Deprecated: use PAN Credential (`pan_credential.json`) or Network Token Credential (`network_token_credential.json`). A card credential containing sensitive payment card details including raw Primary Account Numbers (PANs). This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites.", + "deprecated": true, "allOf": [ { "$ref": "payment_credential.json" @@ -25,7 +26,7 @@ "network_token", "dpan" ], - "description": "The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details." + "description": "Deprecated: the credential type now carries this distinction. The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details." }, "number": { "type": "string", diff --git a/source/schemas/shopping/types/card_payment_instrument.json b/source/schemas/shopping/types/card_payment_instrument.json index 2704308bc..099ceba4b 100644 --- a/source/schemas/shopping/types/card_payment_instrument.json +++ b/source/schemas/shopping/types/card_payment_instrument.json @@ -4,32 +4,21 @@ "title": "Card Payment Instrument", "description": "A basic card payment instrument with visible card details. Can be inherited by a handler's instrument schema to define handler-specific display details or more complex credential structures.", "$defs": { - "available_card_payment_instrument": { - "title": "Available Card Payment Instrument", - "description": "Declares card instrument availability with card-specific constraints.", - "allOf": [ - { "$ref": "available_payment_instrument.json" }, - { - "type": "object", - "properties": { - "type": { - "const": "card" - }, - "constraints": { - "type": "object", - "properties": { - "brands": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1, - "uniqueItems": true, - "description": "Limit to specific card brands (e.g., ['visa', 'mastercard', 'amex'])." - } - } - } - } + "constraint_target": { + "title": "Card Constraint Target", + "description": "The object an available card instrument's `constraints` describes. It declares the constrainable members and their types and is never carried in a payload.", + "type": "object", + "properties": { + "brand": { + "type": "string", + "description": "Card scheme. Derived from the account number, not submitted.", + "examples": [ + "visa", + "mastercard", + "cartebancaire" + ] } - ] + } } }, "allOf": [ @@ -38,20 +27,26 @@ }, { "type": "object", - "required": ["type"], + "required": [ + "type" + ], "properties": { "type": { "type": "string", "const": "card", "description": "Indicates this is a card payment instrument." }, + "network": { + "type": "string", + "description": "Card network elected for this transaction, typically a co-badged selection. When present, the business MAY decline if the card cannot route over it and MUST NOT substitute another." + }, "display": { "type": "object", "description": "Display information for this card payment instrument.", "properties": { "brand": { "type": "string", - "description": "The card brand/network (e.g., visa, mastercard, amex)." + "description": "The card brand/network shown to the buyer (e.g., visa, mastercard, amex). Presentational only." }, "last_digits": { "type": "string", diff --git a/source/schemas/shopping/types/network_token_credential.json b/source/schemas/shopping/types/network_token_credential.json new file mode 100644 index 000000000..02075a3ad --- /dev/null +++ b/source/schemas/shopping/types/network_token_credential.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/types/network_token_credential.json", + "title": "Network Token Credential", + "description": "A card-network token credential verified with a transaction cryptogram. The `number` field carries the network token or wallet-provisioned token rather than the underlying FPAN.", + "allOf": [ + { + "$ref": "payment_credential.json" + }, + { + "type": "object", + "required": [ + "type", + "number", + "cryptogram" + ], + "properties": { + "type": { + "const": "network_token", + "description": "The credential type identifier for network token credentials." + }, + "number": { + "type": "string", + "description": "Network token or wallet-provisioned token replacing the underlying FPAN.", + "examples": [ + "5204240000004242" + ] + }, + "expiry_month": { + "type": "integer", + "description": "The month of the token's expiration date (1-12)." + }, + "expiry_year": { + "type": "integer", + "description": "The year of the token's expiration date." + }, + "name": { + "type": "string", + "description": "Cardholder name.", + "examples": [ + "Jane Doe" + ] + }, + "cryptogram": { + "type": "string", + "description": "Transaction cryptogram or dynamic CVC (dCVV), in the long or short form expected by the card network or processor.", + "examples": [ + "gXc5UCLnM6ckD7pjM1TdPA==" + ] + }, + "eci_value": { + "type": "string", + "description": "Electronic Commerce Indicator / Security Level Indicator associated with the transaction.", + "examples": [ + "07" + ] + }, + "token_requestor_id": { + "type": "string", + "description": "Payment network token requestor identifier, when required by the processor or network-token program.", + "examples": [ + "12345678901" + ] + } + } + } + ] +} diff --git a/source/schemas/shopping/types/pan_credential.json b/source/schemas/shopping/types/pan_credential.json new file mode 100644 index 000000000..b392ee522 --- /dev/null +++ b/source/schemas/shopping/types/pan_credential.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/types/pan_credential.json", + "title": "PAN Credential", + "description": "A card credential carrying a funding primary account number (FPAN). Credential selection follows the shape of the value on the wire rather than its provenance: a network token surfaced in PAN form and verified with a `cvc` \u2014 as with credentials where a dynamic verification code proxies the cryptogram \u2014 is carried here, while a token verified with a discrete `cryptogram` uses Network Token Credential. This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling a PAN credential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites.", + "allOf": [ + { + "$ref": "payment_credential.json" + }, + { + "type": "object", + "required": [ + "type", + "number" + ], + "properties": { + "type": { + "const": "pan", + "description": "The credential type identifier for PAN credentials." + }, + "number": { + "type": "string", + "description": "Funding primary account number (FPAN).", + "examples": [ + "4242424242424242" + ] + }, + "expiry_month": { + "type": "integer", + "description": "The month of the card's expiration date (1-12)." + }, + "expiry_year": { + "type": "integer", + "description": "The year of the card's expiration date." + }, + "name": { + "type": "string", + "description": "Cardholder name.", + "examples": [ + "Jane Doe" + ] + }, + "cvc": { + "type": "string", + "maxLength": 4, + "description": "Card verification code.", + "examples": [ + "223" + ] + } + } + } + ] +}