From 2bfda8e47a2050bd021a80b577c60223ebfa7fe5 Mon Sep 17 00:00:00 2001 From: Daniel Wyckoff Date: Fri, 24 Jul 2026 12:43:12 -0400 Subject: [PATCH] introduce the payment authentication extension --- .cspell/custom-words.txt | 12 + docs/documentation/core-concepts.md | 1 + .../platform-tokenizer-payment-handler.md | 19 + .../payment-actions/device-data-collection.md | 208 +++++++++++ .../payment-actions/three-ds-challenge.md | 230 +++++++++++++ docs/specification/payment-authentication.md | 325 ++++++++++++++++++ docs/specification/payment-handler-guide.md | 38 ++ .../specification/payment-handler-template.md | 20 ++ mkdocs.yml | 13 + ...pping_payment_authentication_response.json | 48 +++ .../shopping/payment_authentication.json | 88 +++++ .../payment-actions/embedded.openrpc.json | 88 +++++ 12 files changed, 1090 insertions(+) create mode 100644 docs/specification/payment-actions/device-data-collection.md create mode 100644 docs/specification/payment-actions/three-ds-challenge.md create mode 100644 docs/specification/payment-authentication.md create mode 100644 scripts/scaffolds/shopping_payment_authentication_response.json create mode 100644 source/schemas/shopping/payment_authentication.json create mode 100644 source/services/payment-actions/embedded.openrpc.json diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt index edf126445..4bef035a6 100644 --- a/.cspell/custom-words.txt +++ b/.cspell/custom-words.txt @@ -1,4 +1,6 @@ # cspell-specific custom words related to UCP +ACS +AReq Accor Adyen Affirm @@ -14,10 +16,14 @@ Carrefour Centricity Chewy Commerce +CReq +CRes Credentialless +DDC Depot Diffie EDITMSG +EMVCo EWALLET Ed25519 EdDSA @@ -40,8 +46,11 @@ Paypal Petbarn Prasad Preorders +PSP Queensway Quicktype +RReq +RRes Sephora Shopee Shopify @@ -127,7 +136,10 @@ streamable superfences talla thibmeu +threeDSCompInd +threeDSMethodData tracción +transStatus ttclid unparseable upsell diff --git a/docs/documentation/core-concepts.md b/docs/documentation/core-concepts.md index da07d1937..0e6f3e387 100644 --- a/docs/documentation/core-concepts.md +++ b/docs/documentation/core-concepts.md @@ -184,6 +184,7 @@ up-to-date list. | :--- | :--- | :--- | | `dev.ucp.shopping.discount` | checkout, cart | Discount codes and promotions | | `dev.ucp.shopping.fulfillment` | checkout | Shipping and delivery options | +| `dev.ucp.shopping.payment_authentication` | checkout | Browser-surface device data collection and 3DS challenges | | `dev.ucp.shopping.ap2_mandate` | checkout | Non-repudiable authorization for autonomous commerce | | `dev.ucp.shopping.buyer_consent` | checkout, cart | Explicit consent capture | diff --git a/docs/specification/examples/platform-tokenizer-payment-handler.md b/docs/specification/examples/platform-tokenizer-payment-handler.md index bf4962f2b..73e48a71d 100644 --- a/docs/specification/examples/platform-tokenizer-payment-handler.md +++ b/docs/specification/examples/platform-tokenizer-payment-handler.md @@ -410,6 +410,25 @@ Content-Type: application/json --- +## Runtime Payment Authentication Actions + +This handler supports both Action types from the negotiated +[Payment Authentication extension](../payment-authentication.md): + +| Action type | Use | +| :---------- | :-- | +| `dev.ucp.payment.device_data_collection` | Collect device/browser data for the selected tokenized instrument. | +| `dev.ucp.payment.three_ds_challenge` | Present a buyer-facing 3DS challenge during payment completion. | + +When either step is needed, the Business returns the corresponding Action while +processing the selected instrument. Its `config.payment_instrument_id` identifies +that instrument, from which the Platform resolves this handler and its trust +policy. The Platform processes the Action according to the extension and +retrieves Checkout after `action.done`. The submitted token and instrument remain +unchanged; the Business determines the payment outcome from its provider state. + +--- + ## PSP Integration ### Prerequisites diff --git a/docs/specification/payment-actions/device-data-collection.md b/docs/specification/payment-actions/device-data-collection.md new file mode 100644 index 000000000..30f9d08dc --- /dev/null +++ b/docs/specification/payment-actions/device-data-collection.md @@ -0,0 +1,208 @@ + + +# Payment Device Data Collection Action + +This specification defines the device data collection Action type declared by +the [Payment Authentication extension](../payment-authentication.md): + +```text +dev.ucp.payment.device_data_collection +``` + +It asks the Platform to run a scoped, invisible browser-capable surface associated +with a payment instrument or attempt. + +## Purpose and Scope + +Device data collection lets a Business, payment handler, PSP, or 3DS provider +collect device or browser data before payment processing continues. UCP does not +carry the collected data. The loaded surface sends provider-specific data directly +to the Business or payment provider through its own integration. + +For EMV 3DS, this Action represents only the Platform-facing instruction to +mount the collection surface. UCP does not define how a Business maps ACS method +availability, method timeouts, or collection outcomes into EMVCo state such as +`threeDSCompInd`. If a provider or Business skips collection, including when an +ACS supplies no method URL, the Business does not emit this Action. + +## Emission and Gated Effect + +A Business **MAY** emit this type from a Create, Update, or Complete Checkout +response after it can associate collection with a payment instrument and +negotiated payment handler. `config.payment_instrument_id` identifies that +instrument. + +The Action gates advancement of the associated payment attempt while the +occurrence remains outstanding in the authoritative Checkout. A terminal surface +event starts reconciliation; it does not itself lift the gate or assert that +collection succeeded. If collection is merely an optimization that the Business +will not wait for, the Business **MUST NOT** emit an Action. + +## Runtime Shape + +The Action is emitted under its type key: + + +```json +{ + "actions": { + "dev.ucp.payment.device_data_collection": [ + { + "id": "ddc-1", + "config": { + "payment_instrument_id": "instrument_1", + "url": "https://payments.example.com/ucp/payment/ddc/session_123" + } + } + ] + } +} +``` + +The config shape is defined inline by the +[Payment Authentication extension schema](site:schemas/shopping/payment_authentication.json). + +| Field | Type | Required | Notes | +| :---- | :--- | :------- | :---- | +| `payment_instrument_id` | string | ✓ | ID of the associated instrument in the containing Checkout. | +| `url` | string | ✓ | Absolute HTTPS URL for the invisible collection surface. | + +The Business **SHOULD** provide a Business- or provider-operated wrapper URL that +owns any provider-specific POST, such as posting `threeDSMethodData` to an ACS +method URL. The Platform performs an ordinary navigation to `config.url`. + +## Platform Behavior + +The Platform **MUST**: + +1. validate the URL according to the Payment Authentication contract, the + payment handler and instrument context, and Platform policy; +2. load the URL in an isolated, invisible browser-capable surface; +3. complete the `action.ready` handshake before allowing collection to begin; +4. correlate embedded notifications with both this Action occurrence and the + mounted surface; and +5. unmount the surface after `action.done`, `action.error`, load failure, or + timeout. + +Mounting the surface **MUST** follow the shared +[Payment Authentication rendering contract](../payment-authentication.md#surface-rendering-and-notifications) +and [Embedded Protocol security requirements](../embedded-protocol.md#security). + +On the web the surface is typically a hidden iframe. A native Platform may use +an isolated webview or equivalent browser surface. It must not be visible to the +Buyer or request Buyer interaction. + +The Platform distinguishes only whether its surface finished or could not +continue. It does not determine whether device data was collected successfully, +whether an ACS method timed out, or whether 3DS authentication should proceed. + +## Embedded Notifications + +After completing the shared +[`action.ready` handshake](../payment-authentication.md#ready-handshake), the +surface sends JSON-RPC 2.0 notifications defined by the +[payment Action embedded contract](../payment-authentication.md#surface-rendering-and-notifications). + +### Done + +When the Platform-facing collection step is finished, the surface **MUST** send: + + +```json +{ + "jsonrpc": "2.0", + "method": "action.done", + "params": { + "id": "ddc-1" + } +} +``` + +The surface may send `action.done` when handler-owned logic determines that the +Platform should close the surface. The notification does not reveal whether +collection succeeded, was unavailable, was skipped, or mapped to a particular +provider completion indicator. + +### Error + +If the surface cannot continue from the Platform-facing transport perspective, +it **MAY** send: + + +```json +{ + "jsonrpc": "2.0", + "method": "action.error", + "params": { + "id": "ddc-1", + "error": { + "ucp": { "version": "{{ ucp_version }}", "status": "error" }, + "messages": [ + { + "type": "error", + "code": "action_unavailable", + "content": "Device data collection could not start.", + "severity": "recoverable" + } + ] + } + } +} +``` + +Recommended surface-level codes are `action_unavailable` when initialization +fails, `action_expired` when the surface's session expired, and `action_failed` +for another terminal surface error. These are diagnostic codes, not EMV 3DS or +payment outcomes. A provider-domain timeout or unavailable method that the +handler can safely continue past should normally result in `action.done`, with +the Business and provider recording the domain outcome server-side. After either +notification, the Platform follows the shared Payment Authentication +reconciliation contract. + +## Deadline and Fallback + +[EMV 3-D Secure Protocol and Core Functions v2.3.1.1](https://www.emvco.com/specifications/emv-3-d-secure-protocol-and-core-functions-specification-6/){ target="_blank" } +requires the 3DS Server to set `threeDSCompInd` to `N` when the 3DS Method does +not complete within five seconds (Req 315). That provider-domain timer begins +when the method is invoked and remains owned by the Business and provider; the +Platform **MUST NOT** derive it from the outer surface's load event. + +The Platform applies a bounded outer-surface deadline defined by the handler or +Platform policy, measured from navigation and capped by Checkout `expires_at`. +That deadline controls only how long the Platform keeps the UCP surface mounted; +it does not set `threeDSCompInd` or another provider outcome. + +If no valid `action.done` or `action.error` arrives before the outer deadline, +the Platform **MUST** unmount the surface, treat the interaction as timed out, +and follow the non-successful surface reconciliation path. It may unmount sooner +after load failure or a valid terminal notification. The Platform **MUST NOT** +remount the same Action type and `id`; an unchanged occurrence follows the +parent Checkout Actions fallback, handoff, and cancellation rules. + +## Security + +The common [Payment Authentication security requirements](../payment-authentication.md#security-and-data-handling) +apply. In addition: + +- The handler's trust policy **MUST** authorize the initial and redirect origins + before the Platform loads them. +- The surface **MUST NOT** receive Platform credentials or access Platform + storage. +- Provider payloads and collected device data **MUST NOT** be copied into the + Action, notification params, Checkout, or payment instrument. +- `action.done` and `action.error` are advisory surface signals. A later + Checkout response, based on Business/provider state, remains authoritative. diff --git a/docs/specification/payment-actions/three-ds-challenge.md b/docs/specification/payment-actions/three-ds-challenge.md new file mode 100644 index 000000000..df6ab5705 --- /dev/null +++ b/docs/specification/payment-actions/three-ds-challenge.md @@ -0,0 +1,230 @@ + + +# Payment 3DS Challenge Action + +This specification defines the 3DS challenge Action type declared by the +[Payment Authentication extension](../payment-authentication.md): + +```text +dev.ucp.payment.three_ds_challenge +``` + +It asks the Platform to present a scoped, buyer-facing payment-authentication +surface. + +## Purpose and Scope + +A 3DS challenge Action lets the Business and payment handler ask the Platform to +present an issuer or provider challenge to the Buyer. UCP does not carry EMV 3DS +payloads such as CReq/CRes, AReq/ARes, RReq/RRes, or `transStatus`. Those details +remain within the loaded surface and the Business, payment handler, PSP, 3DS +server, issuer, or ACS integration. + +The Platform presents the surface and observes only when the Platform-facing +interaction finishes or cannot continue. The Business determines the +authentication and payment outcome from its server-side provider state. + +## Emission and Gated Effect + +The 3DS challenge Action gates the associated payment attempt from producing a +completed Checkout while the occurrence remains outstanding. It does not assert +that the Complete Checkout operation was rejected, and a terminal surface event +does not itself lift the gate. + +A Business **SHOULD** accept Complete Checkout, begin the payment attempt, and +return `complete_in_progress` with the challenge Action. If the Business cannot +accept completion before the challenge, it follows the core Checkout rule: it +returns `incomplete` with a recoverable error Message whose `path` selects the +challenge occurrence. + +## Runtime Shape + +The Action is emitted under its type key: + + +```json +{ + "actions": { + "dev.ucp.payment.three_ds_challenge": [ + { + "id": "three-ds-challenge-1", + "config": { + "payment_instrument_id": "instrument_1", + "url": "https://payments.example.com/ucp/payment/3ds/session_456" + } + } + ] + } +} +``` + +The config shape is defined inline by the +[Payment Authentication extension schema](site:schemas/shopping/payment_authentication.json). + +| Field | Type | Required | Notes | +| :---- | :--- | :------- | :---- | +| `payment_instrument_id` | string | ✓ | ID of the associated instrument in the containing Checkout. | +| `url` | string | ✓ | Absolute HTTPS URL for the buyer-facing challenge surface. | + +The Business **SHOULD** provide a Business- or provider-operated wrapper URL that +owns any provider-specific POST, such as submitting CReq to an ACS challenge +URL. The Platform performs an ordinary navigation to `config.url`; it +**MUST NOT** construct provider requests, parse challenge payloads, or relay 3DS +protocol data. + +## Platform Behavior + +The Platform **MUST**: + +1. validate the URL according to the Payment Authentication contract, the + payment handler and instrument context, and Platform policy; +2. load the URL in an isolated, visible browser-capable surface; +3. complete the `action.ready` handshake before presenting the challenge; +4. prevent interaction with the rest of Checkout while the challenge is + outstanding; +5. correlate embedded notifications with both this Action occurrence and the + mounted surface; and +6. close the surface after `action.done`, `action.error`, unrecoverable load + failure, or abandonment. + +Mounting the surface **MUST** follow the shared +[Payment Authentication rendering contract](../payment-authentication.md#surface-rendering-and-notifications) +and [Embedded Protocol security requirements](../embedded-protocol.md#security). + +On the web the surface may be a full-page frame, modal frame, or separate window +when the negotiated handler defines that presentation and its security policy. +A native Platform may use an isolated webview, browser view, or system browser. +A top-level or system-browser handoff is permitted only when the handler defines +a completion channel that the Platform can authenticate and correlate with the +Action occurrence. + +The Platform **MUST NOT** infer an authentication outcome from frame content, +URL changes, or notification diagnostics. + +## Embedded Notifications + +After completing the shared +[`action.ready` handshake](../payment-authentication.md#ready-handshake), the +surface sends JSON-RPC 2.0 notifications defined by the +[payment Action embedded contract](../payment-authentication.md#surface-rendering-and-notifications). + +### Done + +When the Platform-facing challenge interaction finishes, the surface **MUST** +send: + + +```json +{ + "jsonrpc": "2.0", + "method": "action.done", + "params": { + "id": "three-ds-challenge-1" + } +} +``` + +`action.done` means only that the Platform should close the challenge surface +and retrieve Checkout. It does not mean authentication or payment succeeded. +After closing the surface, the Platform uses Get Checkout and does not call +Complete Checkout again to resume the accepted operation. + +A handler-owned surface **MAY** include provider-specific or diagnostic params +such as correlation identifiers or SDK outcomes. Platforms **MUST** ignore +unknown params for UCP processing unless the negotiated handler defines them. +They are never authoritative payment outcomes by default. + +### Error + +If the surface cannot continue, it **MAY** send: + + +```json +{ + "jsonrpc": "2.0", + "method": "action.error", + "params": { + "id": "three-ds-challenge-1", + "error": { + "ucp": { "version": "{{ ucp_version }}", "status": "error" }, + "messages": [ + { + "type": "error", + "code": "action_failed", + "content": "The challenge surface could not continue.", + "severity": "recoverable" + } + ] + } + } +} +``` + +Recommended surface-level codes are `action_unavailable` when initialization +fails, `action_expired` when the challenge session expired, and `action_failed` +for another terminal surface error. Buyer abandonment and provider +authentication outcomes may be unavailable to the surface or known only by the +provider backend; the Business and payment provider remain authoritative for +those distinctions. After either notification, the Platform follows the shared +Payment Authentication reconciliation contract. + +## Combined Provider Flows + +A payment provider may use one wrapper URL to orchestrate device data collection +and a challenge. The emitted Action type describes the Platform-facing behavior: + +- If the surface remains invisible and never requests Buyer interaction, the + Business emits device data collection. +- If the surface may display a Buyer challenge, the Business emits 3DS challenge. + +The Business must not emit an invisible DDC Action and then make that surface +visible. If collection completes first, the preferred flow is to remove the DDC +occurrence and emit a new challenge occurrence in a later Checkout response. +The two occurrences have different Action types and IDs even when their wrapper +URL origin is the same. + +## Deadline and Fallback + +[EMV 3-D Secure Protocol and Core Functions v2.3.1.1](https://www.emvco.com/specifications/emv-3-d-secure-protocol-and-core-functions-specification-6/){ target="_blank" } +gives the ACS 10 minutes (600 seconds) after successfully sending each challenge +interface to the challenge iframe (Req 226). The surface and provider own that +per-interface timer. It does not define a single UCP deadline beginning at the +initial wrapper navigation. + +The Platform applies a bounded outer-surface deadline defined by the handler or +Platform policy, measured from navigation and capped by Checkout `expires_at`. +The Business **MUST** choose an expiry that accommodates the handler-defined +window. The outer deadline governs only the Platform-owned container and does +not replace or reset an ACS timer. + +The Platform may close sooner after a valid terminal notification, load failure, +Buyer dismissal, or terminal Checkout state. When the outer deadline elapses, it +**MUST** close the surface, treat the interaction as abandoned, and follow the +non-successful surface reconciliation path. It **MUST NOT** remount the same +Action type and `id`; further handling follows the parent Checkout Actions +fallback, handoff, and cancellation rules. + +## Security + +The common [Payment Authentication security requirements](../payment-authentication.md#security-and-data-handling) +apply. In addition: + +- The handler's trust policy **MUST** authorize the initial and redirect origins + before the Platform displays them. +- The Platform validates both message source and origin. +- A later Checkout response based on server-side provider state remains + authoritative regardless of any surface notification. diff --git a/docs/specification/payment-authentication.md b/docs/specification/payment-authentication.md new file mode 100644 index 000000000..db04f9a77 --- /dev/null +++ b/docs/specification/payment-authentication.md @@ -0,0 +1,325 @@ + + +# Payment Authentication Extension + +## Overview + +The Payment Authentication extension defines browser-surface interactions that +a Platform may need to process while a payment attempt is underway. It declares +two concrete [Action types](overview.md#actions): + +| Action type | Platform interaction | +| :---------- | :------------------- | +| `dev.ucp.payment.device_data_collection` | Run an invisible browser-capable device data collection surface. | +| `dev.ucp.payment.three_ds_challenge` | Present a buyer-facing 3DS challenge surface. | + +The extension is named: + +```text +dev.ucp.shopping.payment_authentication +``` + +It extends `dev.ucp.shopping.checkout`. Negotiating this extension activates the +complete contract for both Action types. A Business **MUST NOT** emit either type +unless this extension is active for the Checkout. + +The extension standardizes only the Platform-facing interaction. It does not +re-specify EMV 3DS, carry EMV 3DS messages, report authentication outcomes, or +replace payment-handler processing. The Business, its payment handler, and its +payment provider remain responsible for provider protocol state and for the +authoritative payment outcome. + +## Discovery and Negotiation + +Businesses and Platforms advertise this extension in their profiles: + + +```json +{ + "ucp": { + "version": "{{ ucp_version }}", + "capabilities": { + "dev.ucp.shopping.payment_authentication": [ + { + "version": "{{ ucp_version }}", + "extends": "dev.ucp.shopping.checkout", + "spec": "https://ucp.dev/{{ ucp_version }}/specification/payment-authentication", + "schema": "https://ucp.dev/{{ ucp_version }}/schemas/shopping/payment_authentication.json" + } + ] + } + } +} +``` + +A Platform advertises the extension only if it can process both the invisible +and buyer-facing browser surfaces, enforce the security requirements in this +specification, and process the embedded notifications. A Business advertises it +only when each payment handler that may cause these Actions defines the required +trust and fallback contract. + +Negotiating the extension does not make every runtime URL trustworthy. Each +instance remains subject to its schema, the payment handler and instrument +context established by the payment attempt, and Platform policy. + +## Runtime Shape + +Actions are keyed by type in the Checkout response. For example, a device data +collection step can be followed by a challenge in a later response: + + +```json +{ + "ucp": { + "version": "{{ ucp_version }}", + "status": "success", + "capabilities": { + "dev.ucp.shopping.payment_authentication": [ + { + "version": "{{ ucp_version }}", + "extends": "dev.ucp.shopping.checkout" + } + ] + }, + "payment_handlers": { + "com.example.card": [ + { "id": "card_handler_1", "version": "{{ ucp_version }}" } + ] + } + }, + "id": "checkout_123", + "status": "complete_in_progress", + "currency": "USD", + "line_items": [], + "totals": [ + { "type": "subtotal", "amount": 100 }, + { "type": "total", "amount": 100 } + ], + "links": [], + "payment": { + "instruments": [ + { + "id": "instrument_1", + "handler_id": "card_handler_1", + "type": "card", + "selected": true + } + ] + }, + "actions": { + "dev.ucp.payment.device_data_collection": [ + { + "id": "ddc-1", + "config": { + "payment_instrument_id": "instrument_1", + "url": "https://payments.example.com/3ds/method/session_123" + } + } + ] + } +} +``` + +A Business normally emits only the next interaction the Platform can process. +If collection must precede a challenge, it emits the DDC Action first and emits +the challenge under `dev.ucp.payment.three_ds_challenge` only in a later +Checkout response. + +Each Action's `config.payment_instrument_id` **MUST** identify an instrument in +the containing Checkout. The Platform resolves that instrument's `handler_id` +against `ucp.payment_handlers` and applies that handler's trust and fallback +rules. The Platform **MUST** decline an Action whose instrument or handler +association cannot be resolved unambiguously. + +## Checkout Lifecycle + +These Action types use the parent Checkout lifecycle defined in +[Checkout — Actions](checkout.md#actions): + +- Every emitted Payment Authentication Action gates advancement of its associated + payment attempt while the same Action type and `id` remain outstanding in the + authoritative Checkout. A terminal surface event ends the Platform's current + processing attempt; it does not lift the gate. +- A Business **MAY** emit device data collection from Create or Update Checkout + to perform it before payment completion. While the Checkout is `incomplete`, + the Action prevents `ready_for_complete` until it resolves; unrelated Updates + remain allowed. +- A Business **SHOULD** accept Complete Checkout before beginning a + payment-authentication interaction needed during payment processing and return + `complete_in_progress` with the Action. This represents an accepted payment + attempt that is waiting for Platform work. +- If an Action instead prevents Complete Checkout from being accepted, the + Business **MUST** return `status: incomplete` and a `recoverable` error Message + whose `path` selects the exact Action occurrence. +- While the Checkout is `complete_in_progress`, the Platform **MUST NOT** start + another Update or Complete Checkout operation. It uses Get Checkout to observe + Business processing and follows the parent fallback, handoff, and cancellation + rules. + +A Payment Authentication Action occurrence is single-use. After starting its +surface, the Platform **MUST NOT** process the same Action type and `id` again, +including after `action.done`, `action.error`, a load failure, timeout, or Buyer +dismissal. If the Business needs the Platform to retry the interaction, it +**MUST** replace the occurrence with a new `id`. + +After a terminal surface outcome, the Platform unmounts the surface and +reconciles with the authoritative Checkout: + +| Surface outcome | Reconciliation | +| :-------------- | :------------- | +| `action.done` | Use Get Checkout and repeat with bounded backoff for no more than 30 seconds, stopping earlier at `expires_at`. | +| `action.error`, load failure, timeout, or Buyer dismissal | Use Get Checkout once before applying fallback. The Action type or handler may define additional bounded reconciliation. | + +If the later response removes the Action, replaces it with a new `id`, or +advances the Checkout, that response is authoritative. After learning a terminal +provider outcome, the Business **MUST** remove the original occurrence, replace +it with new work under a new `id`, or advance the Checkout. It **MUST NOT** +continue returning an occurrence that no longer represents processable work. + +If the same occurrence remains after its reconciliation window, the Platform +**MUST NOT** reopen it and follows the parent Checkout Actions fallback, handoff, +and cancellation rules. + +An `action.done` notification means only that the Platform-facing surface +finished. The Business determines whether collection or authentication succeeded +from its server-side payment-provider state. + +## Decline, Failure, and Abandonment + +A Platform **MAY** decline an instance that violates its runtime policy. If it +cannot process the instance, it **SHOULD** use a valid `continue_url` to hand the +session to the Business when available. + +For an Action on an `incomplete` Checkout, the Platform may instead choose a +different payment instrument through an ordinary Update and later submit a new +Complete operation. This option is unavailable during `complete_in_progress`, +when Update Checkout is frozen. The Business treats work associated with the +previous instrument as superseded and stops using it for the new payment +attempt. + +The individual Action specifications define type-specific timeout and fallback +behavior: + +- [Payment Device Data Collection](payment-actions/device-data-collection.md) +- [Payment 3DS Challenge](payment-actions/three-ds-challenge.md) + +## Surface Rendering and Notifications + +Returning a Payment Authentication Action asks the Platform to render its +`config.url`. Before navigating, the Platform **MUST** validate the URL and +install its message receiver so an immediately completing surface cannot race +initialization. + +The `postMessage` and native-webview mechanics follow +[Embedded Protocol — Communication Channels](embedded-protocol.md#communication-channels). +Payment Authentication uses the Action methods below. It does not use a +capability-specific Embedded Protocol `ready` method or `MessageChannel` upgrade; +`action.ready` is its scoped handshake. + +Each Action occurrence uses a fresh, isolated browsing context. Device data +collection is hidden and non-interactive. A 3DS challenge is visible, blocks +interaction with the rest of Checkout, and **SHOULD** show a loading state until +its content renders. Web Platforms use an iframe or other handler-approved +browser context. For messages from a native surface to the Platform, native +Platforms **MUST** expose a JSON-string bridge through +`window.EmbeddedActionProtocolConsumer` or +`window.webkit.messageHandlers.EmbeddedActionProtocolConsumer`. For the ready +response from the Platform to the surface, the Platform calls +`window.EmbeddedActionProtocol.postMessage()` with the JSON-stringified JSON-RPC +response. + +### Ready Handshake + +Before beginning Action-specific work, every surface **MUST** send an +`action.ready` JSON-RPC request. Its `params.id` identifies the outstanding +Action occurrence, while the JSON-RPC envelope `id` correlates the ready +response. Its `params.version` **MUST** equal the active Payment Authentication +extension version in the containing Checkout. + + +```json +{ + "jsonrpc": "2.0", + "id": "ready-1", + "method": "action.ready", + "params": { + "id": "ddc-1", + "version": "{{ ucp_version }}" + } +} +``` + +The Platform **MUST** validate the message source, authorized origin, Action +occurrence, and version before responding. A source or origin failure is +untrusted and receives no response. If the trusted surface names an unknown +Action occurrence or a version other than the active version, the Platform +returns an error result, unmounts the surface, and follows non-successful +reconciliation. + +For a valid request, the Platform returns an empty JSON-RPC result. The +successful response confirms that the requested Payment Authentication extension +version was accepted: + + +```json +{ + "jsonrpc": "2.0", + "id": "ready-1", + "result": {} +} +``` + +The surface **MUST** wait for this successful response before beginning device +data collection, presenting a challenge, or sending `action.done` or +`action.error`. The version remains fixed for the lifetime of that Action +occurrence. + +After the handshake, surfaces send `action.done` or `action.error` notifications +defined by the +[payment Action OpenRPC](site:services/payment-actions/embedded.openrpc.json). +On the web, the Platform **MUST** validate `event.source` against the mounted +surface and `event.origin` against the origin set authorized by the payment +handler. After a valid terminal notification, it unmounts the surface and +ignores duplicate or late notifications from that context. + +### Buyer Dismissal + +Cancellation inside a 3DS surface is a provider outcome, not a surface error. +After recording it for the Business, the surface **SHOULD** send `action.done`. +If the Buyer instead dismisses a Platform-owned container before a terminal +notification, the Platform unmounts it and follows the non-successful surface +reconciliation path above. + +## Security and Data Handling + +Web Platforms **MUST** follow the shared +[Embedded Protocol security requirements](embedded-protocol.md#security) for +CSP, iframe sandboxing, credentialless iframe evaluation, and strict origin +validation. [Embedded Checkout security](embedded-checkout.md#security-for-web-based-hosts) +shows how a UCP capability applies those requirements. Payment Authentication +uses its own `action.ready` handshake and terminal notification methods; it does +not adopt the Embedded Protocol's capability lifecycle or delegation messages. + +In addition, Platforms **MUST**: + +- parse `config.url` with a conformant URL parser; it **MUST** be absolute and + use the `https` scheme; +- enforce the handler's trust policy on the initial URL and every redirect; +- grant only the frame or webview capabilities required by the handler, which + **MUST** document any deviation from the shared sandbox baseline; +- avoid logging session URLs or leaking them through referrers; and +- treat all completion and diagnostic notifications as advisory. diff --git a/docs/specification/payment-handler-guide.md b/docs/specification/payment-handler-guide.md index fa79966bb..2691510bf 100644 --- a/docs/specification/payment-handler-guide.md +++ b/docs/specification/payment-handler-guide.md @@ -723,6 +723,35 @@ instrument acquisition. Instead, the specification **SHOULD** clearly document: business for usage, which is critical for security, based on the available `config` and `checkout`. +### Payment Actions + +A payment handler may require the Platform to perform additional work while the +Business processes an instrument. The Business returns that work in the +Checkout's `actions` map. + +Handler authors have two options: + +- **Use the standard Payment Authentication extension.** Handlers that need + device data collection or a 3DS challenge use + [`dev.ucp.shopping.payment_authentication`](payment-authentication.md), which + defines the `dev.ucp.payment.device_data_collection` and + `dev.ucp.payment.three_ds_challenge` Action types. The handler specification + states which types it can cause and any provider-specific trust or fallback + requirements. +- **Define handler-specific Actions.** When the standard types do not fit, the + handler author publishes a Checkout extension that declares its Action type + keys and `config` shapes. Both the Business and Platform advertise that + extension before those Actions are emitted. + +For each handler-specific Action, document when it is emitted, the exact effect +its type gates, how the Platform processes its config, how the Business observes +its completion, whether processing the same occurrence is ever safe to retry, +and its trust, failure, abandonment, and fallback behavior. + +See [Overview — Actions](overview.md#actions) for the common envelope and +[Schema Authoring — Extension-Declared Action Types](site:documentation/schema-authoring.html) +for the extension schema pattern. + ### Processing **Definition:** The steps a participant (typically business or PSP) takes to @@ -807,6 +836,15 @@ Before publishing a payment handler specification, verify: - [ ] Binding requirements are specified - [ ] Checkout Payment Instrument creation and shape is well-defined +### Payment Authentication Actions (if applicable) + +- [ ] Every Action type is declared by a negotiated extension +- [ ] The gated effect, emission conditions, and sequencing are documented +- [ ] Instrument and handler association is unambiguous +- [ ] Unsupported, abandoned, expired, and failed fallbacks are documented +- [ ] Safe-retry conditions are defined, or retrying the same occurrence is forbidden +- [ ] Server-side outcome observation and pending-attempt cleanup are defined + ### Processing - [ ] Processing steps are enumerated and clear diff --git a/docs/specification/payment-handler-template.md b/docs/specification/payment-handler-template.md index b06a498ac..7aa045304 100644 --- a/docs/specification/payment-handler-template.md +++ b/docs/specification/payment-handler-template.md @@ -315,6 +315,26 @@ Content-Type: application/json --- +## Payment Actions [CONDITIONAL] + +{Include this section if the handler can cause Actions. Identify the negotiated +Checkout extension that declares them. Use the standard +[Payment Authentication extension](payment-authentication.md) for device data +collection and 3DS challenges; link to the defining extension specification for +custom Actions.} + +| Action type | When emitted | Gated effect | Handler-specific requirements | +| :---------- | :----------- | :----------- | :---------------------------- | +| `{action_type}` | {conditions} | {effect} | {provider-specific trust, fallback, retry, or cleanup behavior} | + +For standard Payment Authentication Actions, document only the behavior specific +to this handler, such as which types it can cause, trusted URL rules, how the +Action is associated with its payment instrument, and how the Business observes +provider state. For custom Actions, also document or link to their config and +Platform processing contract. + +--- +