Open-source micro-donation server powered by the x402 protocol on Base. Deploy your own server, then trigger USDC donations with a single HTTP call from any product.
Live Demo | Deploy to Vercel | npm install | Fork this repo
Built and maintained by AllScale Lab.
x402 Charity lets any company add micro-donations to their product. Every user action — a trade, an API call, a game action — can trigger a small USDC donation to a charity of your choice.
- No wallet or crypto knowledge needed from your users
- Your company funds the donations from a dedicated wallet
- All donations are on-chain and publicly verifiable
- Built-in dashboard shows donation history and wallet balances
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ User │ │ Your Product │ │ x402 Charity Server │
│ Action │─────>│ Server │─────>│ (this repo) │
└──────────┘ └──────────────────┘ └──────────┬───────────┘
e.g. swap, calls POST /donate │
API call, with amount │ signs payment
game move │ via x402 protocol
v
┌──────────────────┐
│ x402 Facilitator │
│ (run by Coinbase)│
└────────┬─────────┘
│ settles USDC
│ on Base
v
┌──────────────────┐
│ Charity Wallet │
│ receives USDC │
└──────────────────┘
Step by step:
- User acts — A user does something in your product (swap, API call, game move, checkout, etc.)
- Your server calls ours — Your backend sends
POST /donatewith an amount (e.g.$0.001) to your deployed x402 charity server - x402 protocol handles payment — The charity server uses your pre-funded donation wallet to sign a USDC payment via the x402 protocol
- On-chain settlement — The x402 facilitator (operated by Coinbase) settles the payment on Base — USDC moves from your donation wallet to the charity wallet
- Receipt returned — Your server gets back a response with the on-chain transaction hash as proof
The user never needs a wallet, never signs anything, and never even knows a donation happened. Your company funds all donations from a single pre-funded wallet.
Create a new wallet (MetaMask, Coinbase Wallet, etc.) and export the private key. Fund it with:
- USDC on Base (for donations)
- A small amount of ETH on Base (for gas)
Option A: Run locally
git clone https://github.com/allscale-io/x402charity.git
cd x402charity
cp .env.example .env # edit .env with your keys
pnpm install
pnpm devOption B: Docker
git clone https://github.com/allscale-io/x402charity.git
cd x402charity
docker build -t x402charity .
docker run -p 3402:3402 \
-e DONATION_PRIVATE_KEY="0xabc..." \
-e CHARITY_WALLET="0xdef..." \
-e CHARITY_NAME="Give Directly" \
-e DONATION_NETWORK="base" \
-e DONATE_API_KEY="$(openssl rand -hex 32)" \
x402charityYour server is live at http://localhost:3402 with a built-in dashboard.
From your product server — any language, any framework:
const res = await fetch('https://your-charity-server.com/donate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_DONATE_API_KEY',
},
body: JSON.stringify({ amount: '$0.001' }),
});
const receipt = await res.json();
console.log(receipt.txHash); // on-chain proof| Variable | Required | Description | Default |
|---|---|---|---|
DONATION_PRIVATE_KEY |
Yes | Private key of the wallet that funds donations | — |
CHARITY_WALLET |
Yes | Wallet address of the charity receiving donations | — |
CHARITY_NAME |
No | Display name for the charity | My Charity |
CHARITY_DESCRIPTION |
No | Description of the charity | — |
DONATION_NETWORK |
No | base (mainnet) or base-sepolia (testnet) |
base-sepolia |
BASE_URL |
No | Public URL of your server (auto-detected on Vercel) | http://localhost:3402 |
PORT |
No | Server port | 3402 |
DONATE_API_KEY |
No | Secret key to protect POST /donate. If set, callers must send Authorization: Bearer <key>. If unset, the endpoint is open. Set this in production. |
— (open) |
CORS_ORIGINS |
No | Comma-separated list of allowed CORS origins (e.g. https://myapp.com,https://admin.myapp.com). If unset, all origins are allowed. |
* (all origins) |
Security notes:
- Never commit your
DONATION_PRIVATE_KEYto version control. Use environment variables or a secret manager (e.g. Vercel Environment Variables, AWS Secrets Manager). The private key controls the donation wallet funds.- Set
DONATE_API_KEYin production. Without it, anyone who knows your server URL can trigger donations and drain your wallet. Generate one with:openssl rand -hex 32
| Method | Path | Description |
|---|---|---|
POST |
/donate |
Trigger a donation. Requires Authorization: Bearer <DONATE_API_KEY> if API key is set. Optional body: { "amount": "$0.001" } |
GET |
/donations |
JSON list of all donations with totals |
GET |
/charity |
Charity info (name, wallet, chain) |
GET |
/address |
Donation wallet address and balances |
GET |
/health |
Health check |
npm install x402charityThe simplest way — just call your deployed server's POST /donate endpoint:
await fetch('https://your-charity-server.com/donate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_DONATE_API_KEY',
},
body: JSON.stringify({ amount: '$0.001' }),
});import { x402charity } from 'x402charity/express';
app.use('/api', x402charity({
privateKey: process.env.DONATION_PRIVATE_KEY,
donateEndpoint: 'https://your-charity-server.com/donate',
charity: {
id: 'my-charity',
name: 'My Charity',
walletAddress: '0x...',
chain: 'base-sepolia',
description: 'My charity description',
verified: false,
x402Endpoint: 'https://your-charity-server.com/donate',
},
amount: '$0.001',
shouldDonate: (req) => req.method === 'POST',
}));// middleware.ts
import { x402charity } from 'x402charity/next';
export default x402charity({
privateKey: process.env.DONATION_PRIVATE_KEY,
donateEndpoint: 'https://your-charity-server.com/donate',
charity: {
id: 'my-charity',
name: 'My Charity',
walletAddress: '0x...',
chain: 'base-sepolia',
description: 'My charity description',
verified: false,
x402Endpoint: 'https://your-charity-server.com/donate',
},
amount: '$0.001',
matcher: '/api/*',
});npx x402charity donate testing-charity '$0.001' --network base-sepolia- DEX / Trading — $0.001 per swap. 50K daily trades = $50/day to charity
- AI Products / APIs — $0.001 per API call or prompt
- Games — $0.001 per level cleared or match played
- E-commerce — $0.01 per order or checkout
- Betting / Predictions — $0.001 per bet placed
- Payments / Banking — $0.001 per transfer processed
Or deploy manually:
- Fork this repo
- Import it in Vercel
- Set the following environment variables in your Vercel project settings:
DONATION_PRIVATE_KEY— private key of the wallet that funds donationsCHARITY_WALLET— wallet address of the charity receiving donationsCHARITY_NAME— display name for the charity (optional)DONATION_NETWORK—basefor mainnet orbase-sepoliafor testnet (default:base-sepolia)DONATE_API_KEY— secret key to protectPOST /donate(recommended). Generate with:openssl rand -hex 32
- Deploy
Note:
BASE_URLis auto-detected on Vercel. If you deploy elsewhere (Railway, Fly, etc.), setBASE_URLto your server's public URL (e.g.https://your-app.fly.dev).
Before donations can work, your donation wallet needs funds on the correct network:
- If using
base-sepolia(testnet, the default): Get testnet USDC from the Circle faucet and testnet ETH from a Base Sepolia faucet. - If using
base(mainnet): Fund the wallet with real USDC and a small amount of ETH for gas on Base.
x402charity/
├── packages/
│ ├── core/ # npm package: client, middleware, CLI (x402charity)
│ └── server/ # Deployable donation server
├── registry/
│ └── charities.json # Charity directory
├── docs/
│ └── index.html # Landing page (x402charity.com)
├── api/
│ └── index.ts # Vercel serverless entry point
├── Dockerfile
└── vercel.json
Contributions welcome. Please open an issue first to discuss what you'd like to change.
To add a charity to the public registry, submit a PR editing registry/charities.json:
{
"id": "your-charity-id",
"name": "Your Charity Name",
"description": "What the charity does",
"walletAddress": "0x...",
"chain": "base",
"verified": false,
"category": "education",
"x402Endpoint": "https://your-charity-server.com/donate"
}MIT