Claude
Skills
Sign in
Back

agentic-payments

Included with Lifetime
$97 forever

Agentic and machine-to-machine payments on Stellar. Covers x402 (HTTP 402 paid APIs via OZ Channels facilitator, fee-sponsored clients) and MPP (Machine Payments Protocol) in both Charge mode (per-request Soroban SAC) and Channel mode (off-chain commits, high-frequency). Defaults to USDC (SEP-41 SAC) on `stellar:testnet`/`stellar:pubnet` (CAIP-2). Use when selling a paid API to AI agents, building an x402 client, or designing a payment-channel architecture for high-frequency agent traffic.

Ads & Marketing

What this skill does


# Agentic Payments: x402 + MPP

Two complementary protocols for AI-agent and machine-to-machine payments on Stellar. Pick based on who depends on whom and how often the agent pays.

## Quick decision

| | x402 | MPP Charge | MPP Channel |
|--|------|------------|-------------|
| Per-request on-chain tx? | Yes (via facilitator) | Yes (Soroban SAC) | No (off-chain commits) |
| Needs facilitator? | Yes (OZ Channels) | No | No |
| Client needs XLM? | No (fees sponsored) | Optional (`feePayer`) | Yes |
| Setup complexity | Low | Low | Medium (deploy contract first) |
| Best for | Quickest setup, fee-free clients | No third-party dep | High-frequency agents |

- Selling an API, want zero-XLM clients → see **x402 Seller** below
- Calling an x402 API from an agent → see **x402 Buyer** below
- Selling an API, no facilitator dependency → see **MPP Charge** below
- Agent making many requests per session → see **MPP Channel** below
- Unsure → x402 (lowest friction to get started)

All protocols use USDC (SEP-41 SAC) by default; `stellar:testnet` / `stellar:pubnet` CAIP-2 network IDs.

## Related skills
- The Soroban SACs the protocols call → `../soroban/SKILL.md`
- USDC and other classic assets → `../assets/SKILL.md`
- Wallets and signing in the buyer client → `../dapp/SKILL.md`
- RPC simulation / submission patterns → `../data/SKILL.md`
- SEP-41 (token interface) and related standards → `../standards/SKILL.md`

---

# Part 1: x402 — Paid APIs + Agent Buyer Clients


## When to use x402
x402 is the right choice when:
- You want the fastest path to a paid API — minimal code, no contract deployment
- You want clients (including AI agents) to pay with **zero XLM** — the OZ Channels facilitator sponsors all network fees
- You're building on top of an existing x402 ecosystem (Coinbase, other chains)

Trade-off: you depend on OZ Channels (or a self-hosted relayer) for verification and settlement. If you need zero third-party dependency, use MPP Charge (Part 2 below) instead.

## How x402 works on Stellar

```
Client → GET /resource                               → Server
Client ← 402 Payment Required (payment requirements) ← Server
Client builds Soroban SAC USDC transfer
Client signs auth entries only (not the full tx envelope)
Client → GET /resource + X-PAYMENT header           → Server
Server → OZ Channels /verify + /settle              → Stellar (~5s)
Client ← 200 OK + resource
```

The key Stellar difference: clients sign **auth entries**, not full transaction envelopes. The facilitator assembles the transaction, pays fees, and submits. Clients need zero XLM.

## Seller: monetize an Express API

```bash
npm install @x402/express @x402/core @x402/stellar express dotenv
npm pkg set type=module
```

