https://github.com/albttx/traefik-plugin-x402
traefik plugin for processing x402 payments
https://github.com/albttx/traefik-plugin-x402
traefik traefik-plugin x402
Last synced: 3 days ago
JSON representation
traefik plugin for processing x402 payments
- Host: GitHub
- URL: https://github.com/albttx/traefik-plugin-x402
- Owner: albttx
- License: mit
- Created: 2026-05-07T10:06:50.000Z (about 2 months ago)
- Default Branch: master
- Last Pushed: 2026-05-08T11:50:53.000Z (about 2 months ago)
- Last Synced: 2026-06-25T18:02:47.269Z (8 days ago)
- Topics: traefik, traefik-plugin, x402
- Language: Go
- Homepage:
- Size: 454 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-ccamel - albttx/traefik-plugin-x402 - traefik plugin for processing x402 payments (Go)
README
# traefik-plugin-x402
Traefik v3 middleware that gates HTTP routes behind on-chain stablecoin micropayments using the [x402 protocol](https://x402.org).
[](https://github.com/albttx/traefik-plugin-x402/actions/workflows/test.yml)


---
## Why
Put any HTTP service behind a paywall without touching its source code. A client that cannot pay gets a standard `402 Payment Required` with a machine-readable `PaymentRequirements` body. A client that can pay signs the payment off-chain, retries the request, and the middleware handles verify-and-settle against a remote facilitator — no custom backend logic required.
The default facilitator is Coinbase's public `https://x402.org/facilitator`, free to use on Base Sepolia. USDC on Base / Base Sepolia is the primary supported asset.
The plugin has **zero non-stdlib runtime dependencies** and is interpreted by [Yaegi](https://github.com/traefik/yaegi) at Traefik startup. It never touches private keys or constructs on-chain transactions — all cryptographic verification and settlement is delegated to the facilitator over plain HTTPS JSON.
---
## How it works
```
Client Traefik (x402 middleware) Facilitator Upstream
| | | |
|--- GET /api ---------------> | | |
|<-- 402 PaymentRequirements --| | |
| | | |
|--- GET /api | | |
| X-PAYMENT: ----> | | |
| |--- POST /verify -----------> | |
| |<-- { isValid: true } ------- | |
| | | |
| |--- forward request ----------------------------->|
| |<-- 200 body (buffered) --------------------------|
| | | |
| |--- POST /settle ------------>| |
| |<-- { success: true } --------| |
| | | |
|<-- 200 body | | |
| X-PAYMENT-RESPONSE: | | |
```
1. **No `X-PAYMENT` header** — middleware returns `402` with a `PaymentRequirements` JSON body. No upstream call is made.
2. **`X-PAYMENT` present** — middleware base64-decodes the header, JSON-parses the `PaymentPayload`, and POSTs to `{facilitatorURL}/verify`.
3. **Verify fails** — middleware returns `402` with the failure reason. No upstream call is made.
4. **Verify succeeds** — middleware forwards the request to the upstream and **buffers the full response body**.
5. **Upstream returns 2xx** — middleware POSTs to `{facilitatorURL}/settle`. On success, adds `X-PAYMENT-RESPONSE` (base64 JSON containing the on-chain tx hash) to the response. If settle fails, the 2xx is replaced with a `402`.
6. **Upstream returns non-2xx** — settle is skipped; the buffered error response is forwarded as-is.
> Streaming responses are not supported — the upstream response is always fully buffered before settle is attempted.
---
## Install
### Option A — Traefik Plugin Catalog (recommended)
> Available once the plugin is listed at https://plugins.traefik.io.
In Traefik's **static configuration**:
```yaml
# traefik.yml
experimental:
plugins:
x402:
moduleName: github.com/albttx/traefik-plugin-x402
version: v0.1.0 # replace with the latest tagged release
```
### Option B — Local plugin (self-hosted / air-gapped)
Mount the plugin source under Traefik's plugin directory at the path matching its module name:
```
/plugins-local/
src/
github.com/
albttx/
traefik-plugin-x402/
plugin.go
go.mod
.traefik.yml
internal/
x402/
```
Then in static config:
```yaml
# traefik.yml
experimental:
localPlugins:
x402:
moduleName: github.com/albttx/traefik-plugin-x402
```
---
## Configure
### Options
| Name | Required | Default | Description |
|---|---|---|---|
| `payTo` | yes | — | Recipient wallet address (EVM checksum address) |
| `network` | yes | — | Network identifier, e.g. `base`, `base-sepolia` |
| `asset` | yes | — | ERC-20 contract address of the payment token |
| `amount` | yes | — | Amount in atomic units as a decimal string. USDC has 6 decimals, so `"1000"` = $0.001 |
| `assetName` | yes¹ | `""` | EIP-712 domain `name` for the asset. For Circle USDC: `USDC`. |
| `assetVersion` | yes¹ | `""` | EIP-712 domain `version` for the asset. For Circle USDC: `2`. |
| `description` | no | `""` | Human-readable description included in the `PaymentRequirements` |
| `mimeType` | no | `application/json` | MIME type of the protected resource |
| `resource` | no | full request URL | Override the resource URL advertised in `PaymentRequirements` |
| `scheme` | no | `exact` | Payment scheme. The public facilitator implements `exact`. |
| `facilitatorURL` | no | `https://x402.org/facilitator` | Base URL of the x402 facilitator |
| `maxTimeoutSeconds` | no | `60` | Maximum time (seconds) the client has to complete the payment |
¹ `assetName` / `assetVersion` are required by any facilitator implementing the `exact` scheme on EVM — they're embedded in `PaymentRequirements.extra` so the client signs against the correct EIP-712 domain. Omitting them produces an `invalid_exact_evm_missing_eip712_domain` verify error. For non-USDC tokens, read the contract's `name()` and check the source for the EIP-712 version constant.
### Docker labels (Compose / Swarm)
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.paid.rule=Host(`api.example.com`)"
- "traefik.http.routers.paid.middlewares=x402-pay"
- "traefik.http.middlewares.x402-pay.plugin.x402.payTo=0xYourWallet"
- "traefik.http.middlewares.x402-pay.plugin.x402.network=base"
- "traefik.http.middlewares.x402-pay.plugin.x402.asset=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
- "traefik.http.middlewares.x402-pay.plugin.x402.amount=1000"
- "traefik.http.middlewares.x402-pay.plugin.x402.assetName=USDC"
- "traefik.http.middlewares.x402-pay.plugin.x402.assetVersion=2"
```
### File provider (dynamic config)
```yaml
# dynamic.yml
http:
middlewares:
x402-pay:
plugin:
x402:
payTo: "0xYourWallet"
network: "base"
asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" # USDC on Base mainnet
amount: "1000"
assetName: "USDC"
assetVersion: "2"
description: "API access"
routers:
paid:
rule: "Host(`api.example.com`)"
entryPoints: [web]
service: my-upstream@file
middlewares: [x402-pay@file]
```
---
## Compatibility
| Dimension | Supported |
|---|---|
| Traefik | v3.7+ (older versions fail against Docker Engine 29's `MinAPIVersion=1.44`) |
| Networks (public facilitator) | `base`, `base-sepolia` |
| Default asset | USDC |
| Plugin runtime dependencies | none (stdlib only) |
---
## Try it locally
A one-command Docker Compose demo with Traefik + a dummy upstream + the plugin loaded via Yaegi is included. See **[docs/dev.md](docs/dev.md)** for the quickstart, the unit / end-to-end test workflow, and troubleshooting.
---
## Roadmap
- Pluggable facilitator selection per-route
- Prometheus metrics (verify/settle latency, failure rates)
- Structured JSON logging
- Response streaming (currently buffered)
- Payment caching via cookie or JWT to avoid re-verification for the same payer across requests
- Solana and Stellar network support once public facilitators are available
---
## Contributing
Pull requests welcome. See [docs/dev.md](docs/dev.md) for setup, test commands, and dev environment details. Open an [issue](https://github.com/albttx/traefik-plugin-x402/issues) first to discuss non-trivial changes.
---
## License
MIT — see [LICENSE](LICENSE).
---
## Acknowledgements
Implements the [x402 protocol](https://x402.org) designed and specified by [Coinbase](https://github.com/coinbase/x402). The plugin system is provided by [Traefik](https://traefik.io).