x402USDC on Base + Solana

x402-recurring

Signed mandate documents plus a client-side scheduler that pays per run over x402 and collects signed run reports. No subscriptions, no stored cards, no server-side pulls.

git clone https://github.com/nirholas/x402-recurring && cd x402-recurring
npm install && npm run dev

Pay on either rail

Every paid route answers with a dual-rail 402. The accepts array carries one EVM entry and one Solana entry; your client (or the checkout modal) picks whichever wallet it has. Each rail settles through its own facilitator — no single one handles both.

EVM · BASEUSDC on base-sepolia / base
0x40252CFDF8B20Ed757D61ff157719F33Ec332402
settled via x402.org/facilitator
SOLANAUSDC on solana-devnet / solana
WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW
settled via facilitator.payai.network

How a standing order works

1 · MANDATEFree signed document: task, cadence, price/run, budget
2 · 402Scheduler calls /execute; server quotes the mandate's exact price on both rails
3 · PAYClient signs USDC on Base or Solana; facilitator settles
4 · 200Signed run report + task artifact in the response body

The server never pulls funds. Your scheduler pays each run when it happens — cancel by simply not paying again. Budget caps (maxRuns, price ceiling, expiry) live in the signed mandate.

Pricing

RoutePriceReturns
POST /mandatesfreeSigned mandate document
POST /execute/:mandateId$0.005 (per mandate — its own `pricePerRun`)Signed run report: run number, budget state, and the task artifact
GET /mandates/:idfreeMandate status snapshot
GET /mandatesfreeMandate summaries
POST /verifyfree`{ valid: true | false }`
GET /healthfree`{ ok: true }`

Quickstart

import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";

const payFetch = wrapFetchWithPayment(fetch, privateKeyToAccount(process.env.PRIVATE_KEY));

const mandate = await (await fetch("http://localhost:4021/mandates", {
  method: "POST", headers: { "content-type": "application/json" },
  body: JSON.stringify({ task: { type: "heartbeat" }, pricePerRun: "$0.005", maxRuns: 5 })
})).json();

const report = await (await payFetch(
  `http://localhost:4021/execute/${mandate.mandateId}`, { method: "POST" }
)).json();   // signed run report — the purchased artifact

Discovery