npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@docknetwork/ap2

v0.5.0

Published

Basic AP2 support

Downloads

141

Readme

@docknetwork/ap2

AP2 mandate and receipt issuance/verification using the lightweight Dock crypto utilities. Mandates and receipts are compact ES256 SD-JWTs/JWTs compatible with the AP2 v0.2 spec.

See the AP2 documentation and specification for protocol details.

import {
  buildPaymentReceipt,
  computeMandateReference,
  generateSigner,
  signReceipt,
  verifyPaymentReceipt,
  verifyClosedPaymentMandate,
} from '@docknetwork/ap2';

const keypair = generateSigner();
// Use the exact compact presentation received from the Shopping Agent.
const closedMandatePresentation = receivedClosedMandatePresentation;
const receipt = buildPaymentReceipt({
  status: 'Success',
  iss: 'mpp.acme',
  iat: Math.floor(Date.now() / 1000),
  reference: computeMandateReference(closedMandatePresentation),
  payment_id: 'PAY-001',
});

const jwt = await signReceipt(receipt, {
  signer: keypair,
  type: 'payment',
});
// Verify the mandate first (signature, cnf key-binding, checkout binding, aud, expiry, nonce).
// openMandatePresentation is required -- it's how the trusted verification
// key is derived from the Open Mandate's own cnf.jwk, rather than trusted
// from a caller-supplied key. userPublicKey is also required: it's the
// User's own key (resolved independently, e.g. via DID resolution or a
// wallet registry), verified against the Open Mandate's issuer signature --
// without it, the cnf.jwk delegation inside a self-consistent but entirely
// forged Open + Closed Mandate chain would go unnoticed. expectedNonce is
// required too: Closed Mandate envelopes carry no exp of their own, so
// nonce -- checked against the single-use value the merchant generated for
// this transaction -- is what stops a validly-signed presentation from
// being replayed for a different one.
const mandateVerification = verifyClosedPaymentMandate(
  closedMandatePresentation,
  {
    openMandatePresentation, userPublicKey, expectedNonce, checkoutJwt, openCheckoutMandatePresentation,
  },
);

const result = verifyPaymentReceipt(jwt, {
  publicKey: keypair.publicKey(),
  expectedIssuer: 'mpp.acme',
  mandatePresentation: closedMandatePresentation,
  // Compose mandate verification into the receipt check so a bare reference
  // hash match cannot be mistaken for a fully verified mandate/receipt bundle.
  mandateVerification,
  maxReceiptAge: 300,
});

if (!result.verified) {
  throw result.error;
}

Checkout receipts use issueCheckoutReceipt and verifyCheckoutReceipt. The generic issueReceipt and verifyReceipt functions remain available for the Dock keypair flow. generateSigner() is a quick-start convenience -- a fresh ES256 keypair, the only algorithm these receipt functions currently accept. signer is really just {sign: function(Uint8Array): *}, so bringing your own signer (an HSM/KMS-backed one, or an existing key via Secp256r1Keypair.fromSeed/ fromPrivateKey, both re-exported from this package) works the same way -- generateSigner() isn't required. Verification returns { verified, receipt, protectedHeader } on success and { verified: false, error } on failure, following the credential SDK result style. Successful results may also include referenceVerified and mandateVerified when those checks were requested.

Payloads and signing

buildCheckoutReceipt and buildPaymentReceipt validate against the packaged AP2 JSON Schemas, clone the payload, and preserve extension properties. These schemas are also available as named exports (checkoutReceiptSchema, paymentReceiptSchema) for consumers that want the raw JSON Schema — e.g. for form generation or documentation — without re-validating through this package.

signReceipt is the BYOK entry point. Its signer.sign(data) function may be synchronous or asynchronous and must return an ES256 signature in 64-byte JOSE r || s form, or Dock's 65-byte form with a recovery byte:

const jwt = await signReceipt(receipt, {
  type: 'payment',
  kid: 'processor-key-1',
  signer: {
    async sign(data) {
      return kms.signEs256(data);
    },
  },
});

computeSdHash({ issuerJwt, disclosures }) is the shared RFC 9901 primitive. computeMandateReference(presentation) parses an AP2 presentation or chain, selects its final SD-JWT, removes a trailing key-binding JWT, and delegates to that primitive. encodeDisclosure([salt, claimName, value]) creates the base64url-encoded disclosure form used by both functions.

