t402: An internet native payments protocol
Project description
t402 Python
Python SDK for the T402 HTTP-native stablecoin payments protocol.
Installation
pip install t402
# or with uv
uv add t402
Features
- Multi-Chain Support: EVM (Ethereum, Base, Polygon, etc.), TON, TRON, Solana
- Server Middleware: FastAPI and Flask integrations
- Client Libraries: httpx and requests adapters
- ERC-4337 Account Abstraction: Gasless payments with smart accounts
- USDT0 Cross-Chain Bridge: LayerZero-powered bridging
- WDK Integration: Tether Wallet Development Kit support
FastAPI Integration
The simplest way to add t402 payment protection to your FastAPI application:
from fastapi import FastAPI
from t402.fastapi.middleware import require_payment
app = FastAPI()
app.middleware("http")(
require_payment(price="0.01", pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C")
)
@app.get("/")
async def root():
return {"message": "Hello World"}
To protect specific routes:
app.middleware("http")(
require_payment(price="0.01",
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C"),
path="/foo" # <-- this can also be a list ex: ["/foo", "/bar"]
)
Flask Integration
The simplest way to add t402 payment protection to your Flask application:
from flask import Flask
from t402.flask.middleware import PaymentMiddleware
app = Flask(__name__)
# Initialize payment middleware
payment_middleware = PaymentMiddleware(app)
# Add payment protection for all routes
payment_middleware.add(
price="$0.01",
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
)
@app.route("/")
def root():
return {"message": "Hello World"}
To protect specific routes:
# Protect specific endpoint
payment_middleware.add(
path="/foo",
price="$0.001",
pay_to_address="0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
)
Client Integration
Simple Usage
Httpx Client
from eth_account import Account
from t402.clients.httpx import t402HttpxClient
# Initialize account
account = Account.from_key("your_private_key")
# Create client and make request
async with t402HttpxClient(account=account, base_url="https://api.example.com") as client:
response = await client.get("/protected-endpoint")
print(await response.aread())
Requests Session Client
from eth_account import Account
from t402.clients.requests import t402_requests
# Initialize account
account = Account.from_key("your_private_key")
# Create session and make request
session = t402_requests(account)
response = session.get("https://api.example.com/protected-endpoint")
print(response.content)
Advanced Usage
Httpx Extensible Example
import httpx
from eth_account import Account
from t402.clients.httpx import t402_payment_hooks
# Initialize account
account = Account.from_key("your_private_key")
# Create httpx client with t402 payment hooks
async with httpx.AsyncClient(base_url="https://api.example.com") as client:
# Add payment hooks directly to client
client.event_hooks = t402_payment_hooks(account)
# Make request - payment handling is automatic
response = await client.get("/protected-endpoint")
print(await response.aread())
Requests Session Extensible Example
import requests
from eth_account import Account
from t402.clients.requests import t402_http_adapter
# Initialize account
account = Account.from_key("your_private_key")
# Create session and mount the t402 adapter
session = requests.Session()
adapter = t402_http_adapter(account)
# Mount the adapter for both HTTP and HTTPS
session.mount("http://", adapter)
session.mount("https://", adapter)
# Make request - payment handling is automatic
response = session.get("https://api.example.com/protected-endpoint")
print(response.content)
Manual Server Integration
If you're not using the FastAPI middleware, you can implement the t402 protocol manually. Here's what you'll need to handle:
- Return 402 error responses with the appropriate response body
- Use the facilitator to validate payments
- Use the facilitator to settle payments
- Return the appropriate response header to the caller
Here's an example of manual integration:
from typing import Annotated
from fastapi import FastAPI, Request
from t402.types import PaymentRequiredResponse, PaymentRequirements
from t402.encoding import safe_base64_decode
payment_requirements = PaymentRequirements(...)
facilitator = FacilitatorClient(facilitator_url)
@app.get("/foo")
async def foo(req: request: Request):
payment_required = PaymentRequiredResponse(
t402_version: 1,
accepts=[payment_requirements],
error="",
)
payment_header = req.headers.get("X-PAYMENT", "")
if payment_header == "":
payment_required.error = "X-PAYMENT header not set"
return JSONResponse(
content=payment_required.model_dump(by_alias=True),
status_code=402,
)
payment = PaymentPayload(**json.loads(safe_base64_decode(payment_header)))
verify_response = await facilitator.verify(payment, payment_requirements)
if not verify_response.is_valid:
payment_required.error = "Invalid payment"
return JSONResponse(
content=payment_required.model_dump(by_alias=True),
status_code=402,
)
settle_response = await facilitator.settle(payment, payment_requirements)
if settle_response.success:
response.headers["X-PAYMENT-RESPONSE"] = base64.b64encode(
settle_response.model_dump_json().encode("utf-8")
).decode("utf-8")
else:
payment_required.error = "Settle failed: " + settle_response.error
return JSONResponse(
content=payment_required.model_dump(by_alias=True),
status_code=402,
)
For more examples and advanced usage patterns, check out our examples directory.
Multi-Chain Support
TON Network
from t402 import (
TON_MAINNET,
TON_TESTNET,
validate_ton_address,
prepare_ton_payment_header,
get_ton_network_config,
)
# Validate address
is_valid = validate_ton_address("EQD...")
# Get network config
config = get_ton_network_config(TON_MAINNET)
TRON Network
from t402 import (
TRON_MAINNET,
TRON_NILE,
validate_tron_address,
prepare_tron_payment_header,
get_tron_network_config,
)
# Validate address
is_valid = validate_tron_address("T...")
# Get network config
config = get_tron_network_config(TRON_MAINNET)
Solana (SVM) Network
from t402 import (
SOLANA_MAINNET,
SOLANA_DEVNET,
SOLANA_TESTNET,
validate_svm_address,
prepare_svm_payment_header,
get_svm_network_config,
get_svm_usdc_address,
is_svm_network,
)
# Validate address
is_valid = validate_svm_address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
# Get network config
config = get_svm_network_config(SOLANA_MAINNET)
# Get USDC mint address
usdc_mint = get_svm_usdc_address(SOLANA_MAINNET)
# Check if network is Solana
is_solana = is_svm_network("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp")
Install with optional Solana dependencies:
pip install t402[svm]
ERC-4337 Account Abstraction
Gasless payments using smart accounts and paymasters:
from t402 import (
create_bundler_client,
create_paymaster,
create_smart_account,
SafeAccountConfig,
)
# Create bundler client
bundler = create_bundler_client(
bundler_type="pimlico",
api_key="your_api_key",
chain_id=8453 # Base
)
# Create paymaster for sponsored transactions
paymaster = create_paymaster(
paymaster_type="pimlico",
api_key="your_api_key",
chain_id=8453
)
# Create Safe smart account
account = create_smart_account(
config=SafeAccountConfig(
owner_private_key="0x...",
chain_id=8453,
),
bundler=bundler,
paymaster=paymaster,
)
USDT0 Cross-Chain Bridge
Bridge USDT0 across chains using LayerZero:
from t402 import (
create_usdt0_bridge,
create_cross_chain_payment_router,
get_bridgeable_chains,
)
# Check supported chains
chains = get_bridgeable_chains()
# Create bridge client
bridge = create_usdt0_bridge(
private_key="0x...",
source_chain_id=1, # Ethereum
)
# Get quote
quote = await bridge.get_quote(
destination_chain_id=8453, # Base
amount="1000000", # 1 USDT0
)
# Execute bridge
result = await bridge.bridge(
destination_chain_id=8453,
amount="1000000",
recipient="0x...",
)
Deprecation Notice: exact-legacy Scheme
⚠️ Deprecated in v2.3.0: The
exact-legacyscheme is deprecated and will be removed in v3.0.0.
The exact-legacy scheme uses the traditional approve + transferFrom pattern for legacy USDT tokens. This has been superseded by the exact scheme with USDT0.
Why Migrate?
| Feature | exact-legacy | exact (USDT0) |
|---|---|---|
| Transactions | 2 (approve + transfer) | 1 (single signature) |
| Gas Cost | User pays gas | Gasless (EIP-3009) |
| Chains | ~5 chains | 19+ chains |
| Cross-chain | ❌ | ✅ LayerZero bridge |
Migration Guide
# Before (deprecated)
from t402.schemes.evm import ExactLegacyEvmClientScheme, ExactLegacyEvmServerScheme
client_scheme = ExactLegacyEvmClientScheme(signer)
server_scheme = ExactLegacyEvmServerScheme()
# After (recommended)
from t402.schemes.evm import ExactEvmClientScheme, ExactEvmServerScheme
client_scheme = ExactEvmClientScheme(signer)
server_scheme = ExactEvmServerScheme()
USDT0 Token Addresses
| Chain | USDT0 Address |
|---|---|
| Ethereum | 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee |
| Arbitrum | 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 |
| Ink | 0x0200C29006150606B650577BBE7B6248F58470c1 |
| Berachain | 0x779Ded0c9e1022225f8E0630b35a9b54bE713736 |
| And 15+ more... | See USDT0 documentation |
WDK Integration
Tether Wallet Development Kit support:
from t402 import (
WDKSigner,
generate_seed_phrase,
WDKConfig,
get_wdk_usdt0_chains,
)
# Generate new wallet
seed = generate_seed_phrase()
# Create WDK signer
signer = WDKSigner(
config=WDKConfig(
seed_phrase=seed,
chains=get_wdk_usdt0_chains(),
)
)
# Get address
address = await signer.get_address(chain_id=8453)
# Sign payment
signature = await signer.sign_payment(
chain_id=8453,
amount="1000000",
recipient="0x...",
)
API Reference
Core Types
| Type | Description |
|---|---|
PaymentRequirements |
Payment configuration |
PaymentPayload |
Signed payment data |
VerifyResponse |
Verification result |
SettleResponse |
Settlement result |
Network Utilities
| Function | Description |
|---|---|
is_evm_network(network) |
Check if EVM network |
is_ton_network(network) |
Check if TON network |
is_tron_network(network) |
Check if TRON network |
is_svm_network(network) |
Check if Solana SVM network |
get_network_type(network) |
Get network type string |
Facilitator Client
from t402 import FacilitatorClient, FacilitatorConfig
client = FacilitatorClient(FacilitatorConfig(
url="https://facilitator.t402.io"
))
# Verify payment
result = await client.verify(payload, requirements)
# Settle payment
result = await client.settle(payload, requirements)
Requirements
- Python 3.10+
- pip or uv package manager
Documentation
Full documentation available at docs.t402.io
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file t402-1.9.1.tar.gz.
File metadata
- Download URL: t402-1.9.1.tar.gz
- Upload date:
- Size: 2.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8c7ad0cdaa14078d7e38c90029533da612609d562a233e8d5f75cc097a7d154
|
|
| MD5 |
b2d43ddbe0c2aeaba6c0aac4187100c1
|
|
| BLAKE2b-256 |
edf10356dc36cf7654bcfc8fb8d0b1fff45f9edfffae25da8b9b7f58c35b981e
|
File details
Details for the file t402-1.9.1-py3-none-any.whl.
File metadata
- Download URL: t402-1.9.1-py3-none-any.whl
- Upload date:
- Size: 1.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56de53842015f89340bf250b07145635de2ce1efb651dd2fee951365426086be
|
|
| MD5 |
b5c38f1df796701217992620e7c0f0fc
|
|
| BLAKE2b-256 |
c356c32f88444bfd3384c0da8acc2da0a77f3b788f6893152400ad9bd9c14e56
|