A Flask-based payment page demo implementing the x402 protocol for USDC cryptocurrency payments on the Base chain. Uses the Coinbase facilitator for payment verification and settlement.
- Python 3.13+
- uv package manager
- A Base wallet address for receiving USDC payments
# Clone the repository
git clone <repository-url>
cd x402-payment-link
# Install dependencies
uv sync
# Copy environment template and configure
cp .env.example .env
# Edit .env with your wallet addresspython3 main.pyThe server starts at http://localhost:5000 (or your configured APP_PORT).
docker compose up --build- Open
http://localhost:5000/demoin your browser - Fill out the order form and submit
- The payment page returns a 402 status with payment requirements
- Use an x402-compatible wallet to complete the payment
Create a .env file in the project root. See .env.example for a template.
| Variable | Description | Example |
|---|---|---|
ADDRESS |
Wallet address for receiving USDC payments | 0xYourBaseWalletAddress |
NETWORK |
Blockchain network | base-sepolia (testnet) or base (mainnet) |
| Variable | Default | Description |
|---|---|---|
APP_PORT |
5000 |
Server port |
ENVIRONMENT |
staging |
Affects product pricing (staging or production) |
MAX_DEADLINE_SECONDS |
60 |
Payment timeout in seconds |
FACILITATOR_URL |
https://x402f1.secondstate.io |
x402 facilitator service URL |
APP_NAME |
x402-mvp |
Application name shown in paywall |
APP_LOGO |
/static/secondstate.png |
Logo URL for paywall |
To send order confirmation emails via SendGrid:
| Variable | Description |
|---|---|
SENDGRID_API_KEY |
Your SendGrid API key |
FROM_EMAIL |
Sender email address |
ORDER_CONFIRMATION_RECIPIENT |
CC recipient for order confirmations |
The x402 protocol enables HTTP-native cryptocurrency payments using the 402 Payment Required status code.
┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browse │────▶│ Submit Order │────▶│ Payment 402 │────▶│ Settlement │
│ Product │ │ Form │ │ Requirements│ │ Confirmation│
└──────────┘ └──────────────┘ └─────────────┘ └─────────────┘
- Browse Product (
GET /<product>) - User views product page with pricing - Submit Order (
POST /<product>/order) - Order details saved, redirects to payment - Payment Required (
GET /<product>/order/<order_id>) - Returns 402 with payment requirements if noX-PAYMENTheader - Settlement - Client sends payment via
X-PAYMENTheader; facilitator verifies and settles on blockchain - Confirmation - Success page displayed with transaction link; email sent if configured
├── main.py # Flask application entry point
├── config.py # Environment configuration
├── products.yaml # Product catalog
├── services/
│ ├── product_service.py # Product catalog management
│ ├── order_service.py # Order persistence (JSONL)
│ ├── payment_service.py # x402 payment verification/settlement
│ └── notification_service.py # SendGrid email notifications
├── templates/
│ └── <product_id>/
│ ├── product.html # Product landing page
│ ├── order_confirmation.html # Success page
│ └── order_confirmation_email.html # Email template
├── static/ # Static assets (images, etc.)
├── data/ # Runtime data (orders, logs)
├── Dockerfile
└── docker-compose.yaml
- Add an entry to
products.yaml:
my_product:
name: My Product
description: "Product description here"
image: https://example.com/image.png
staging:
price: 0.10
shipping: 0.00
production:
price: 29.99
shipping: 4.99- Create templates in
templates/my_product/:product.html- Product landing page with order formorder_confirmation.html- Post-payment success pageorder_confirmation_email.html- Email template (optional)
The docker-compose.yaml configuration:
services:
x402-mvp:
build: .
restart: unless-stopped
ports:
- "${SERVER_PORT}:${APP_PORT}"
volumes:
- ./.env:/app/.env
- ./data:/app/dataVolume Mounts:
.env- Configuration filedata/- Persistent storage for orders (<product>.txt) and logs (app.log)
Port Configuration:
- Set
SERVER_PORTin your environment for the external port - Set
APP_PORTin.envfor the internal Flask port
| Service | Purpose |
|---|---|
product_service |
Reads product catalog from products.yaml |
order_service |
Generates order IDs, stores orders as JSONL in data/ |
payment_service |
Creates payment requirements, verifies/settles via facilitator |
notification_service |
Sends confirmation emails via SendGrid |
- Set
NETWORK=base-sepoliain.env - Set
ENVIRONMENT=stagingfor lower test prices ($0.10) - Use a testnet wallet with Sepolia USDC
- No Coinbase API keys required for testnet
Orders are stored as JSON lines in data/<product>.txt:
{"time":"2024-01-01T12:00:00","email":"[email protected]","order_id":"ABC123","quantity":1,"total":"0.10","payment":false}After payment settlement, a new entry is appended with "payment":true.
See LICENSE for details.