Skip to content

Ap2/v0.2 mandates#611

Open
mike-parkhill wants to merge 9 commits into
masterfrom
ap2/v0.2-mandates
Open

Ap2/v0.2 mandates#611
mike-parkhill wants to merge 9 commits into
masterfrom
ap2/v0.2-mandates

Conversation

@mike-parkhill

Copy link
Copy Markdown
Contributor

Brings @docknetwork/ap2 up to the current AP2 spec (v0.2), which
replaced the old Cart/Intent/Payment Mandate model with two mandate
families — Checkout Mandate and Payment Mandate — each in an
Open and Closed state, secured as SD-JWTs with a delegate_payload
envelope rather than flat JSON-LD VCs.

mike-parkhill and others added 8 commits July 22, 2026 10:46
Implements the AP2 v0.2 mandate model: Open/Closed Checkout Mandate and
Open/Closed Payment Mandate, as SD-JWTs with a delegate_payload envelope
(RFC 9901 array-element disclosure wrapping the mandate content, not a
flat payload). Mandates are self-signed (BYOK signer, same contract as
signReceipt) rather than issued by a third party.

Adds:
- buildOpen/ClosedCheckoutMandate, buildOpen/ClosedPaymentMandate
- signOpen/ClosedCheckoutMandate, signOpen/ClosedPaymentMandate
- verifyClosedCheckoutMandate, verifyClosedPaymentMandate — brought
  in-house per this package now owning mandate verification, rather
  than requiring an external dependency
- computeCheckoutHash / computeDisclosureDigest — the two distinct hash
  primitives mandates need (hash of checkout_jwt vs. per-disclosure
  digest), reusing existing crypto-utils SD-JWT primitives throughout

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Round-trips Open/Closed Checkout and Payment Mandates through real
Secp256r1Keypair signing and verification, plus negative cases (tampered
checkout_hash, wrong signing key).

Also decodes the AP2 spec's own literal encoded token from the
checkout_mandate page and asserts this package's digest computation
matches it exactly — the only way to catch a subtly-wrong digest
algorithm rather than one that's merely self-consistent. This caught two
transcription typos while writing the test (independently confirmed via
plain node:crypto that the spec page's human-readable JSON annotation
has a one-character typo, not this code).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a Mandates section to the README (usage example, sd_hash caveat)
and removes the now-outdated "mandate verification lives outside this
package" line. Adds a changeset (minor bump) per this monorepo's
changesets-based release process.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the 4 hand-authored mandate content schemas with the canonical
versions published at schema.truvera.io last week (adding their $id/
$metadata), which are meaningfully more precise than what was written
from just reading the spec's property tables:

- payment_amount.amount is integer minor units (not a bare number)
- Open Checkout Mandate constraints must contain at least one
  checkout.line_items entry (schema "contains", not just conventional)
- Open Payment Mandate constraints must contain a payment.reference
  entry with conditional_transaction_id — also schema-required
- Full constraint type coverage for Open Payment Mandate via proper
  $defs/oneOf: payment.agent_recurrence, allowed_payees,
  allowed_payment_instruments, allowed_pisps, amount_range, budget,
  execution_date, reference (previously only amount_range and
  allowed_payees were modeled at all)