Mandates

AP2 v0.2 defines two mandate families — Checkout Mandate and Payment Mandate — each with an Open and Closed state. Mandates are self-signed SD-JWTs: Open Mandates are signed by the user's key (via a Trusted Surface, e.g. a wallet), Closed Mandates by the Shopping Agent's key endorsed in the Open Mandate's cnf claim. This package builds, signs, and verifies all four shapes:

import {
  buildOpenCheckoutMandate,
  signOpenCheckoutMandate,
  buildClosedCheckoutMandate,
  signClosedCheckoutMandate,
  verifyClosedCheckoutMandate,
  buildOpenPaymentMandate,
  signOpenPaymentMandate,
  buildClosedPaymentMandate,
  signClosedPaymentMandate,
  verifyClosedPaymentMandate,
  computeCheckoutHash,
} from '@docknetwork/ap2';

// 1. User's wallet signs an Open Checkout Mandate (once, up front).
const openCheckoutContent = buildOpenCheckoutMandate({
  vct: 'mandate.checkout.open.1',
  constraints: [/* checkout.line_items, checkout.allowed_merchants */],
  cnf: { jwk: agentPublicJwk },
});
const openCheckoutPresentation = await signOpenCheckoutMandate(openCheckoutContent, {
  signer: userSigner,
});

// 2. Later, the Shopping Agent closes it against a specific merchant checkout.
const closedCheckoutContent = buildClosedCheckoutMandate({
  vct: 'mandate.checkout.1',
  checkout_jwt: merchantSignedCheckoutJwt,
  checkout_hash: computeCheckoutHash(merchantSignedCheckoutJwt),
});
const closedCheckoutPresentation = await signClosedCheckoutMandate(closedCheckoutContent, {
  signer: agentSigner,
  nonce: 'merchant-supplied-nonce',
  openMandatePresentation: openCheckoutPresentation,
});

// 3. The Merchant/Credential Provider verifies it. The verification key is
// always derived from the Open Mandate's own cnf.jwk -- there is no
// caller-supplied-key option, since a caller-supplied key that happens to
// verify a signature proves nothing about *whose* key it was authorized to be.
// userPublicKey (the User's own key, resolved independently -- e.g. via DID
// resolution or a wallet registry) is required too, and is checked against
// the Open Mandate's own issuer signature: without it, cnf.jwk could name
// anyone, since nothing would confirm the Open Mandate actually came from
// the User it claims to. expectedNonce -- the same single-use value the
// merchant generated in step 2 -- is required as well, since a Closed
// Mandate envelope has no exp of its own to bound how long it stays replayable.
const result = verifyClosedCheckoutMandate(closedCheckoutPresentation, {
  openMandatePresentation: openCheckoutPresentation,
  userPublicKey,
  expectedNonce: 'merchant-supplied-nonce',
});
if (!result.verified) throw result.error;

Payment Mandates follow the same build*/sign*/verifyClosedPaymentMandate pattern, binding to a Checkout Mandate via transaction_id (a hash of checkout_jwt, verified against a checkoutJwt passed to verifyClosedPaymentMandate).

