Skip to main content

Agent quickstart (5 minutes)

Build an agent that makes a test payment against a sandbox merchant. By the end you have a working signed-Offer + PAR + token + KB-JWT + charge round trip against sandbox.oid4pay.com.

Prerequisites

Step 1: install the MCP server

npm install @oid4pay/oid4pay-mcp

The MCP server wraps the wire shapes so you do not need to implement DPoP proof generation, KB-JWT minting, or PAR posting yourself. The same underlying SDK is available standalone at @oid4pay/oid4ac-merchant; see node-merchant.

Step 2: generate keypairs

node --eval "
import('node:crypto').then(async ({ webcrypto }) => {
  const dpop = await webcrypto.subtle.generateKey({ name: 'Ed25519' }, true, ['sign']);
  const pkj = await webcrypto.subtle.generateKey({ name: 'Ed25519' }, true, ['sign']);
  for (const [name, k] of [['dpop', dpop], ['pkj', pkj]]) {
    const pub = await webcrypto.subtle.exportKey('jwk', k.publicKey);
    const prv = await webcrypto.subtle.exportKey('jwk', k.privateKey);
    process.stdout.write(JSON.stringify({ name, pub, prv }) + '\n');
  }
});
"

Save both JWKs. The dpop key signs DPoP proofs; the pkj key signs private_key_jwt assertions. They MUST be distinct keys.

Step 3: register the agent (RFC 7591 DCR)

curl -sS https://sandbox.oid4pay.com/oauth/register \
  -H "content-type: application/json" \
  -d '{
    "client_name": "my-test-agent",
    "token_endpoint_auth_method": "private_key_jwt",
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "redirect_uris": ["http://localhost:8080/callback"],
    "jwks": {"keys": [<pkj pub JWK>, <dpop pub JWK>]},
    "dpop_jwk_thumbprint": "<sha-256 thumbprint of dpop pub JWK>"
  }'

The response carries your client_id. Store it.

Step 4: pick an offer

curl -sS https://shop.alpacanica.com/.well-known/oid4ac-catalog | jq '.items[0]'

The catalog is RFC 9421 signed. The merchant SDK verifies the signature for you in the next step; the offer_url + offer_digest are what you pass downstream.

Step 5: run the MCP agent_payment_initiate tool

import { createClient } from "@oid4pay/oid4pay-mcp";

const client = createClient({
  asOrigin: "https://sandbox.oid4pay.com",
  clientId: process.env.OID4PAY_CLIENT_ID,
  dpopJwk: JSON.parse(process.env.OID4PAY_DPOP_PRIVATE_JWK),
  pkjJwk: JSON.parse(process.env.OID4PAY_PKJ_PRIVATE_JWK),
});

const result = await client.agent_payment_initiate({
  store_url: "https://shop.alpacanica.com",
  offer_url: "/products/test-pinata",
  max_amount_minor: 1500,
  currency: "EUR",
});

console.log(result);

The MCP tool walks the full path: fetches the signed Offer, verifies the RFC 9421 signature against the merchant's JWKS, posts a PAR, opens the wallet consent screen (or auto-consents on the kill-switch test mode), exchanges the code for a JWT-AT and SD-JWT VC mandate, mints the KB-JWT, presents the mandate to the merchant, and triggers the charge.

Step 6: read the result

The response shape:

{
  "charge_id": "ch_test_1abc...",
  "mandate_id": "mandate_test_xyz",
  "stripe_payment_intent_id": "pi_test_2def...",
  "offer_digest": "sha-256:<base64url>",
  "settled_at": "2026-05-14T10:23:01Z"
}

Next steps