Updates redactPaymentConstraints to also treat
payment.allowed_payment_instruments as array-element-disclosable,
matching the "x-selectively-disclosable-array" annotation on the
published schema (payment.allowed_pisps is not marked disclosable and
is left unredacted, same as before).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The round-trip test's Open Payment Mandate constraints now include a
payment.reference entry, and the two negative-path tests' Open Checkout
Mandates now include a minimal checkout.line_items constraint — both now
schema-required (see previous commit), where the tests previously used
an empty/incomplete constraints array that only happened to pass under
the old, looser hand-authored schema.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Points readers at the canonical schema.truvera.io documents (referenced
in each schema file's $id) and calls out the two schema-required
constraints and the full set of supported Open Payment Mandate
constraint types.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates @docknetwork/ap2 to support AP2 v0.2’s mandate model (Open/Closed Checkout and Payment mandates) using SD-JWT delegate_payload envelopes, alongside the existing receipt tooling.

Changes:

  • Added mandate build/sign/verify APIs (including selective disclosure handling and key-binding envelope verification).
  • Vendored upstream AP2 v0.2 JSON Schemas and added a generator to produce self-contained mandate schemas for AJV validation.
  • Added mandate-focused Jest coverage and updated package docs/changelog/version for the new public surface.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
packages/ap2/upstream-ap2-schemas/types/receipt_status.json Adds upstream Receipt Status type schema.
packages/ap2/upstream-ap2-schemas/types/pisp.json Adds upstream PISP type schema.
packages/ap2/upstream-ap2-schemas/types/payment_instrument.json Adds upstream Payment Instrument type schema.
packages/ap2/upstream-ap2-schemas/types/merchant.json Adds upstream Merchant type schema.
packages/ap2/upstream-ap2-schemas/types/amount.json Adds upstream Amount type schema.
packages/ap2/upstream-ap2-schemas/payment_receipt.json Adds upstream Payment Receipt schema.
packages/ap2/upstream-ap2-schemas/payment_mandate.json Adds upstream Closed Payment Mandate schema.
packages/ap2/upstream-ap2-schemas/open_payment_mandate.json Adds upstream Open Payment Mandate schema.
packages/ap2/upstream-ap2-schemas/open_checkout_mandate.json Adds upstream Open Checkout Mandate schema.
packages/ap2/upstream-ap2-schemas/checkout_receipt.json Adds upstream Checkout Receipt schema.
packages/ap2/upstream-ap2-schemas/checkout_mandate.json Adds upstream Closed Checkout Mandate schema.
packages/ap2/tests/mandates.test.js Adds byte-level and round-trip mandate tests.
packages/ap2/src/utils.js Adds mandate schema exports, mandate content validation, and mandate type constants.
packages/ap2/src/schemas/payment-mandate-open.json Adds generated Open Payment Mandate AJV schema (inlined types).
packages/ap2/src/schemas/payment-mandate-closed.json Adds generated Closed Payment Mandate AJV schema (inlined types).
packages/ap2/src/schemas/checkout-mandate-open.json Adds generated Open Checkout Mandate AJV schema (inlined types).
packages/ap2/src/schemas/checkout-mandate-closed.json Adds generated Closed Checkout Mandate AJV schema (inlined types).
packages/ap2/src/mandates.js Implements mandate SD-JWT construction, redaction, signing, and verification.
packages/ap2/src/index.js Exposes mandate APIs and schema exports from the package root.
packages/ap2/scripts/generate-schemas.mjs Adds schema generator to inline upstream types/*.json references into self-contained schemas.
packages/ap2/README.md Documents mandate support and updates examples accordingly.
packages/ap2/package.json Bumps version and adds generate-schemas script.
packages/ap2/CHANGELOG.md Documents the new 0.3.0 mandate feature set and schema generation approach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ap2-protocol.org/schemas/types/merchant.json",
"title": "Merchant",
"description": "Schema defining a Mechant object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ap2-protocol.org/schemas/types/amount.json",
"title": "Amount",
"description": "Schema defining an Amount and Current object",
Comment on lines +19 to +22
"constraints": {
"type": "array",
"description": "Array of constraints that the future checkout action must abide by.",
"items": {
Comment on lines +78 to +81
"risk_data": {
"type": "object",
"description": "An map of relevant risk signals collected by the trusted surface at time of mandate creation."
},
Comment on lines +45 to +48
"risk_data": {
"type": "object",
"description": "An map of relevant risk signals collected by the trusted surface at time of mandate creation."
},
Comment on lines +18 to +21
"constraints": {
"type": "array",
"description": "Array of constraints that the future checkout action must abide by.",
"items": {
Comment on lines +146 to +149
"risk_data": {
"type": "object",
"description": "An map of relevant risk signals collected by the trusted surface at time of mandate creation."
},
Comment on lines +193 to +198
"required": [
"id",
"name"
],
"description": "Schema defining a Mechant object"
},
Comment on lines +113 to +116
"risk_data": {
"type": "object",
"description": "An map of relevant risk signals collected by the trusted surface at time of mandate creation."
},
Comment on lines +2 to +4
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ap2-protocol.org/schemas/receipt-status.json",
"title": "Receipt Status",
Open Payment Mandates have no verify* counterpart today (only the
Closed mandate verifiers exist), so no consumer can read back a
mandate's own budget/allowed_payees/allowed_payment_instruments
constraints without hand-rolling SD-JWT disclosure resolution.

resolveOpenPaymentMandateContent parses and resolves an Open Payment
Mandate presentation's content -- including per-array-element
disclosures for payment.allowed_payees/payment.allowed_payment_instruments
-- validates it against the Open Payment Mandate schema, and checks
exp when present. It deliberately does not verify an issuer signature:
Open Payment Mandates are signed by the User's key, which is generally
a different key from the mandate's own cnf.jwk, so there is no single
key this package can check the envelope against on its own -- a caller
that independently knows the issuing key can verify it separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants