@curless/agentbank-merchant-sdk
v0.2.0
Published
Merchant-side SDK for agentbank — accept agent payments: open a merchant-quoted checkout (you price it, an agent pays it), PaymentIntents, edge-safe webhook verification, x402 challenge helpers.
Readme
@curless/agentbank-merchant-sdk
Merchant-side SDK for agentbank. Drop into a merchant's backend (Shopify storefront, ClubMed booking flow, TikTok Shop seller service, anything that wants to accept agent payments) and you get:
checkout.open— the fast path. You price an order from your own catalog and open a checkout; an agent pays it; the money lands in your account. agentbank never stores your catalog — the price you pass is authoritative. You accept every agent-payment protocol (ACP, x402, A2A, UCP, AP2, …) with zero protocol code.AgentbankMerchant— typed client; also exposesPaymentIntents(create/get/capture/list/ledger-entries across ACP / x402 / AP2 / UCP / A2A),PaymentLinks,Customers,BalanceTransactions, andWebhookEndpoints. No refund method — refunds are Curless-originated (see below).verifyWebhook/parseVerifiedWebhook— HMAC-SHA256 signature verification (Stripe-stylet=…,v1=…), constant-time, edge-safe (Web Crypto, runs on Vercel Edge / Cloudflare Workers). Async —awaitthem.x402—buildChallenge()to emit your own canonical 402 from your route,decodeXPaymentHeader()to parse what the agent paid with.
One runtime dependency (@curless/agentbank-core, for portable crypto + the HTTP layer). Edge-safe — Web Crypto only, no node: builtins. Node 18+.
Refunds: agentbank exposes no refund-initiation endpoint. The merchant refunds in Curless (it holds the money); Curless executes the card refund and forwards the event, which advances the PI to
refunded/partially_refunded. Read the resulting state viapaymentIntents.get()/.list().
Fast path — open a merchant-quoted checkout
import { AgentbankMerchant } from '@curless/agentbank-merchant-sdk';
const agentbank = new AgentbankMerchant({
baseUrl: 'https://mcp.curless.ai',
apiKey: process.env.AGENTBANK_MERCHANT_TOKEN!, // merchant:write
merchantId: process.env.AGENTBANK_MERCHANT_ID!,
});
// In your "an agent wants to buy" handler: YOU set the price.
const checkout = await agentbank.checkout.open({
currency: 'EUR',
items: [
{ sku: 'resort-bali', name: 'Club Med Bali — 7 nights', quantity: 1, unitPrice: 192000 },
],
});
// Hand checkout.id back to the buyer agent; it pays, money lands in your account.
return { checkoutId: checkout.id, amount: checkout.amount, currency: checkout.currency };That's the whole integration: install, set three env vars, call checkout.open.
You stay the source of truth for your catalog and prices — nothing to sync.
Lower-level — PaymentIntents, webhooks, x402
import { AgentbankMerchant, parseVerifiedWebhook, x402 } from '@curless/agentbank-merchant-sdk';
const client = new AgentbankMerchant({
baseUrl: 'https://api.agentbank.dev',
apiKey: process.env.AGENTBANK_KEY!, // merchant:write
});
// Create a PaymentIntent the agent will pay against
const pi = await client.paymentIntents.create({
organizationId: 'org_123',
curlessMerchantId: 'curless_mch_clubmed_bali',
amount: 1160,
currency: 'USD',
protocol: 'x402',
items: [{ catalogItemId: 'room-suite', quantity: 1, unitPrice: 1160 }],
idempotencyKey: req.headers['idempotency-key'],
});
// Verify an inbound agentbank webhook (edge-safe; HMAC is async)
const event = await parseVerifiedWebhook(
process.env.WEBHOOK_SECRET!, // whsec_… from /v1/webhook-endpoints
await req.text(), // RAW body, NOT JSON-parsed
req.headers['x-agentbank-signature'],
);
// Or emit your own 402 challenge from your route
return new Response(JSON.stringify(x402.buildChallenge({
asset: '0x036CbD…F7e', // base-sepolia USDC
network: 'base-sepolia',
payTo: '0xMerchantAddr',
amount: '6200000', // 6.20 USDC
resource: 'https://shop.example/api/checkout',
})), { status: 402, headers: { 'content-type': 'application/json' } });Timeouts & errors
Every request carries a 30s default deadline (timeoutMs on the constructor
or per request; 0 disables) and supports AbortSignal via
RequestOptions.signal. ALL failures throw AgentbankError — transport
failures use status === 0 with code timeout / aborted / network_error.