build* validates content against this package's JSON Schemas (src/schemas/*.json), generated from the real upstream AP2 source (upstream-ap2-schemas/, a pinned mirror of google-agentic-commerce/AP2) via npm run generate-schemas — re-run that script and rebuild after re-vendoring a newer upstream tag; don't hand-edit src/schemas/*.json directly. Two constraints are schema-required, not just conventional: an Open Checkout Mandate's constraints must contain at least one checkout.line_items entry, and an Open Payment Mandate's constraints must contain a payment.reference entry (with a conditional_transaction_id — see the Open Payment Mandate schema for the full set of supported constraint types, including payment.allowed_payees, payment.allowed_payment_instruments, payment.allowed_pisps, payment.amount_range, payment.budget, payment.agent_recurrence, and payment.execution_date).

The signer for both signing functions uses the same BYOK signer.sign(data) contract as signReceipt — bridge it to a wallet-held key without ever exposing the private key to this package.

Known caveat: sd_hash (the claim binding a Closed Mandate back to its Open Mandate) is implemented here as the RFC 9901 hash of the referenced Open Mandate's full presentation (computeSdHash), matching the Agent Authorization Framework's "Mandate Receipt... calculated in the same manner as sd_hash" language. The AP2 spec's own worked example for the Closed Checkout Mandate shows an sd_hash value that instead numerically matches the digest of its own checkout_jwt disclosure — this looks like a reused placeholder string in the docs rather than a deliberate alternate meaning (the same string implausibly also appears as an unrelated conditional_transaction_id example elsewhere), but it has not been confirmed against a reference implementation or the normative "Delegate SD-JWT" individual draft this spec depends on for chain verification.

Mandate lifecycle and revocation

AP2 v0.2 defines no revocation mechanism for Mandates, and this package does not invent one. The spec's only lifecycle-management guidance (§ Mandate Management) reads: "In the case of external Trusted Surfaces it could make sense to allow for management of delegated Mandates but that is outside the scope of this specification." Managing (and revoking) a granted Open Mandate is left entirely to the Shopping Agent / wallet UI layer as a local, User-facing concern — there is no protocol-level status check, status list, or credentialStatus-style claim a verifier can query, and no way for a Merchant or Credential Provider to learn that a User revoked a mandate after the fact.

exp is therefore the only verifier-enforceable way a mandate's lifetime ends, and it's schema-optional — buildOpenCheckoutMandate/ buildOpenPaymentMandate both accept content with no exp at all, which this package then treats as never expiring (see "accepts a mandate with no exp" in the test suite). The spec (§ Autonomous) only RECOMMENDS setting exp "to the smallest value that will allow the Shopping Agent to complete the assigned task" — it does not require it, and this package does not enforce it either, since doing so would exceed what the spec actually mandates. If your integration needs a shorter effective lifetime or a way to invalidate a mandate mid-flight, set exp deliberately tight yourself (re-issuing Open Mandates as needed) and/or track revocation state out of band on your own side (e.g. a Merchant's own record of voided orders, or a Credential Provider invalidating the payment token it separately issued — the one place the spec does gesture at invalidation, under § Security Considerations' Double Spend discussion, and only as a MAY).

Adding a proprietary status/revocation field would not help: it would be invisible to any AP2-conformant counterparty that isn't running this exact package, and the spec's Constraint mechanism explicitly requires unknown constraint types to fail evaluation (§ Agent Authorization: "Any unknown Constraints MUST be treated as failing evaluation"), so a custom constraint-based revocation scheme would break interop with stock verifiers rather than add safety.

Verification guarantees

Receipt verification:

  • accepts only ES256 compact JWTs with typ: JWT;
  • requires expectedIssuer, binding the caller-supplied trusted public key to the signed iss claim;
  • validates integer iat against currentDate (default: now), clockTolerance (default: 30 seconds), and optional maxReceiptAge;
  • enforces the concrete checkout/payment schema and its status-dependent fields.

When mandatePresentation is supplied, verification also computes the receipt reference over the exact final SD-JWT presentation. The input is the issuer-signed JWT and selected disclosures with their original tilde framing. A trailing key-binding JWT is excluded, _sd_alg selects SHA-256, SHA-384, or SHA-512, and SHA-256 is used when _sd_alg is absent. Successful results then include referenceVerified: true.

That reference check does not verify the mandate itself. Pass mandateVerification to compose a prior mandate-verification result (or a synchronous callback that returns one) into verifyReceipt / verifyPaymentReceipt / verifyCheckoutReceipt. Accepted shapes are Dock-style { verified: true } and @ar-agents/ap2-style { ok: true }. On success the receipt result includes mandateVerified: true; on failure the mandate error/reason is returned as { verified: false, error }.

For dispute evidence, mandate verification should cover signatures, disclosure digests, key binding, delegation, audience, nonce, dates, constraints, checkout_hash, and payment transaction linkage — then pass that result as mandateVerification alongside mandatePresentation.

Examples

Run the complete payment receipt issue-and-verify example:

yarn example:receipt

See examples/issue-and-verify-receipt.mjs.

Run the Closed Checkout Mandate verification example, showing how to resolve userPublicKey from a User's DID:

yarn example:did-mandate

See examples/verify-mandate-with-did-user-key.mjs.