Tenzro Testnet is live —request testnet TNZO
← Back to Tutorials

Agent Payments with Visa TAP

PaymentsIntermediate25 min

Visa Trusted Agent Protocol (TAP) brings cryptographic identity verification to agentic commerce. Using RFC 9421 HTTP Message Signatures, agents prove their identity by signing HTTP requests with their private keys. TAP enables transparent, verifiable agent payments without relying on API keys, tokens, or session state—just cryptographic proof of identity at the edge.

Prerequisites

  • Connection to Tenzro MCP server at mcp.tenzro.network/mcp
  • Registered agent identity (did:tenzro:machine:*)
  • Funded wallet with TNZO balance
  • Basic understanding of HTTP signatures and cryptographic keys

What You'll Learn

  • Create Visa TAP payment challenges with domain binding
  • Sign HTTP requests using RFC 9421 HTTP Message Signatures
  • Verify agent identity through a 7-stage cryptographic verification pipeline
  • Settle payments on Tenzro Ledger with receipt verification

What is Visa TAP?

Visa Trusted Agent Protocol (TAP) is a payment protocol designed for the agentic economy. Unlike traditional payment methods that rely on API keys or bearer tokens, TAP uses RFC 9421 HTTP Message Signatures to cryptographically bind payments to agent identities.

Key features of Visa TAP:

  • Cryptographic identity: Agents sign requests with their private keys, verified against TDIP identities
  • RFC 9421 compliance: Uses standard HTTP signature format (Signature-Input + Signature headers)
  • Domain binding: Signatures tied to specific domains to prevent cross-site reuse
  • Replay protection: Nonces and timestamp validation prevent signature replay attacks
  • CDN-level verification: Signatures verified at the edge before requests reach application servers
  • Transparent commerce: All payment requests are cryptographically auditable

Step 1: Create a Visa TAP Payment Challenge

Use the create_payment_challenge MCP tool to generate a Visa TAP payment challenge for a resource:

curl -s -X POST "https://mcp.tenzro.network/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: $(uuidgen)" \
  -d '{
    "jsonrpc": "2.0",
    "id": 7,
    "method": "tools/call",
    "params": {
      "name": "create_payment_challenge",
      "arguments": {
        "protocol": "visa-tap",
        "resource": "/api/inference",
        "amount": 250000,
        "asset": "TNZO",
        "recipient": "0x0000000000000000000000000000000000000001"
      }
    }
  }'

Response:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": {
          "challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
          "protocol": "visa-tap",
          "resource": "/api/inference",
          "amount": 250000,
          "asset": "TNZO",
          "recipient": "0x0000000000000000000000000000000000000001",
          "chain_id": 1337,
          "required_headers": [
            "@method",
            "@authority",
            "@path",
            "content-digest",
            "content-type"
          ],
          "max_signature_age_secs": 480,
          "domain_binding": "api.tenzro.network",
          "created_at": "2026-03-24T14:20:00Z",
          "expires_at": "2026-03-24T15:20:00Z"
        }
      }
    ],
    "isError": false
  }
}

The challenge includes required_headers (the HTTP fields that must be signed), max_signature_age_secs (480 seconds = 8 minutes), and domain_binding to prevent cross-domain signature reuse.

Step 2: Sign the Request with RFC 9421

The agent must sign the HTTP request using RFC 9421 HTTP Message Signatures. The signature covers specific HTTP fields (method, authority, path, content-digest, content-type) and includes metadata (created timestamp, keyid, nonce).

Rust example using Tenzro SDK:

use tenzro_payments::visa_tap::VisaTapClient;
use tenzro_crypto::signers::Ed25519Signer;

// Agent's signing key
let signer = Ed25519Signer::from_private_key(&agent_private_key)?;
let agent_did = "did:tenzro:machine:agent123";

// Create TAP client
let client = VisaTapClient::new(signer, agent_did);

// Build the HTTP request parts
let request_parts = HttpRequestParts {
    method: "POST",
    authority: "api.tenzro.network",
    path: "/api/inference",
    headers: vec![
        ("content-type", "application/json"),
        ("content-digest", "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"),
    ],
    body: r#"{"model":"gemma4-9b","prompt":"What is Tenzro?"}"#,
};

// Sign the request (produces Signature-Input and Signature headers)
let signed_request = client.sign_request(&request_parts).await?;

println!("Signature-Input: {}", signed_request.signature_input);
println!("Signature: {}", signed_request.signature);

The resulting HTTP request headers:

POST /api/inference HTTP/1.1
Host: api.tenzro.network
Content-Type: application/json
Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type");created=1711289600;keyid="did:tenzro:machine:agent123";nonce="abc123xyz456"
Signature: sig1=:MEUCIQDXlI2fZH+sTmSu6Z1xN3vKjP5wQmN7rL8sF4tG9yH3xQIgRpLsK2mN8vT6wY1zU4cD5eF7gH9iJ0kL2mN4oP6qR8s=:

{"model":"gemma4-9b","prompt":"What is Tenzro?"}

Step 3: Verify Payment Credential

Use the verify_payment MCP tool to verify the signed request and settle the payment on-chain:

curl -s -X POST "https://mcp.tenzro.network/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: $(uuidgen)" \
  -d '{
    "jsonrpc": "2.0",
    "id": 8,
    "method": "tools/call",
    "params": {
      "name": "verify_payment",
      "arguments": {
        "challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
        "protocol": "visa-tap",
        "payer_did": "did:tenzro:machine:agent123",
        "amount": 250000,
        "asset": "TNZO",
        "signature": "MEUCIQDXlI2fZH+sTmSu6Z1xN3vKjP5wQmN7rL8sF4tG9yH3xQIgRpLsK2mN8vT6wY1zU4cD5eF7gH9iJ0kL2mN4oP6qR8s=",
        "signature_input": "sig1=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\");created=1711289600;keyid=\"did:tenzro:machine:agent123\";nonce=\"abc123xyz456\""
      }
    }
  }'

Response:

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "content": [
      {
        "type": "text",
        "text": {
          "verified": true,
          "receipt": {
            "payment_id": "visa_tap_pay_u4v5w6x7y8z9",
            "challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
            "payer_did": "did:tenzro:machine:agent123",
            "amount": 250000,
            "asset": "TNZO",
            "recipient": "0x0000000000000000000000000000000000000001",
            "settlement_tx": "0x1122334455667788...",
            "settled_at": "2026-03-24T14:22:35Z",
            "stages_passed": [
              "HeaderExtraction",
              "KeyRetrieval",
              "TimestampValidation",
              "ReplayPrevention",
              "DomainBinding",
              "CryptographicVerification",
              "ResultConstruction"
            ]
          }
        }
      }
    ],
    "isError": false
  }
}

The response shows verified: true and a receipt with the settlement transaction hash. The stages_passed array documents which verification stages succeeded.

Step 4: Understand the 7-Stage Pipeline

Visa TAP verification runs through a rigorous 7-stage pipeline to ensure cryptographic integrity and prevent fraud:

// 7-Stage Visa TAP Verification Pipeline

// Stage 1: Header Extraction
// Parses Signature-Input and Signature headers
// Example: sig1=("@method" "@authority" "@path" ...);created=1711289600;keyid="did:tenzro:machine:agent123"

// Stage 2: Key Retrieval
// Resolves agent DID via TDIP registry
// Fetches Ed25519 public key from identity document

// Stage 3: Timestamp Validation
// Checks 'created' parameter within max_signature_age_secs (480 seconds = 8 minutes)
// Prevents stale signatures from being replayed

// Stage 4: Replay Prevention
// Checks nonce against cache (in-memory or Redis)
// Each nonce can only be used once per challenge

// Stage 5: Domain Binding
// Verifies @authority matches required domain ("api.tenzro.network")
// Prevents cross-domain signature reuse

// Stage 6: Cryptographic Verification
// Constructs canonical signing payload per RFC 9421
// Verifies Ed25519 signature with agent's public key

// Stage 7: Result Construction
// Builds VerificationResult with all metadata
// Returns receipt with settlement transaction hash

Stage Breakdown

  1. Header Extraction — Parses Signature-Input and Signature headers per RFC 9421
  2. Key Retrieval — Resolves agent DID via TDIP registry, fetches Ed25519 public key
  3. Timestamp Validation — Confirms created timestamp is within 8-minute window
  4. Replay Prevention — Checks nonce against cache (in-memory or Redis), each nonce usable once
  5. Domain Binding — Verifies @authority matches required domain (api.tenzro.network)
  6. Cryptographic Verification — Constructs canonical signing payload, verifies Ed25519 signature
  7. Result Construction — Builds VerificationResult with all metadata and settlement receipt

Step 5: Check Settlement Receipt

The settlement receipt provides a complete audit trail of the payment:

{
  "payment_id": "visa_tap_pay_u4v5w6x7y8z9",
  "challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
  "payer_did": "did:tenzro:machine:agent123",
  "amount": 250000,
  "asset": "TNZO",
  "recipient": "0x0000000000000000000000000000000000000001",
  "settlement_tx": "0x1122334455667788...",
  "settled_at": "2026-03-24T14:22:35Z",
  "stages_passed": [
    "HeaderExtraction",
    "KeyRetrieval",
    "TimestampValidation",
    "ReplayPrevention",
    "DomainBinding",
    "CryptographicVerification",
    "ResultConstruction"
  ]
}

How It Differs from MPP

Visa TAP (Cryptographic Identity)

  • RFC 9421 HTTP signatures— Every request signed with agent's private key
  • Stateless verification — No session management, signatures verified independently
  • CDN-level authorization — Signatures can be verified at edge proxies before application layer
  • Domain binding — Signatures tied to specific domains to prevent cross-site reuse
  • Identity-first — Payment authorization tied directly to cryptographic identity

MPP (Session-Based Payments)

  • Session tokens — Challenge → credential → voucher flow with session state
  • Stateful verification — Requires session management and voucher tracking
  • Application-level authorization — Verification happens at application server
  • Session binding — Credentials tied to session IDs
  • Payment-first — Authorization based on payment proof (vouchers)

Use Visa TAP when you need cryptographic agent identity verification, CDN-level authorization, or transparent commerce auditing. Use MPPfor streaming services, per-token billing, or when integrating with Stripe's Machine Payments Protocol.

Security Considerations

What's Next?

Ready to Accept Visa TAP Payments?

Explore the Tenzro Rust SDK for production-ready Visa TAP client and server implementations with RFC 9421 compliance and automatic signature verification.