# XDC Taskman — x402 Integration Guide (for service providers)

This guide helps you make your API **pay-per-call in USDC on XDC** using the
x402 protocol, and fixes the most common integration errors (including
`Facilitator does not support exact on xdc`).

You do **not** need a wallet, an RPC node, or any XDC for gas. A **facilitator**
verifies and settles the USDC payment on-chain and pays the gas. You just wrap
your routes and point them at a facilitator.

---

## Constants (copy these exactly)

| Thing | Value |
|---|---|
| Network (mainnet) | `xdc` |
| Network (testnet) | `xdc-apothem` |
| USDC asset (mainnet) | `0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1` |
| USDC asset (testnet) | `0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4` |
| USDC decimals | `6` |
| Scheme | `exact` |
| Facilitator URL | `https://xdc-mcp.vercel.app/api/facilitator` |

> The network string must be **exactly** `xdc` (not `xdc-mainnet`, `XDC`, or the
> chain id `50`). The facilitator's supported list keys on `xdc`.

---

## 1. Verify the facilitator supports XDC

Before anything, confirm the facilitator advertises `exact` on `xdc`:

```bash
curl -s https://xdc-mcp.vercel.app/api/facilitator/supported
```

Expected:

```json
{"kinds":[
  {"x402Version":1,"scheme":"exact","network":"xdc","asset":"0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1","extra":{"decimals":6}},
  {"x402Version":1,"scheme":"exact","network":"xdc-apothem","asset":"0xb5AB…71D4","extra":{"decimals":6}}
]}
```

If you see this, the facilitator is fine — any "not supported" error is in your
server config (see Troubleshooting).

## 2. Wrap your routes + initialize the facilitator

The single most common bug: **building payment requirements before the resource
server has fetched the facilitator's supported kinds.** With `@x402/core` you
must `await initialize()` first.

```ts
import { x402ResourceServer } from "@x402/core/server";

const server = new x402ResourceServer({
  facilitator: { url: "https://xdc-mcp.vercel.app/api/facilitator" },
});

// ⚠️ REQUIRED before wrapping routes — fetches /supported from the facilitator.
await server.initialize();

// Now it knows exact/xdc is supported, so building requirements succeeds.
createPaidWrappers(server);
startServer();
```

If you wrap routes (call `buildPaymentRequirements`) synchronously at startup
*before* `initialize()` resolves, the supported set is empty → you get
`Facilitator does not support exact on xdc`.

### Express-style middleware shape

```ts
app.use(paymentMiddleware(
  payTo,                                  // your USDC receiving address (XDC)
  { "GET /your-route": { price: "0.01", network: "xdc" } },
  { url: "https://xdc-mcp.vercel.app/api/facilitator" },
));
```

## 2b. Your 402 response must be spec-compliant (checklist)

The most common onboarding bug: a paid route returns a 402 whose `accepts` entry
is missing fields, so the client rejects it before paying. Each entry needs
**all** of these:

```http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "xdc",
      "maxAmountRequired": "100",
      "resource": "https://your-api.example/v1/assets",
      "description": "Assets List",
      "mimeType": "application/json",
      "payTo": "0xYourReceivingAddress",
      "asset": "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USDC", "version": "2" }
    }
  ]
}
```

| Field | Must be |
|---|---|
| `scheme` | `"exact"` |
| `network` | `"xdc"` (mainnet) or `"xdc-apothem"` — **not** `xdc-mainnet`, `XDC`, or `50` |
| `maxAmountRequired` | price in **atomic units**, as a string (USDC 6 decimals → `0.0001 = "100"`, `0.01 = "10000"`, `1 = "1000000"`) |
| `resource` | the **exact** URL the client called (absolute) |
| `payTo` | your receiving `0x` address (match your marketplace listing) |
| `asset` | mainnet USDC `0xfA2958…8eb1` (testnet `0xb5AB…71D4`) |
| `maxTimeoutSeconds` | validity window, e.g. `60` |
| `extra` | `{ "name": "USDC", "version": "2" }` — required so the client can build the EIP-712 domain to sign |

Diff your output against a known-good one:

```bash
curl -s https://xdctaskman-x402-server.vercel.app/x402/echo | jq .accepts
```

> Client errors like `maxAmountRequired: Required (received undefined)` or
> `resource: Required` mean those fields are missing. Free routes never 402, so
> they won't surface this — it's isolated to the paid route's payment config.

## 3. Set your payTo

`payTo` is the address USDC settles to. Use the address you actually control.
If you list in the XDC Taskman marketplace, advertise the **same** address your
402 challenge returns — otherwise the listing and the real recipient diverge.

## 4. Test end to end

```bash
# 1) Unpaid request returns 402 with requirements
curl -i https://your-api.example/your-route

# 2) A client signs an EIP-3009 USDC authorization, resends with X-PAYMENT,
#    the facilitator settles, and you return 200. Test with the CLI:
npx xdc-taskman call https://your-api.example/your-route
```

---

## Troubleshooting

### ❌ `Facilitator does not support exact on xdc`
> `Make sure to call initialize() to fetch supported kinds from facilitators.`

The resource server's supported-kinds list was empty or from the wrong
facilitator when it built requirements. Check, in order:

1. **`initialize()` not awaited.** Call `await server.initialize()` **before**
   wrapping any routes / `buildPaymentRequirements`. (This is the usual cause —
   the stack shows the error inside `startServer → createPaidWrappers`.)
2. **Wrong facilitator URL.** The default (Coinbase / x402.org) facilitator
   serves Base, not XDC. Point at `https://xdc-mcp.vercel.app/api/facilitator`.
3. **Network string mismatch.** Use exactly `"xdc"` (or `"xdc-apothem"`), not
   `"xdc-mainnet"`, `"XDC"`, or `50`. Asset `0xfA2958…8eb1`, 6 decimals.

Verify the facilitator independently with the `/supported` curl above.

### ❌ My route never returns 402 (it's always free / always 200)
The middleware isn't in front of that route, or the route key doesn't match.
Ensure the route is registered in the middleware config (method + path) and the
middleware runs before your handler.

### ❌ Payments settle to the wrong address
Your 402 challenge's `payTo` is authoritative — that's where funds go. If a
marketplace lists a different address, fix the listing to match your `payTo`
(the 402 wins, not the listing).

### ❓ Do I need XDC for gas?
No. Settlement uses **EIP-3009 `transferWithAuthorization`** — the facilitator
submits the transaction and pays the gas. Neither you nor your callers need
native XDC.

### ❓ Mainnet vs testnet
Mainnet network `xdc` (chainId 50). Testnet network `xdc-apothem`. Use the
matching USDC asset from the constants table.

---

## How it fits together

```
Client (agent)  →  Your API  →  Facilitator
  signs USDC        returns 402,    verifies + settles on XDC,
  payment           calls verify/   pays the gas, returns tx hash
                    settle
```

- Facilitator endpoints: `GET /supported`, `POST /verify`, `POST /settle`.
- Reference implementation: `@xdctaskman/x402-server` (Express + middleware + MCP).
- Wire format: x402 `exact-evm`, USDC (EIP-3009).
- Caller side (how agents pay): https://xdctaskman-web.vercel.app/integrate
- List your API: https://xdctaskman-web.vercel.app/providers

Questions? Reply to whoever sent you this, or open an issue.
