Skip to main content
All Work

fiat402

UPI payment scheme for the x402 protocol.

Year

2026

Status

Deployed

Stack

Node.js · Express · Next.js · Razorpay · Upstash Redis · PostgreSQL

Problem

The x402 protocol lets AI agents pay for resources autonomously, but every settlement path that exists assumes crypto rails or card networks. Nothing bridges it to UPI — the rail nearly a billion people in India actually transact on. An agent operating in the Indian market has no sanctioned way to pay a merchant, and no existing scheme even models what "pay with UPI" should look like inside x402's request/response shape.

Why it matters

Agentic commerce in India isn't optional infrastructure — it's the default expectation the moment an agent needs to buy something from a real merchant here. A payment scheme that can't settle over UPI can't actually operate in this market, regardless of how sound its cryptographic guarantees are elsewhere.

Approach

fiat402 defines `upi` as a first-class x402 payment scheme — scheme: "upi", network: "upi:in", asset: "INR", amounts in paise — settled through Razorpay Payment Links rather than routed around them. It ships via the self-facilitation pattern, a sanctioned x402 production path, so it doesn't wait on an upstream protocol PR to be usable. The core design constraint is authority, not settlement. x402 requests can be evaluated by AI, but AI is never allowed to approve a payment — the money belongs to the human, not the agent. A deterministic policy engine (hard spend ceiling, merchant allowlist, velocity limits) is the only component with final say. An AI advisory layer runs in parallel and can flag semantic mismatches — does this request actually match what the human authorized? — but its output is informational only, and it fails closed to "hold" on any error, timeout, or ambiguity.

Architecture

Facilitator on Node.js + Express, Upstash Redis for live state, Postgres (Neon/Supabase) for durable records, deployed on Render. Merchant reference app and dashboard on Next.js 15. The hardest engineering problem was reconciling x402's synchronous settlement model — it expects a verdict in roughly 1-2 seconds — with UPI's inherently asynchronous collect flow, where a human has up to 90 seconds to approve a payment on their phone. Naive polling doesn't scale and adds latency on every check. The fix is a bounded-wait pattern: the state machine (created → pending → approved/declined/expired → settled/failed) publishes transitions over a Redis pub/sub channel, and the `/settle` endpoint subscribes and blocks until either a resolution arrives or a hard timeout fires — no polling loop anywhere in the critical path. Razorpay webhooks drive every state transition, verified via HMAC-SHA256 over the raw request body before any JSON parsing touches it — a detail that matters because parsing first invalidates the signature. Every completed request produces one auditable Postgres record containing the x402 request ID, the Razorpay payment ID, the deterministic engine's verdict, and the AI's advisory recommendation side by side — so any payment can be reconstructed after the fact from a single row, not stitched together across logs.

System flow

Agent sends x402 request→Deterministic policy check (ceiling, allowlist, velocity)→AI advisory check runs in parallel (semantic match, fail-closed to hold)→Razorpay UPI Payment Link created→Bounded-wait on Redis pub/sub for human approval (up to 90s)→Webhook confirms payment.captured→State transitions to settled→Audit record written to Postgres

Execution Context

Built for Razorpay AI Buildathon 2026, Track 01, across eight sequential build modules — payment scheme types, deterministic policy engine, AI advisory layer, Razorpay integration, state machine, HTTP server, merchant middleware, and a client SDK — each shipped with its own test suite (59 tests passing across facilitator, scheme, and merchant packages) before moving to the next. The dashboard went through a second pass to replace an initial bare-bones UI with a proper state-machine visualization, built with Claude Code and design/animation skills, after live end-to-end testing surfaced real bugs a test suite alone hadn't caught — including a silent dropped-subscription bug in the WebSocket layer and a race condition where a payment event could fire and be missed during the confirm-gate wait.

Tradeoffs

Self-facilitation means fiat402 doesn't yet have to negotiate scheme adoption with the x402 ecosystem's upstream maintainers — but it also means the scheme isn't canonical until that conversation happens. We accepted that tradeoff because a working, sanctioned production path beats a spec proposal sitting in review. Keeping AI strictly advisory costs some responsiveness — a semantically ambiguous request always waits on a human "hold" rather than auto-approving — but that's the entire point: an agent should never be able to talk its way past the ceiling on its own authority.

Learnings

The gap between "the tests pass" and "this works" turned out to live almost entirely in timing — subscriptions that silently die, events that fire a few milliseconds before a listener is ready, links that stay payable for minutes after they should have expired. Unit tests verified logic; only running the full stack live, end to end, surfaced the actual production bugs. The instinct to keep AI advisory-only rather than authoritative also paid off in an unexpected way: it made the audit trail trivial to reason about, because every payment has exactly one authority to point to, not two systems that might disagree.