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
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,11 @@ The response config includes runtime token lifecycle information.
Upon receiving a checkout with a token credential:

1. **Validate Handler:** Confirm `instrument.handler_id` matches the expected handler ID.
2. **Detokenize or Delegate:**
2. **Validate Binding:** Validate `credential.binding` according to the handler's binding policy.
3. **Detokenize or Delegate:**
* **Option A (Direct):** Call the platform's **credential provider** `/detokenize` endpoint directly, then process payments.
* **Option B (Delegated):** Forward the token to a PSP for detokenization and payment processing.
3. **Return Response:** Respond with the finalized checkout state.
4. **Return Response:** Respond with the finalized checkout state.

For option B, see section [PSP Integration](#psp-integration).

Expand Down Expand Up @@ -397,7 +398,14 @@ Content-Type: application/json
},
"credential": {
"type": "token",
"token": "ptok_x9y8z7w6v5u4"
"token": "ptok_x9y8z7w6v5u4",
"binding": {
"type": "dev.ucp.shopping.checkout",
"id": "checkout_789"
},
"identity": {
"access_token": "business_abc123"
}
}
}
]
Expand All @@ -409,6 +417,10 @@ Content-Type: application/json
}
```

The Platform **MUST** copy `binding` and any `identity` unchanged from the token
issuance context. Before detokenization or delegation, the Business **MUST**
validate the binding according to the handler's binding policy.

---

## Runtime Payment Authentication Actions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,11 @@ Response:
}
```

The Platform retains the `binding` and any `identity` sent with this request.

### Step 4: Complete Checkout

The Platform submits the token.
The Platform submits the token and its issuance context.

<!-- ucp:example schema=shopping/checkout op=complete direction=request -->
```json
Expand All @@ -266,7 +268,14 @@ Content-Type: application/json
},
"credential": {
"type": "token",
"token": "tok_a1b2c3d4e5f6"
"token": "tok_a1b2c3d4e5f6",
"binding": {
"type": "dev.ucp.shopping.checkout",
"id": "checkout_789"
},
"identity": {
"access_token": "merchant_xyz789"
}
}
}
]
Expand All @@ -278,6 +287,12 @@ Content-Type: application/json
}
```

The Platform **MUST** copy `binding` and any `identity` unchanged from the
tokenization request. The Business **MUST** validate the binding according to
the handler's binding policy. In PSP Mode, the Business passes the token and
binding to the PSP, which **MUST** compare the binding with its issuance record
and authenticate the caller independently.

---

## Implementation Guide
Expand Down
2 changes: 1 addition & 1 deletion docs/specification/payment/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ instrument **SHOULD** extend that instrument's target rather than restate it.
| Schema | Description |
| :--------------------------------------------------------------------------------------------- | :------------------------------------------ |
| [`payment_credential.json`](site:schemas/common/types/payment_credential.json) | Base: type discriminator only |
| [`token_credential.json`](site:schemas/common/types/token_credential.json) | Token: type + token string |
| [`token_credential.json`](site:schemas/common/types/token_credential.json) | Token: string + optional binding/identity |
| [`pan_credential.json`](site:schemas/common/types/pan_credential.json) | Raw FPAN, verified with `cvc`. Source only. |
| [`network_token_credential.json`](site:schemas/common/types/network_token_credential.json) | Network token, verified with `cryptogram` |

