Documentation Index Fetch the complete documentation index at: https://developers.fd.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Coming soon. The prism-flask package is not yet published to PyPI. This page describes the planned API.
Overview
The prism-flask package provides Flask middleware for payment-protecting your API routes using the x402 protocol. It uses Flask’s decorator pattern and request context for seamless integration.
Pythonic API Flask decorators and context managers
Request Context Payment info in g.prism_payer
Minimal Setup Single function call to configure
Installation
pip
Poetry
requirements.txt
bash poetry add prism-flask
The canonical SDK config is identify_token (PRISM_IDENTIFY_TOKEN env var) — formerly api_key / PRISM_API_KEY. SDKs accept the legacy names as fallback during the migration window; new code should use the canonical names.
Quick Start
from flask import Flask, g, jsonify
from prism_flask import prism_payment_middleware
from prism_sdk_core import PrismMiddlewareConfig, RoutePaymentConfig
app = Flask ( __name__ )
# Configure Prism middleware
prism_payment_middleware (
app,
config = PrismMiddlewareConfig (
identify_token = 'dev-key-123' ,
base_url = 'https://prism-gw.fd.xyz'
),
routes = {
'/api/premium' : RoutePaymentConfig (
price = 0.01 ,
description = 'Premium API access'
)
}
)
@app . route ( '/api/premium' )
def premium ():
payer = getattr (g, 'prism_payer' , None )
return jsonify ({
'message' : 'Premium content!' ,
'payer' : payer
})
if __name__ == '__main__' :
app. run ( port = 5000 )
Configuration
Middleware Configuration
from prism_sdk_core import PrismMiddlewareConfig
config = PrismMiddlewareConfig (
identify_token = 'your-project-identify-token' , # Required: Your Prism API key
base_url = 'https://...' , # Optional: Gateway URL
debug = True # Optional: Enable debug logging
)
Route Configuration
from prism_sdk_core import RoutePaymentConfig
routes = {
'/api/premium' : RoutePaymentConfig (
price = 0.01 , # Price in USD (0.01 = $0.01)
description = 'Premium API' # Human-readable description
)
}
Route Protection Patterns
Exact Path Matching
prism_payment_middleware (
app,
config = config,
routes = {
'/api/premium' : RoutePaymentConfig (
price = 0.01 ,
description = 'Premium API'
),
'/api/weather' : RoutePaymentConfig (
price = '$0.001' ,
description = 'Weather data'
)
}
)
# ✅ Protected: GET /api/premium
# ✅ Protected: GET /api/weather
# ❌ Not protected: GET /api/public
Wildcard Matching
prism_payment_middleware (
app,
config = config,
routes = {
'/api/*' : RoutePaymentConfig (
price = 0.005 ,
description = 'API access'
)
}
)
# ✅ Protected: GET /api/users
# ✅ Protected: GET /api/posts/123
# ❌ Not protected: GET /public
Multiple Route Groups
prism_payment_middleware (
app,
config = config,
routes = {
# Free tier
'/api/basic/*' : RoutePaymentConfig (
price = '$0.0001' ,
description = 'Basic API'
),
# Premium tier
'/api/premium/*' : RoutePaymentConfig (
price = '$0.01' ,
description = 'Premium API'
),
# Specific expensive endpoint
'/api/ai/generate' : RoutePaymentConfig (
price = '$0.50' ,
description = 'AI content generation'
)
}
)
Payment info is stored in Flask’s g context object:
from flask import g
@app . route ( '/api/premium' )
def premium ():
# Access payer wallet address
payer = getattr (g, 'prism_payer' , None )
# Returns: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' or None
# Access full payment object
payment = getattr (g, 'prism_payment' , None )
"""
{
'scheme': 'eip3009',
'network': 'eth-sepolia',
'asset': 'usdc',
'amount': '10000',
'recipient': '0x...',
'nonce': '0x...',
'validBefore': 1735430400,
'signature': '0x...'
}
"""
return jsonify ({
'message' : 'Premium content' ,
'payer' : payer,
'network' : payment. get ( 'network' ) if payment else None
})
Safe Access Helper
def get_payer ():
"""Safely get payer address from request context"""
return getattr (g, 'prism_payer' , None )
def get_payment ():
"""Safely get full payment object from request context"""
return getattr (g, 'prism_payment' , None )
@app . route ( '/api/data' )
def data ():
payer = get_payer ()
if not payer:
return jsonify ({ 'error' : 'Payment required' }), 402
return jsonify ({
'data' : [ 1 , 2 , 3 ],
'payer' : payer
})
Settlement Validation
The Flask middleware uses @app.after_request decorator to validate settlement before sending data:
# Internal implementation (automatic!)
@app . after_request
def settlement_validation ( response ):
"""Validate settlement before sending response"""
settlement_result = g. get ( 'prism_settlement_result' )
if not settlement_result or not settlement_result. get ( 'success' ):
# ❌ Settlement failed - return error instead of data
error_reason = (settlement_result or {}). get ( 'errorReason' , 'Settlement processing failed' )
return jsonify ({
'x402Version' : 2 ,
'error' : 'Payment settlement failed' ,
'details' : error_reason
}), 402
# ✅ Settlement succeeded - add header and return original response
response.headers[ 'X-PAYMENT-RESPONSE' ] = settlement_result[ 'transaction' ]
return response
Key Points:
@app.after_request runs after view function but before sending to client
Can return a different response if settlement fails
Works with all Flask response types (jsonify(), make_response(), etc.)
See Stablecoin Settlement for details.
Error Handling
Payment Errors
When a request lacks valid payment:
HTTP/ 1.1 402 Payment Required
{
"x402Version" : 2 ,
"paymentRequired" : true ,
"acceptedPayments" : [
{
"scheme" : "eip3009" ,
"network" : "eth-sepolia" ,
"asset" : "usdc" ,
"amount" : "10000" ,
"recipient" : "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" ,
"nonce" : "0xabc123..." ,
"validBefore" : 1735430400
}
],
"description" : "Premium API access" ,
"priceUSD" : "0.01"
}
Gateway Errors
HTTP/ 1.1 500 Internal Server Error
{
"x402Version" : 2 ,
"error" : "Gateway Error" ,
"details" : "Failed to contact payment gateway" ,
"gateway" : {
"traceId" : "abc-123-xyz" ,
"timestamp" : "2025-12-18T10:30:00Z"
}
}
Custom Error Handlers
from werkzeug.exceptions import HTTPException
@app . errorhandler ( 402 )
def handle_payment_required ( error ):
"""Custom handler for payment required errors"""
return jsonify ({
'error' : 'Payment required' ,
'message' : 'Please provide payment to access this resource' ,
'documentation' : 'https://apps.fd.xyz/prism/'
}), 402
@app . errorhandler ( 500 )
def handle_internal_error ( error ):
"""Custom handler for internal errors"""
app.logger. error ( f 'Internal error: { error } ' )
return jsonify ({
'error' : 'Internal server error' ,
'message' : 'Something went wrong'
}), 500
Testing
Unit Testing with pytest
import pytest
from flask import Flask
from prism_flask import prism_payment_middleware
from prism_sdk_core import PrismMiddlewareConfig, RoutePaymentConfig
@pytest . fixture
def app ():
"""Create test Flask app"""
app = Flask ( __name__ )
prism_payment_middleware (
app,
config = PrismMiddlewareConfig (
identify_token = 'test-key' ,
base_url = 'http://test-gateway'
),
routes = {
'/api/premium' : RoutePaymentConfig (
price = 0.01 ,
description = 'Test'
)
}
)
@app . route ( '/api/premium' )
def premium ():
from flask import g, jsonify
payer = getattr (g, 'prism_payer' , None )
return jsonify ({ 'message' : 'Premium' , 'payer' : payer})
return app
@pytest . fixture
def client ( app ):
"""Create test client"""
return app. test_client ()
def test_payment_required ( client ):
"""Test 402 response without payment"""
response = client. get ( '/api/premium' )
assert response.status_code == 402
assert response.json[ 'paymentRequired' ] == True
def test_valid_payment ( client ):
"""Test access with valid payment"""
# Mock payment header
payment = {
'scheme' : 'eip3009' ,
'network' : 'eth-sepolia' ,
'asset' : 'usdc' ,
'amount' : '10000' ,
'signature' : '0x' + '0' * 130
}
response = client. get (
'/api/premium' ,
headers = { 'X-PAYMENT' : str (payment)}
)
# In test mode, mock signatures are accepted
assert response.status_code == 200
assert response.json[ 'message' ] == 'Premium'
Integration Testing
import requests
import json
def test_full_payment_flow ():
"""Test complete payment flow with real Gateway"""
base_url = 'http://localhost:5000'
# 1. Request without payment
response = requests. get ( f ' { base_url } /api/premium' )
assert response.status_code == 402
payment_req = response. json ()[ 'acceptedPayments' ][ 0 ]
# 2. Sign payment with wallet (use web3.py)
from eth_account import Account
from eth_account.messages import encode_structured_data
private_key = 'your-test-private-key'
account = Account. from_key (private_key)
# EIP-712 structured data
structured_data = {
'types' : {
'EIP712Domain' : [ ... ],
'TransferAuthorization' : [ ... ]
},
'domain' : { ... },
'message' : payment_req
}
signed = account. sign_message ( encode_structured_data (structured_data))
payment = {
** payment_req,
'signature' : signed.signature. hex ()
}
# 3. Send request with signed payment
response = requests. get (
f ' { base_url } /api/premium' ,
headers = { 'X-PAYMENT' : json. dumps (payment)}
)
assert response.status_code == 200
assert 'X-PAYMENT-RESPONSE' in response.headers # Transaction hash
Production Deployment
Environment Variables
# .env
PRISM_IDENTIFY_TOKEN = your-production-identify-token
PRISM_BASE_URL = https://prism-gw.fd.xyz
FLASK_ENV = production
Production Configuration
import os
from flask import Flask
from prism_flask import prism_payment_middleware
from prism_sdk_core import PrismMiddlewareConfig, RoutePaymentConfig
app = Flask ( __name__ )
# Load config from environment
prism_payment_middleware (
app,
config = PrismMiddlewareConfig (
identify_token = os. getenv ( 'PRISM_IDENTIFY_TOKEN' ),
base_url = os. getenv ( 'PRISM_BASE_URL' ),
debug = os. getenv ( 'FLASK_ENV' ) != 'production'
),
routes = {
'/api/premium' : RoutePaymentConfig (
price = 0.01 ,
description = 'Premium API'
)
}
)
Logging & Monitoring
import logging
from flask import Flask, g
# Configure logging
logging. basicConfig (
level = logging. INFO ,
format = ' %(asctime)s - %(name)s - %(levelname)s - %(message)s '
)
logger = logging. getLogger ( __name__ )
app = Flask ( __name__ )
@app . before_request
def log_request ():
"""Log incoming requests"""
logger. info ( f 'Request: { request.method } { request.path } ' )
@app . after_request
def log_response ( response ):
"""Log payment events"""
if response.status_code == 402 :
logger. info ( f 'Payment required: { request.path } ' )
elif response.status_code == 200 and hasattr (g, 'prism_payer' ):
payer = getattr (g, 'prism_payer' )
tx_hash = response.headers. get ( 'X-PAYMENT-RESPONSE' )
logger. info ( f 'Payment succeeded: { request.path } | Payer: { payer } | TX: { tx_hash } ' )
return response
WSGI Deployment (Gunicorn)
# Install Gunicorn
pip install gunicorn
# Run with multiple workers
gunicorn -w 4 -b 0.0.0.0:5000 app:app
# gunicorn.conf.py
bind = "0.0.0.0:5000"
workers = 4
worker_class = "sync"
timeout = 30
keepalive = 2
# Logging
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
Type Hints
Full type hint support with mypy:
from typing import Optional, Dict, Any
from flask import Flask, g, jsonify, Response
from prism_flask import prism_payment_middleware
from prism_sdk_core import PrismMiddlewareConfig, RoutePaymentConfig
app: Flask = Flask ( __name__ )
def get_payer () -> Optional[ str ]:
"""Get payer address from request context"""
return getattr (g, 'prism_payer' , None )
def get_payment () -> Optional[Dict[ str , Any]]:
"""Get payment object from request context"""
return getattr (g, 'prism_payment' , None )
@app . route ( '/api/premium' )
def premium () -> Response:
"""Payment-protected endpoint"""
payer: Optional[ str ] = get_payer ()
return jsonify ({
'message' : 'Premium content' ,
'payer' : payer
})
Examples
AI Agent API
from flask import Flask, request, jsonify, g
from prism_flask import prism_payment_middleware
from prism_sdk_core import PrismMiddlewareConfig, RoutePaymentConfig
app = Flask ( __name__ )
prism_payment_middleware (
app,
config = PrismMiddlewareConfig ( identify_token = 'your-project-identify-token' ),
routes = {
'/api/ai/chat' : RoutePaymentConfig (
price = '$0.01' ,
description = 'AI chat response'
),
'/api/ai/image' : RoutePaymentConfig (
price = '$0.50' ,
description = 'AI image generation'
)
}
)
@app . route ( '/api/ai/chat' , methods = [ 'POST' ])
def ai_chat ():
"""AI chat endpoint"""
data = request. get_json ()
message = data. get ( 'message' )
payer = getattr (g, 'prism_payer' )
# Call AI service
response = call_ai_service (message)
return jsonify ({
'response' : response,
'payer' : payer,
'credits_used' : 1
})
Content Paywall
@app . route ( '/articles/<int:article_id>' )
def get_article ( article_id ):
"""Payment-protected article access"""
payer = getattr (g, 'prism_payer' )
article = db. get_article (article_id)
return jsonify ({
'id' : article.id,
'title' : article.title,
'content' : article.content,
'author' : article.author,
'payer' : payer
})
Premium API with Rate Limiting
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter (
app = app,
key_func = get_remote_address,
default_limits = [ "100 per hour" ]
)
@app . route ( '/api/premium' )
@limiter . limit ( "10 per minute" ) # Additional rate limit for premium
def premium ():
"""Rate-limited premium endpoint"""
payer = getattr (g, 'prism_payer' )
return jsonify ({
'message' : 'Premium content' ,
'payer' : payer
})
Troubleshooting
ImportError: cannot import name 'prism_payment_middleware'
Make sure you installed the correct package: pip install prism-flask # Not prism-sdk-core alone
Check Python version: python --version # Requires Python 3.8+
g.prism_payer is None even with valid payment
Check: 1. Payment header is properly formatted JSON string 2. Middleware
is configured BEFORE route definitions 3. Route path matches configured route
pattern 4. Payment signature is valid Debug: python @app.before_request def debug_payment(): print(f'Path: {request.path}') print(f'Payment header:{" "} {request.headers.get("X-PAYMENT")}') print(f'Payer:{" "} {getattr(g, "prism_payer", "NOT SET")}')
Settlement fails but payment was valid
Common causes: 1. Insufficient balance in sender’s wallet 2. Token
allowance not set for USDC contract 3. Network congestion (transaction
timeout) 4. Nonce already used (replay attack prevention) Check logs:
python import logging logging.basicConfig(level=logging.DEBUG)
Type hints not working in IDE
Install type stubs: Configure mypy: # mypy.ini
[mypy]
python_version = 3.8
plugins = sqlmypy
Flask-CORS Integration
Enable CORS for browser-based clients:
from flask import Flask
from flask_cors import CORS
from prism_flask import prism_payment_middleware
app = Flask ( __name__ )
# Configure CORS
CORS (app, resources = {
r "/api/ * " : {
"origins" : [ "https://yourapp.com" ],
"methods" : [ "GET" , "POST" ],
"allow_headers" : [ "Content-Type" , "X-PAYMENT" ],
"expose_headers" : [ "X-PAYMENT-RESPONSE" ]
}
})
# Then configure Prism
prism_payment_middleware (app, config, routes)
Blueprint Support
Works with Flask Blueprints:
from flask import Blueprint, g, jsonify
api_bp = Blueprint ( 'api' , __name__ , url_prefix = '/api' )
@api_bp . route ( '/premium' )
def premium ():
"""Blueprint route - payment still works!"""
payer = getattr (g, 'prism_payer' )
return jsonify ({ 'message' : 'Premium' , 'payer' : payer})
# Register blueprint
app. register_blueprint (api_bp)
# Configure Prism with blueprint routes
prism_payment_middleware (
app,
config = config,
routes = {
'/api/premium' : RoutePaymentConfig ( price = 0.01 , description = 'Premium' )
}
)