```js
// server.js
import "dotenv/config";
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { HTTPFacilitatorClient } from "@x402/core/server";
import { ExactStellarScheme } from "@x402/stellar/exact/server";

// Drive the CAIP-2 network ID from one place. Switching to mainnet means
// flipping STELLAR_NETWORK and FACILITATOR_URL in .env, nothing in code.
const NETWORK = process.env.STELLAR_NETWORK || "stellar:testnet";

if (!process.env.OZ_API_KEY) {
  throw new Error(
    "OZ_API_KEY is required. Generate one at https://channels.openzeppelin.com/testnet/gen (testnet) or https://channels.openzeppelin.com/gen (mainnet)."
  );
}

const facilitator = new HTTPFacilitatorClient({
  url: process.env.FACILITATOR_URL ?? "https://channels.openzeppelin.com/x402/testnet",
  // OZ Channels requires Bearer auth on both testnet and mainnet
  createAuthHeaders: async () => {
    const h = { Authorization: `Bearer ${process.env.OZ_API_KEY}` };
    return { verify: h, settle: h, supported: h };
  },
});

const resourceServer = new x402ResourceServer(facilitator)
  .register(NETWORK, new ExactStellarScheme());

const app = express();

app.use(
  paymentMiddleware(
    {
      "GET /weather": {
        accepts: {
          scheme: "exact",
          price: "$0.001", // human-readable, auto-converts to 7-decimal USDC units
          network: NETWORK,
          payTo: process.env.STELLAR_RECIPIENT, // recipient G... account
        },
        description: "Current weather data",
      },
    },
    resourceServer
  )
);

app.get("/weather", (_req, res) => {
  res.json({ city: "San Francisco", temp: 18, conditions: "Foggy" });
});

app.listen(3001, () => console.log(`x402 server on http://localhost:3001 (${NETWORK})`));
```

**Env vars:**
- `STELLAR_NETWORK` — CAIP-2 network ID; defaults to `stellar:testnet`. Set to `stellar:pubnet` for mainnet.
- `STELLAR_RECIPIENT` — your G... address (receives USDC, needs a USDC trustline)
- `OZ_API_KEY` — OZ Channels API key (**required on both testnet and mainnet**; generate at the link in the runbook below)
- `FACILITATOR_URL` — defaults to testnet URL above; set to `https://channels.openzeppelin.com/x402` for mainnet

**Price format options:**
- `"$0.001"` — human-readable, auto-converts to 7-decimal USDC units
- `{ amount: "1000", asset: "ASSET_SAC_CONTRACT_ID" }` — explicit base units for non-USDC assets

**`payTo` is the recipient's classic Stellar account (`G...`), not the USDC SAC contract address.** Sending USDC lands in the classic balance of the `payTo` account, which is why that account also needs a USDC trustline. The SAC contract address is what the protocol invokes `transfer` on; see "Two USDC addresses" below.

## Buyer: agent client

```bash
npm install @x402/fetch @x402/stellar dotenv
npm pkg set type=module
```

```js
// client.js
import "dotenv/config";
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { createEd25519Signer } from "@x402/stellar";
import { ExactStellarScheme } from "@x402/stellar/exact/client";

const NETWORK = process.env.STELLAR_NETWORK || "stellar:testnet";

// createEd25519Signer takes the raw S... secret string and the CAIP-2 network ID.
// Do NOT pre-wrap with Keypair.fromSecret or call getNetworkPassphrase yourself —
// the signer does both internally.
const signer = createEd25519Signer(process.env.STELLAR_SECRET_KEY, NETWORK);

// wrapFetchWithPaymentFromConfig returns a fetch that handles 402 negotiation
// and auth-entry signing transparently.
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: NETWORK, client: new ExactStellarScheme(signer) }],
});

const res = await fetchWithPayment("http://localhost:3001/weather");
console.log(await res.json());
// Paid automatically: 402 negotiation + auth-entry signing under the hood
```

**Env vars:**
- `STELLAR_NETWORK` — CAIP-2 network ID; defaults to `stellar:testnet`. Must match the server's network.
- `STELLAR_SECRET_KEY` — your S... secret key (needs USDC trustline + balance)

**Browser frontends:** this client uses Node `fetch` and `createEd25519Signer`, both of which run in Node. A vanilla browser cannot sign Soroban auth entries through a typical wallet extension without additional glue. For a browser payer, run the x402 client server-side and expose a thin proxy endpoint to the page, or wire up Wallets-Kit / Freighter with custom auth-entry signing.

## Testnet runbook

You need two Stellar testnet accounts: a **client/payer** (signs and pays from a USDC balance) and a **server/recipient** (the `payTo` in your route config). Both need a USDC trustline.

Two steps are web-only (Captcha or auth form) and cannot be scripted: the Circle USDC faucet and the OZ Channels key generator. Everything else can be automated. A complete `setup.js` sketch lives at the end of this section.

1. **Generate two keypairs**
   ```bash
   node -e "const { Keypair } = require('@stellar/stellar-sdk'); for (const n of ['RECIPIENT','PAYER']) { const k = Keypair.random(); console.log(n, k.publicKey(), k.secret()); }"
   ```

2. **Fund both with testnet XLM (friendbot)**
   ```bash
   curl "https://friendbot.stellar.org?addr=RECIPIENT_G..."
   curl "https://friendbot.stell

Related in Ads & Marketing