Skip to main content

Payment Protocols

Agent payment protocols let autonomous systems prove intent, permissions, and constraints before money moves. Without them, you have no way to verify that an agent was actually authorized to initiate a payment — or to what limits. Anima supports three major agent payment protocols through a single unified interface, ProtocolRouter, so your fraud engines and limit checks stay protocol-agnostic.

Protocol comparison

ProtocolPrimary primitiveBest fitDelegation model
Visa TAPHTTP signatures + noncesReal-time API authorizationKey-based agent identity
Google AP2Mandates + capabilitiesCheckout and shopping tasksMulti-hop narrowing chain
Mastercard VISD-JWT credentialsConstrained delegated paymentsL1 → L2 → L3 credential chain

ProtocolRouter

ProtocolRouter gives you a single entry point to verify payment requests across all supported protocols. Pass the incoming request and it routes verification to the correct protocol handler, then returns a normalized result.
protocol-router.ts
const verification = await protocolRouter.verify({
  protocol: request.protocol,
  headers: request.headers,
  body: request.body,
  featureFlags: {
    visaTap: true,
    googleAp2: true,
    mastercardVi: true,
  },
  fallback: "deny",
});
Normalize all protocol outputs into one internal policy schema so your fraud and limit engines stay protocol-agnostic. This lets you apply consistent rules without branching logic per protocol.

Protocol details

Visa TAP secures agent-initiated payment requests with HTTP Message Signatures (RFC 9421) and registry-verifiable agent keys.Supported algorithms: Ed25519, rsa-pss-sha256Replay protection
FieldRequirementPurpose
Nonce64-byte base64 valuePrevent duplicate request replay
Freshness window8 minutes max skewReject stale captured requests
Nonce cacheTTL ≥ 8 minutesGuarantee one-time use within validity window
Agent Registry API
  • GET /api/agents/:id — Fetch agent identity and public keys
  • POST /api/agents/:id/keys — Register a new signing key
  • POST /api/agents/:id/revoke — Revoke a compromised key
Signing example
visa-tap-sign.ts
import { createHash, randomBytes, sign } from "crypto";

const body = JSON.stringify({ amount: 1250, currency: "USD" });
const nonce = randomBytes(64).toString("base64");
const created = Math.floor(Date.now() / 1000);
const digest = createHash("sha-256").update(body).digest("base64");

const signatureBase = [
  '"@method": post',
  '"@path": /payments/authorize',
  '"x-agent-nonce": ' + nonce,
  '"x-agent-created": ' + created,
  '"digest": sha-256=:' + digest + ":",
].join("\n");

const signature = sign(null, Buffer.from(signatureBase), privateKey).toString(
  "base64",
);

const signatureInput =
  'sig1=("@method" "@path" "@x-agent-nonce" "@x-agent-created" "digest");alg="ed25519"';
Validate signature, timestamp, nonce uniqueness, and key status in a single atomic verification transaction.
Persist nonce and signature input hashes for short-term forensic replay analysis.
AP2 defines structured mandates and delegation chains so agents can execute commerce actions with explicit, narrow authority.Mandate types: Cart, Intent, PaymentScopes: checkout, payment, browse, compare, negotiateCapabilities
CapabilityDescription
select_itemChoose candidate products or services
add_to_cartModify cart composition and quantities
checkoutSubmit checkout details up to the payment step
payExecute an authorized payment transaction
confirm_orderConfirm and persist the final order receipt
manage_subscriptionCreate, update, or cancel recurring service plans
Multi-hop delegationDelegation chains narrow authority at each hop — every child mandate is a strict subset of its parent. Child scopes must be a subset of parent scopes, and child capabilities must be a subset of parent capabilities.Mandate parsing example
ap2-parse-mandate.ts
type Scope = "checkout" | "payment" | "browse" | "compare" | "negotiate";
type Capability =
  | "select_item"
  | "add_to_cart"
  | "checkout"
  | "pay"
  | "confirm_order"
  | "manage_subscription";

interface MandatePayload {
  type: "Cart" | "Intent" | "Payment";
  scopes: Scope[];
  capabilities: Capability[];
  delegations: Array<{
    delegate: string;
    scopes: Scope[];
    capabilities: Capability[];
  }>;
}

const mandate = JSON.parse(payload) as MandatePayload;

const hasPaymentScope = mandate.scopes.includes("payment");
const canPay = mandate.capabilities.includes("pay");

if (!hasPaymentScope || !canPay) {
  throw new Error("Mandate does not authorize payment execution");
}
Persist parsed mandate snapshots and subset-validation results for every delegation hop.
Route all AP2 verification outcomes through ProtocolRouter.verify() for consistent fallback and metrics tagging.
Mastercard VI uses SD-JWT credential chains (signed with ES256 over P-256/SHA-256) to delegate payment authority from issuers to agents with explicit time and policy constraints.Credential chain levels: L1, L2 Immediate, L2 Autonomous, L3a/L3bConstraint types
ConstraintIntent
allowed_merchantPermit transactions only for specific merchant identities
line_itemsConstrain purchasable SKUs and quantities
allowed_payeeRestrict the destination payee account
amountSet maximum and minimum transaction amount boundaries
budgetEnforce a cumulative spending ceiling over a period
recurrenceDefine schedule rules for recurring charges
agent_recurrenceLimit autonomous repeated actions by agent identity
referenceRequire transaction metadata matching a policy reference
Credential chain validation example
mastercard-vi-validate.ts
const result = await verifyViChain({
  algorithm: "ES256",
  credentials: {
    l1,
    l2,
    l3,
  },
  now: Date.now(),
});

if (!result.signaturesValid) throw new Error("Invalid signature chain");
if (!result.hashLinksValid) throw new Error("sd_hash mismatch");
if (!result.timeWindowsValid)
  throw new Error("Credential expired or not yet valid");

const policyDecision = evaluateConstraints({
  constraints: result.constraints,
  paymentRequest,
});

if (!policyDecision.allowed) {
  throw new Error("Constraint violation:" + policyDecision.reason);
}
Never skip L3 terminal validation even when L1 and L2 are valid. L3 binds the delegated chain to the immediate transaction context.
Cache verified L1 trust anchors, but always recompute L2 and L3 validity and constraint checks per authorization request.
Feed normalized constraint results into your shared decision engine to keep Mastercard VI aligned with Visa TAP and AP2 policy outputs.