Merchants already have HTTP APIs. The gateway adds agent discovery, invocation and payment protocols without moving that logic into every backend or routing funds through a hosted intermediary.
┌──────────────────────────────────────────────────────────────────────────────┐
│ AI Agent │
└───────────────────────────────────────┬──────────────────────────────────────┘
│ MCP (tools/list, tools/call) · A2A · ACP · HTTP
│ payment proof · Agent-Authorization
┌───────────────────────────────────────▼──────────────────────────────────────┐
│ Agent Commerce Gateway │
│ (runs in MERCHANT infrastructure) │
│ │
│ protocol adapters: mcp · http · a2a · acp │
│ │ │
│ ▼ │
│ ExecutionPipeline │
│ │ │
│ ┌────────────────────┬───┴─────────────┬────────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ AuthorizationProvider PaymentProvider BackendExecutor ReceiptStore │
│ (ap2) (x402, mpp) (bounded HTTP) (SQLite) │
└───────────────────────────────────────┬──────────────────────────────────────┘
│
┌────────────▼───────────┐
│ Merchant Backend API │
└────────────────────────┘
payment protocol: buyer → merchant, directly. Never through the gateway.
Three properties define the design:
-
Self-hosted. The gateway runs in the merchant's infrastructure. There is no central service operated by this project, and none is planned.
-
Non-custodial. Payment goes from buyer to merchant without passing through the gateway, which needs no buyer or merchant signing key. Local facilitator mode holds a separate gas-paying signer in process; the Security model says where it is allowed.
-
Configuration, not rewriting. A merchant describes an existing endpoint in
config.yaml.agent-commerce import openapican generate draft resource definitions:OpenAPI -> importer -> resource definitions -> canonical model -> pipelineThe importer stops at the config boundary. Runtime code does not read the OpenAPI document or use OpenAPI-specific core types, so imported and hand-written resources follow the same path. See OpenAPI import.
The core defines protocol-neutral commerce operations. It uses stable method
names such as x402 and mpp for selection, but imports no MCP, JSON-RPC,
x402 SDK, EIP-712 or EVM wire types. Its main concepts are:
| Concept | Type |
|---|---|
| a thing an agent can invoke | CommerceResource |
| what it costs | Pricing |
| how to reach the merchant backend | BackendHandler |
| one inbound call | CanonicalRequest |
| what must be paid, and the opaque provider challenge | PaymentRequirement |
| what happened to a payment | PaymentResult |
| proof of delivery | CommerceReceipt |
| everything worth observing | CommerceEvent |
Adapters and providers own protocol- and rail-specific representations.
Provider-native challenges pass through the core as opaque
Record<string, unknown> values in PaymentRequirement.challenge.accepts.
New protocols map to the canonical model at their boundary instead of changing the pipeline. See Adapter guide.
Every adapter uses this pipeline, so each surface gets the same validation, authorization, payment and receipt behavior.
CanonicalRequest
│
├─ resolve resource ─────────────────────► RESOURCE_NOT_FOUND
├─ validate input and the backend
│ request shape ────────────────────────► INPUT_INVALID / BACKEND_ERROR
├─ resolve price
│
├─ free ───────────────────────────────────────────────────┐
└─ paid │
├─ create requirement │
├─ no proof ─────────────► PaymentRequiredOutcome │
├─ verify rejected ────────────► PAYMENT_INVALID │
├─ replayKey missing ──────────► PAYMENT_INVALID │
├─ authorize + reserve ────────► AUTHORIZATION_* │
├─ reserve replayKey ──────────► PAYMENT_REPLAYED │
│ or STORAGE_ERROR │
└─ upfront flow: settle ► PAYMENT_SETTLEMENT_FAILED │
│
┌──────────────────────────────────────────────────────────┘
├─ call merchant backend ────────────────► BACKEND_TIMEOUT / BACKEND_ERROR
├─ authorization flow (default): settle ─► PAYMENT_SETTLEMENT_FAILED
├─ store receipt + events ───────────────► logged on failure, never thrown
└─ ExecutionOutcome (delivered)
verify never moves money; only settle does. The replay reservation sits
deliberately between them: a duplicate authorization is rejected before any
funds move. The provider's requirement chooses the flow: x402 settles after a
successful backend call by default, and MPP before it. Under authorization,
a backend failure settles nothing.
Authorization is optional per resource. When required, its reservation sits between payment verification and settlement. It is consumed when the payment settles, marked when the outcome is unknown, and released when no money moved: a rejected settlement, a failed replay reservation, or a backend failure before settlement. See AP2.
The protocol adapter assigns one requestId. The pipeline carries it through
errors, events, payment attempts and the receipt, linking the audit records for
one invocation.
Event sequence for a successful paid request:
authorization flow (default)
resource.requested → payment.verified → backend.called → payment.settled
→ resource.delivered
upfront flow
resource.requested → payment.verified → payment.settled → backend.called
→ resource.delivered
The challenge before it is a separate request with its own requestId:
resource.requested → payment.required.
A resource requiring authorization adds authorization.verified (or
authorization.rejected) between the request and the payment events. The event
types are the same whatever the method, so reading the audit trail never
requires knowing what AP2 is.
An adapter that fails to start is marked unhealthy without stopping other
adapters. Readiness and doctor report the failure. Protocol and payment
failures remain separate.
SQLite stores receipts, events and payment_attempts behind ReceiptStore.
A UNIQUE constraint on payment_attempts.replay_key makes replay reservation
atomic. The pipeline omits raw payment proofs, and SQLite row mapping redacts
recognized secret fields before persistence.
LLMs may call the gateway but are not runtime dependencies. Routing, validation, payment verification, receipts and protocol adaptation are deterministic. CI uses a deterministic buyer and local chain, without a model, public RPC or real funds.
| Concern | Path |
|---|---|
| canonical model, errors, pipeline | src/core |
| config schema, loader, env substitution | src/config |
| Fastify server, routes, adapter mounting | src/gateway |
| MCP adapter | src/protocols/mcp |
| A2A adapter (experimental) | src/protocols/a2a |
| ACP checkout adapter (experimental) | src/protocols/acp |
| shared bearer check, body reader, URL join | src/protocols/http.ts |
| x402 provider + local/remote facilitator | src/payments/x402 |
| MPP provider, settling through an x402 provider | src/payments/mpp |
| AP2 mandate verification | src/authorization/ap2 |
| SQLite receipts/events/attempts | src/storage/receipts |
| OpenAPI import (config ingress only) | src/openapi |
CLI (init, import openapi, validate, doctor, demo, version) |
src/cli |
| demo merchant API / buyer / dashboard | demo/* |
| MockUSDC + local chain scripts | contracts/, scripts/chain/ |