con un clic
x402-stellar-context
// Project context for the x402-stellar monorepo. Use this to understand what the project does, how it's structured, and how the pieces fit together. Load this first when working on any part of the codebase.
// Project context for the x402-stellar monorepo. Use this to understand what the project does, how it's structured, and how the pieces fit together. Load this first when working on any part of the codebase.
| name | x402-stellar-context |
| description | Project context for the x402-stellar monorepo. Use this to understand what the project does, how it's structured, and how the pieces fit together. Load this first when working on any part of the codebase. |
An implementation of the x402 payment protocol for the Stellar network. The x402 protocol uses HTTP status code 402 (Payment Required) to enable machine-readable paywalls — a server responds with 402 + a PAYMENT-REQUIRED header, the client pays via blockchain, then retries the request with a PAYMENT-SIGNATURE header. The facilitator verifies and settles payments on-chain.
This repo provides:
@x402/express middlewareClient Server Facilitator Stellar
| | | |
|-- GET /networks ------->| | |
|<-- { networks: [...] } -| | |
| | | |
|-- GET /protected/:net ->| | |
|<-- 402 + requirements --| | |
| | | |
| (user signs tx in wallet) | |
| | | |
|-- GET /protected/:net ->| | |
| (PAYMENT-SIGNATURE hdr)|-- POST /verify -------->| |
| |<-- { valid: true } -----| |
| | | |
| |-- POST /settle -------->| |
| | |-- submit tx --->|
| |<-- { success, txHash } -| |
|<-- 200 + PAYMENT-RESPONSE + content -------------| |
GET /networks to discover which networks are available (testnet, mainnet, or both)GET /protected/testnet or GET /protected/mainnet402 Payment Required + PAYMENT-REQUIRED header (base64 PaymentRequired payload: price, network, payTo address)PAYMENT-SIGNATURE headerPAYMENT-RESPONSE header (base64 SettlementResponse) and injects the transaction hash link via txHashInjector middlewarex402 v2 HTTP transport uses:
PAYMENT-REQUIRED (server → client, 402 response)PAYMENT-SIGNATURE (client → server, retried request)PAYMENT-RESPONSE (server → client, settlement metadata)The vendored @x402/* packages in this repo still include v1 fallbacks (X-PAYMENT, X-PAYMENT-RESPONSE) for backward compatibility, but v2 headers are the canonical protocol path.
pnpm 10.7.0 monorepo with Turborepo. All own packages are scoped @x402-stellar/*.
| Package | Path | Purpose |
|---|---|---|
@x402-stellar/paywall | packages/paywall/ | Builds the paywall HTML page. Uses a builder pattern: createPaywall().withNetwork(stellarPaywall).build(). The stellarPaywall handler uses @creit.tech/stellar-wallets-kit for wallet connection. Bundles React + wallet UI into a single HTML string via esbuild. |
@x402-stellar/facilitator | examples/facilitator/ | Express service that wraps x402Facilitator from @x402/core. Registers the ExactStellarScheme with a Stellar signer derived from FACILITATOR_STELLAR_PRIVATE_KEY. Exposes /verify, /settle, /supported, /health. |
@x402-stellar/simple-paywall-server | examples/simple-paywall/server/ | Express service that uses @x402/express paymentMiddleware to protect GET /protected/:network (testnet and/or mainnet). Configures per-network x402ResourceServer instances with HTTPFacilitatorClient pointing at each network's facilitator. Exposes GET /networks for network discovery. Also uses the paywall package to generate the 402 response HTML. |
@x402-stellar/simple-paywall-client | examples/simple-paywall/client/ | React/Vite SPA. Three-page demo: Home, Try It (fetches GET /networks and shows buttons for each available network), and the protected content page. |
4 pre-built @x402/* packages live under vendors/x402/typescript/packages/. They are a git submodule from the upstream coinbase/x402 repo, included as dist-only (never modify source, never rebuild).
| Package | Purpose |
|---|---|
@x402/core | Protocol types, facilitator base class, server base class, HTTPFacilitatorClient |
@x402/extensions | Protocol extensions (not directly used) |
@x402/express | paymentMiddleware and x402ResourceServer for Express |
@x402/stellar | ExactStellarScheme (facilitator + server sides), createEd25519Signer |
Turborepo resolves ^build dependencies. In practice: paywall builds first (esbuild + tsup), then server (imports paywall). Facilitator and client have no cross-dependencies and build in parallel.
Paywall as bundled HTML: The paywall package pre-bundles the entire React wallet UI into an HTML string at build time. When the server returns a 402, it sends this HTML directly — no client-side JS bundle loading needed. The template.ts file (~2.25 MB, auto-generated) contains the base64-encoded bundle.
txHashInjector middleware: Registered before the @x402/express middleware. It wraps res.end/res.write to intercept the response body after payment settlement and replaces {{TX_LINK}} with a Stellar Expert transaction link. This lets the protected content reference its own payment transaction.
Facilitator is internal: In the Docker Compose setup, the facilitator is not exposed externally — it runs on an internal network. Facilitator routes (/verify, /settle, /supported) are NOT externally accessible — they are an internal service, not a public API.
Startup validation: Before creating the Express app, the server calls Env.validateFacilitators() which fetches GET /supported from every configured facilitator (with optional Authorization: Bearer header). If any facilitator is unreachable or returns a non-200 status, the server throws with aggregated diagnostics.
trust proxy via proxy-addr: Both server and facilitator use the proxy-addr package with TRUST_PROXY env var (defaulting to loopback,linklocal,uniquelocal) instead of Express's built-in string-based trust. This matches the pattern used in Stellar's laboratory-backend.
x402-stellar/
├── packages/paywall/ # Paywall HTML builder
│ └── src/
│ ├── builder.ts # PaywallBuilder class
│ ├── stellar-handler.ts # Stellar wallet integration
│ ├── stellar.ts # Re-export for paywall/stellar entry
│ ├── types.ts # PaywallConfig, PaywallProvider types
│ └── browser/ # esbuild source for the wallet UI
│ └── build.ts # Build script -> gen/template.ts
├── examples/
│ ├── facilitator/ # Stellar facilitator service
│ │ └── src/
│ │ ├── app.ts # Express app: /verify, /settle, /supported, /health
│ │ ├── index.ts # Entrypoint: listen + graceful shutdown
│ │ ├── config/env.ts # Typed Env class (validates at startup)
│ │ └── utils/logger.ts # Pino structured logging
│ └── simple-paywall/
│ ├── server/ # Paywall server
│ │ └── src/
│ │ ├── app.ts # Express app: helmet, cors, trust proxy, error handler
│ │ ├── index.ts # Entrypoint: listen + graceful shutdown
│ │ ├── config/env.ts # Typed Env class
│ │ ├── middleware/
│ │ │ ├── payment.ts # Creates x402 paymentMiddleware
│ │ │ └── txHashInjector.ts # Replaces {{TX_LINK}} in responses
│ │ ├── routes/
│ │ │ ├── health.ts # GET /health
│ │ │ └── protected.ts # GET /protected/:network (paywalled content)
│ │ └── utils/logger.ts
│ ├── client/ # React/Vite SPA
│ └── docker-compose.yml # 3-container local setup
├── vendors/x402/ # Git submodule (dist-only, never modify)
│ └── typescript/packages/{core,extensions,http/express,mechanisms/stellar}
├── Dockerfile # Multi-target: facilitator, server, client
├── Makefile # check = install + format + lint + typecheck + test + build
├── turbo.json # Task config with ^build dependency
├── pnpm-workspace.yaml # Workspace globs
└── .env.example # Template for required env vars
| Mode | Description | Config |
|---|---|---|
| Local dev | 3 separate processes (facilitator, server, client) | pnpm dev |
| Docker Compose | 3 containers, facilitator internal, server on 3001, client on 8080 | docker-compose.yml |
| Standalone Docker | Any individual target (facilitator, server, client) | docker build --target <name> |
Minimum required for the system to function:
| Variable | Used By | Purpose |
|---|---|---|
FACILITATOR_STELLAR_PRIVATE_KEY | Facilitator | Signs and submits Stellar transactions |
TESTNET_SERVER_STELLAR_ADDRESS | Server | Stellar public address for testnet payments |
MAINNET_SERVER_STELLAR_ADDRESS | Server | Stellar public address for mainnet payments |
CLIENT_STELLAR_PRIVATE_KEY | Client (dev only) | Wallet key for testing (never in production) |
Per-network server variables use TESTNET_ or MAINNET_ prefixes: <NET>_SERVER_STELLAR_ADDRESS, <NET>_STELLAR_RPC_URL, <NET>_FACILITATOR_URL, <NET>_FACILITATOR_API_KEY. Provide at least one set to enable that network.
Client env variables are resolved as follows: VITE_SERVER_URL, VITE_APP_NAME, and VITE_PAYMENT_PRICE may be provided via runtime window.__CONFIG__ (Docker, injected by docker-entrypoint.sh) or via Vite build-time env, with hardcoded defaults as fallback. VITE_PORT configures the Vite dev server port at startup (default 5173) and is not read from window.__CONFIG__ at runtime in Docker.
Full reference in the root README.md and per-service .env.example files.
101 tests across 6 files:
examples/facilitator/tests/config/env.test.ts — 25 tests (Env class validation)examples/facilitator/tests/routes/facilitator.test.ts — 10 tests (route behavior)examples/simple-paywall/server/tests/config/env.test.ts — 41 tests (Env + dual-network config + address validation + validateFacilitators)examples/simple-paywall/server/tests/middleware/txHashInjector.test.ts — 10 testsexamples/simple-paywall/server/tests/routes/protected.test.ts — 12 tests (health + networks + protected routes per network)examples/simple-paywall/server/tests/routes/protected-single-network.test.ts — 3 tests (single-network deployment behavior)Run with pnpm test (Turborepo runs vitest in each package).