Expand Down
47 changes: 34 additions & 13 deletions docs/specification/payment/tokenization.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,17 @@ Tokenization handlers transform credentials between source and checkout forms:
| | |<--------------- | | |
| | * pan | | What comes OUT | |
| | * network_token | ----->| * token | |
| | | | * binding (optional) | |
| | | | * identity (optional) | |
| | | | | |
| +-----------------+ +-------------------------+ |
| |
+-------------------------------------------------------------------------+
```

Tokenization handlers accept source credentials (e.g., a PAN credential) and
produce checkout credentials (e.g., tokens).
produce checkout credentials (e.g., tokens). A token credential can carry the
binding and participant identity established during tokenization.

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
Expand Down Expand Up @@ -114,6 +117,22 @@ required when the caller acts on behalf of another participant, and omitted
when the authenticated caller is that participant. See
[Payment Identity Schema](site:schemas/common/types/payment_identity.json).

`TokenCredential` defines optional `binding` and `identity` members so the
Platform can carry this issuance context into checkout. Existing credentials
remain valid, and a concrete handler **MAY** require either member.

When either member is included, the Platform **MUST** copy it unchanged from
the tokenization request. Before detokenization or processing, the Business
**MUST** validate a carried binding according to the handler's binding policy.
The carried identity remains a participant identifier; caller authentication
is separate, as required by rule 4 below.

A tokenize-to-process handler has no `/detokenize` request that can carry the
binding. Its concrete credential schema therefore **SHOULD** require `binding`,
and its specification **MUST** define how the Business passes the binding to the
Processor. Before processing, the Processor **MUST** compare it by exact
equality with the binding recorded at issuance.

Binding is a replay guard, not a resource reference. The following rules apply
to every tokenizer:

Expand All @@ -133,15 +152,16 @@ to every tokenizer:
implementation.
4. Every token is issued to exactly one participant. A Tokenizer **MUST**
record that participant at `/tokenize` — from `identity` when present,
otherwise the authenticated caller. On `/detokenize` a Tokenizer **MUST**
resolve the requesting participant the same way, **MUST** verify it matches
the participant recorded at issuance, and **MUST** verify that the
authenticated caller is that participant or is authorized to act for it. A
Tokenizer **MUST NOT** return the credential when either check fails.
`identity` is a participant identifier, not a credential, and a Tokenizer
**MUST NOT** accept it as authentication. The mechanism by which one
participant is authorized to act for another is handler-defined and outside
the scope of this specification.
otherwise the authenticated caller. On `/detokenize` or a combined
processing operation, a Tokenizer **MUST** resolve the requesting participant
the same way, **MUST** verify it matches the participant recorded at
issuance, and **MUST** verify that the authenticated caller is that
participant or is authorized to act for it. A Tokenizer **MUST NOT** return
the credential or process the payment when either check fails. `identity` is
a participant identifier, not a credential, and a Tokenizer **MUST NOT**
accept it as authentication. The mechanism by which one participant is
authorized to act for another is handler-defined and outside the scope of
this specification.

---

Expand Down Expand Up @@ -239,7 +259,7 @@ See the full [OpenAPI specification](site:handlers/tokenization/openapi.json) fo
| Requirement | Description |
| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
| **Binding required** | Credentials **MUST** be bound to a `binding` resource (`type` and `id`) and issued to exactly one participant to prevent reuse |
| **Binding verified** | Tokenizer **MUST** verify binding matches before returning credentials |
| **Binding verified** | Tokenizer **MUST** exact-match binding on detokenization; Processor **MUST** exact-match it during processing |
| **Cryptographically random** | Use secure random generators; tokens must be unguessable |
| **Sufficient length** | Minimum 128 bits of entropy |
| **Non-reversible** | Cannot derive the credential from the token |
Expand Down Expand Up @@ -298,11 +318,12 @@ A tokenizer handler conforms to this pattern if it:
- [ ] Defines authentication and onboarding requirements
- [ ] Documents credential transformation between source and checkout forms
- [ ] Produces tokens compatible with the `TokenCredential` schema
- [ ] Preserves carried `binding` and `identity` unchanged from tokenization through checkout
- [ ] Specifies token lifecycle policy (TTL, single-use, etc.)
- [ ] Requires `binding` with `type` and `id` on tokenization requests
- [ ] Uses `PaymentIdentity` for participant identification
- [ ] Verifies `binding` matches by exact equality on detokenization requests
- [ ] Records the participant each token is issued to, and on detokenization verifies both that participant and the caller's authority to act for it
- [ ] Verifies `binding` by exact equality on detokenization and, for tokenize-to-process, during processing
- [ ] Records the participant each token is issued to, and on detokenization or processing verifies both that participant and the caller's authority to act for it
- [ ] Accepts binding types it does not recognize
- [ ] Requires security acknowledgements from participants receiving raw credentials

Expand Down
10 changes: 10 additions & 0 deletions source/schemas/common/types/token_credential.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
"type": "string",
"description": "The token value.",
"ucp_response": "omit"
},
"binding": {
"$ref": "binding.json",
"description": "Resource binding supplied when the token was issued. The Platform MUST preserve this value when carrying it into checkout.",
"ucp_response": "omit"
},
"identity": {
"$ref": "payment_identity.json",
"description": "Participant identity supplied when the token was issued. The Platform MUST preserve this value when carrying it into checkout.",
"ucp_response": "omit"
}
}
}
Expand Down
Loading