@xdcai/x402-seller
v1.0.0-rc.1
Published
Protect seller API routes with x402 exact payments through the XDC AI facilitator. Buyers stay accountless.
Maintainers
Readme
@xdcai/x402-seller
TypeScript seller SDK for protecting API routes with x402 exact payments through the XDC AI facilitator.
Runtime: Node.js 20+ (including Hono on Node). This package uses Node APIs
(node:crypto, Buffer) and does not claim edge-runtime compatibility.
Buyers and agents stay accountless. They do not need an XDC AI account, API key, dashboard session, or prepaid XDC AI platform balance. The seller keeps the facilitator API key on the server.
npm install @xdcai/[email protected]License: Apache-2.0.
Public v1 exports
| Export | Purpose |
|---|---|
| paymentMiddleware | Express middleware |
| honoPaymentMiddleware | Hono-on-Node middleware |
| createSellerGuard | Framework-agnostic guard |
| SellerSdkError | Typed SDK error |
| XDC_USDC | Production XDC USDC constants |
| Header / protocol constants | Canonical + legacy x402 v2 names |
Internal helpers (protect, buyerErrorBody, classifyFacilitatorError,
HTTP clients) are not part of the supported public surface.
Subpath imports:
import { paymentMiddleware } from "@xdcai/x402-seller/express";
import { honoPaymentMiddleware } from "@xdcai/x402-seller/hono";Express quickstart
import express from "express";
import { paymentMiddleware, XDC_USDC } from "@xdcai/x402-seller";
const app = express();
app.use(paymentMiddleware({
facilitatorUrl: process.env.FACILITATOR_URL!,
facilitatorApiKey: process.env.FACILITATOR_API_KEY!,
seller: {
receiver: process.env.SELLER_RECEIVER_ADDRESS!,
},
routes: {
"GET /api/data": {
price: "10000",
asset: process.env.TOKEN_ASSET ?? XDC_USDC.asset,
network: XDC_USDC.network,
description: "Premium data endpoint",
tokenName: process.env.TOKEN_NAME ?? XDC_USDC.eip712.name,
tokenVersion: process.env.TOKEN_VERSION ?? XDC_USDC.eip712.version,
},
},
}));
app.get("/api/data", (_req, res) => {
res.json({ ok: true, data: "paid content" });
});Hono (Node)
import { Hono } from "hono";
import { honoPaymentMiddleware, XDC_USDC } from "@xdcai/x402-seller";
const app = new Hono();
app.use("*", honoPaymentMiddleware({ /* same config as Express */ }));
app.get("/api/data", (c) => c.json({ ok: true }));Required configuration
| Field / env | Purpose |
|---|---|
| facilitatorUrl / FACILITATOR_URL | Facilitator base URL |
| facilitatorApiKey / FACILITATOR_API_KEY | Seller server-side key (xdcai_live_<keyId>_<secret>) |
| seller.receiver / SELLER_RECEIVER_ADDRESS | Address that receives buyer payments (payTo) |
| route price | Atomic token amount string |
| route asset / TOKEN_ASSET | EIP-3009 token contract |
Security: FACILITATOR_API_KEY is server-side only. Never send it to a
browser, mobile app, or buyer/agent client.
Production XDC USDC
| Constant | Value |
|---|---|
| Network | eip155:50 |
| Asset | 0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1 |
| EIP-712 name / version | USDC / 2 |
| Decimals | 6 |
Do not use USD Coin for this contract — it produces a different domain
separator and the signed authorization reverts. GET /supported does not
return the token address or EIP-712 name/version.
Use XDC_USDC from the package instead of hardcoding.
Exact payment flow (x402 v2 headers)
- Buyer/agent calls the seller route with no payment signature header.
- The SDK returns
402with:- JSON body
accepts[]exact requirements - canonical
PAYMENT-REQUIREDheader (standard base64 of the same body) Cache-Control: no-store
- JSON body
- Buyer signs EIP-3009
transferWithAuthorizationand retries with canonicalPAYMENT-SIGNATURE(preferred) or legacyX-PAYMENT. - The SDK calls facilitator
/verify, then/settle, then polls until confirmed settlement (success: trueand a transaction hash). - Only then does the seller route handler run.
- The SDK sets canonical
PAYMENT-RESPONSEand also legacyX-PAYMENT-RESPONSE(same standard-base64 receipt) on success.
Inbound acceptance:
| Header | Role |
|---|---|
| PAYMENT-SIGNATURE | Canonical v2 payment proof (PaymentPayload) |
| X-PAYMENT | Legacy alias (PaymentPayload or proof wrapper; base64 or base64url) |
Generated responses prefer canonical v2 names. Legacy aliases remain documented and tested for migration.
Queued or pending settlement is not treated as paid. Batch/channel mode is not supported in V1.
Error categories and retries
| Category | Buyer-safe body | Typical HTTP | Safe retry? |
|---|---|---|---|
| payment_required | n/a (402 challenge) | 402 | Pay and retry once |
| buyer_payment_invalid | buyer_payment_invalid | 402 | Fix proof; do not blind-retry |
| settlement_failed | settlement_failed | 402 | Investigate; do not duplicate pay blindly |
| seller_api_key_invalid | payment_temporarily_unavailable | 503 | Seller ops: rotate/fix key |
| seller_rate_limited | payment_temporarily_unavailable | 503 | Back off |
| seller_out_of_facilitator_credits | payment_temporarily_unavailable | 503 | Seller ops: top up credits |
| facilitator_unavailable | payment_temporarily_unavailable | 503 | Retry with backoff |
| misconfigured_sdk | misconfigured_sdk | 500 | Fix seller config |
Buyer-visible bodies never include the seller API key.
Key rotation and upgrades
- Issue a new seller API key in the dashboard / admin flow.
- Deploy the new
FACILITATOR_API_KEYto seller servers. - Revoke the old key after traffic has moved.
- Pin SDK versions explicitly (
@xdcai/[email protected]). Avoid floatinglatestin production or agent skill examples. - Review release notes for header/export changes before major upgrades.
Agentic buyers
Agents can call seller endpoints using normal x402 exact payment behavior.
They do not need an XDC AI API key. They receive 402 requirements, sign/pay,
retry with PAYMENT-SIGNATURE, and receive a PAYMENT-RESPONSE receipt.
V1 limitation
Exact scheme only (scheme=exact, network eip155:50). Batch/channel
settlement is a later advanced agentic mode and is not included in this
package.
Local examples
cd packages/x402-seller-sdk
npm install
npm test
npm run build
FACILITATOR_URL=http://localhost:8080 \
FACILITATOR_API_KEY=change-me-local-dev \
SELLER_RECEIVER_ADDRESS=0x... \
TOKEN_ASSET=0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1 \
npm run example
# Hono-on-Node example
npm run example:honoClean tarball consumer check (no secrets, installs packed artifact only):
npm run test:consumer
npm run pack:dry-run