https://github.com/stellar/x402-stellar
Tools, examples, and references for the [x402 protocol](https://www.x402.org/) on the Stellar network.
https://github.com/stellar/x402-stellar
Last synced: about 1 month ago
JSON representation
Tools, examples, and references for the [x402 protocol](https://www.x402.org/) on the Stellar network.
- Host: GitHub
- URL: https://github.com/stellar/x402-stellar
- Owner: stellar
- License: apache-2.0
- Created: 2026-03-03T07:06:59.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-04-27T18:39:44.000Z (2 months ago)
- Last Synced: 2026-04-27T20:25:43.806Z (2 months ago)
- Language: TypeScript
- Homepage:
- Size: 1.19 MB
- Stars: 7
- Watchers: 0
- Forks: 4
- Open Issues: 16
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# x402 Stellar
Tools, examples, and references for the [x402 protocol](https://www.x402.org/) on the Stellar network.
## Repository Structure
```
x402-stellar/
├── examples/ # Example applications
│ ├── facilitator/ # Stellar facilitator service
│ └── simple-paywall/ # Paywall demo (server + client)
│ └── docker-compose.yml
├── Dockerfile # Multi-target Dockerfile
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
└── Makefile
```
## Prerequisites
- Node.js 22+
- pnpm 10+
## Quick Start
```bash
# Clone the repo
git clone https://github.com/stellar/x402-stellar.git
cd x402-stellar
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Copy env template and fill in your values
cp examples/simple-paywall/server/.env.example examples/simple-paywall/server/.env
```
### Running the Simple Paywall Example
You need three terminals:
**Terminal 1 -- Facilitator:**
```bash
cd examples/facilitator
cp .env.example .env # fill in FACILITATOR_STELLAR_PRIVATE_KEY
pnpm dev
```
**Terminal 2 -- Server:**
```bash
cd examples/simple-paywall/server
pnpm dev
```
**Terminal 3 -- Client:**
```bash
cd examples/simple-paywall/client
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) and navigate through Home → Try It. The page dynamically shows buttons for each configured network (testnet, mainnet, or both) based on which `TESTNET_*` / `MAINNET_*` env vars are set.
### Docker Compose (with ingress rewrite simulation)
To run the 3-service compose stack plus a local ingress simulator (production-style rewrite behavior):
```bash
docker compose \
-f examples/simple-paywall/docker-compose.yml \
-f examples/simple-paywall/docker-compose.ingress-sim.yml \
up -d --build
```
Then open [http://localhost:8099/x402-demo/try](http://localhost:8099/x402-demo/try).
This simulator rewrites:
- `/x402-demo(/|$)(.*)` → client `/$2`
- `/x402-demo/api(/|$)(.*)` → server `/$2`
It is useful for reproducing ingress-prefix issues locally. Full verification steps (including decoding `PAYMENT-REQUIRED.resource.url`) are in `examples/simple-paywall/INGRESS_SIMULATION.md`.
To stop:
```bash
docker compose \
-f examples/simple-paywall/docker-compose.yml \
-f examples/simple-paywall/docker-compose.ingress-sim.yml \
down
```
## Examples
| Example | Path | Description |
| ------------------ | -------------------------- | -------------------------------------------------------- |
| **Facilitator** | `examples/facilitator/` | Stellar facilitator service that processes x402 payments |
| **Simple Paywall** | `examples/simple-paywall/` | Express API with x402 middleware + React/Vite frontend |
## Development
```bash
make install # pnpm install
make build # pnpm build (turbo)
make dev # pnpm dev
make lint # pnpm lint
make typecheck # pnpm typecheck
make test # pnpm test
make check # format + lint + typecheck + test
make clean # remove dist/ and node_modules/ from examples
```
## Deployment
The Dockerfile is a multi-target build with targets for `facilitator`, `server`, and `client`. See `examples/simple-paywall/docker-compose.yml` for the compose configuration.
## Environment Variables
### Server
| Variable | Default | Description |
| ------------------ | -------------------------------- | ------------------------------------------- |
| `PORT` | `3001` | Server port |
| `NODE_ENV` | `development` | Environment |
| `LOG_LEVEL` | `info` | Pino log level |
| `CORS_ORIGINS` | `http://localhost:5173` | Allowed CORS origins (comma-separated) |
| `PAYWALL_DISABLED` | — | Set to `true` to disable the paywall |
| `PAYMENT_PRICE` | `0.01` | Price for protected content |
| `CLIENT_HOME_URL` | — | Client home page URL for paywall brand link |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Express trust proxy setting |
### Per-Network (Testnet / Mainnet)
The server supports multiple Stellar networks simultaneously. Each network is configured with a `TESTNET_` or `MAINNET_` prefix. Provide at least one set to enable that network's `/protected/` endpoint.
| Variable (replace `_` with `TESTNET_` or `MAINNET_`) | Default | Description |
| ------------------------------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------- |
| `_SERVER_STELLAR_ADDRESS` | _required per network_ | Stellar address to receive payments |
| `_STELLAR_RPC_URL` | testnet RPC (testnet only) | Custom Soroban RPC URL (required for mainnet) |
| `_FACILITATOR_URL` | — | x402 facilitator URL |
| `_FACILITATOR_API_KEY` | — | Facilitator API key(s); comma-separate multiple keys for round-robin load balancing (optional) |
### Facilitator
| Variable | Default | Description |
| ------------------------------------- | -------------------------------- | ---------------------------------------- |
| `FACILITATOR_STELLAR_PRIVATE_KEY` | — | Facilitator wallet private key |
| `FACILITATOR_STELLAR_FEE_BUMP_SECRET` | — | Fee-bump signer secret (high-throughput) |
| `FACILITATOR_STELLAR_CHANNEL_SECRETS` | — | Channel account secrets, comma-separated |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Express trust proxy setting |
| `MAX_TRANSACTION_FEE_STROOPS` | `50000` (library default) | Max fee in stroops accepted from clients |
### Client
| Variable | Default | Description |
| -------------------- | ----------------------- | ---------------------------------------------------------------------------------------- |
| `VITE_SERVER_URL` | `http://localhost:3001` | Server URL for the client SPA (build-time or runtime) |
| `VITE_APP_NAME` | `x402 on Stellar` | App name for the client SPA (build-time or runtime) |
| `VITE_PAYMENT_PRICE` | `0.01` | Payment price shown in the UI (build-time or runtime) |
| `VITE_BASE_ROUTE` | `/` | Rewrites asset paths and sets the SPA router basename for subpath serving (runtime only) |
| `VITE_PORT` | `5173` | Dev server port (build-time only) |
> **Note:** `VITE_SERVER_URL`, `VITE_APP_NAME`, and `VITE_PAYMENT_PRICE` can be overridden at container launch time (via `docker-entrypoint.sh` → `window.__CONFIG__`). `VITE_BASE_ROUTE` is runtime-only: at container startup, the entrypoint rewrites asset paths in `index.html` and injects the SPA router basename so the app works when served under a subpath (e.g. `/x402-demo/`). `VITE_PORT` only affects the Vite dev server and is not used in Docker. These variables are included in the root `.env.example` (commented out); alternatively, set them in the client's own `.env` file or pass them via the environment (e.g. `VITE_PORT=3000 pnpm dev`).
## High-Throughput Facilitator Setup (Recommended)
For production or high-volume scenarios, the facilitator supports **channel accounts** with a **fee-bump signer**. This is the recommended setup because it allows parallel transaction submission — each channel account maintains its own sequence number, and a dedicated fee-bump signer pays all fees.
### How It Works
On Stellar, a single account can only submit one transaction at a time because each transaction increments the account's sequence number. With 19 channel accounts + 1 fee-bump signer, the facilitator can submit up to 19 transactions in parallel:
- **Channel accounts** (inner-tx signers): Each holds its own sequence number. The facilitator uses round-robin selection to distribute transactions across them. Created with zero balance via sponsored reserves.
- **Fee-bump signer**: A separate funded account that wraps each inner transaction in a fee-bump transaction. It pays the network fees but does not manage sequence numbers.
### Setup
1. Generate and fund the accounts on testnet:
```bash
cd examples/facilitator
pnpm generate-channel-accounts
```
This will:
- Create a fee-payer keypair and fund it via Friendbot
- Generate 19 channel account keypairs
- Create all channel accounts on-chain with zero balance (using sponsored reserves)
- Save all keys to a timestamped `.env` file under `scripts/output/`
- Print the environment variables to add to your `.env`
2. Copy the output into your `.env`:
```bash
# examples/facilitator/.env
FACILITATOR_STELLAR_PRIVATE_KEY=S_YOUR_EXISTING_KEY
FACILITATOR_STELLAR_FEE_BUMP_SECRET=S_FEE_BUMP_SECRET_FROM_SCRIPT
FACILITATOR_STELLAR_CHANNEL_SECRETS=S_CH1,S_CH2,...,S_CH19
```
3. Start the facilitator — it auto-detects high-throughput mode:
```bash
pnpm dev
```
You should see a log line: `High-throughput mode: fee-bump signer + channel accounts`.
When the channel account variables are not set, the facilitator falls back to single-signer mode using `FACILITATOR_STELLAR_PRIVATE_KEY`.
### Testnet Reset Recovery
Stellar's public testnet resets periodically, wiping all accounts. Two utility scripts re-fund your accounts without regenerating keys:
```bash
cd examples/facilitator
# Re-fund accounts from your .env file
pnpm refund-accounts-from-env
# Re-fund all signers registered with x402.org
pnpm refund-accounts-from-remote
```
Both scripts skip accounts that are already funded. See `examples/facilitator/README.md` for details.
## Adding a New Example
1. Create a directory under `examples/` (e.g. `examples/my-example/`)
2. Add a `package.json` with name `@x402-stellar/my-example`
3. The workspace globs in `pnpm-workspace.yaml` will pick it up automatically
4. Run `pnpm install` from the root to link it