An open API service indexing awesome lists of open source software.

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.

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