Python SDK

Beta

The Orvion Python SDK provides seamless integration with FastAPI for creating payment-protected API endpoints using the x402 protocol.

Quick Start

1. Install the SDK

pip install orvion

2. Add Middleware

Python
from fastapi import FastAPI
from orvion.fastapi import OrvionMiddleware
import os
app = FastAPI()
# Add the Orvion middleware
app.add_middleware(
OrvionMiddleware,
api_key=os.environ["ORVION_API_KEY"]
)

3. Protect Endpoints

Python
from orvion.fastapi import require_payment
from fastapi import Request
# Define a protected endpoint with pricing
@app.get("/api/premium/data")
@require_payment(amount="0.10", currency="USDC")
async def premium_data(request: Request):
return {
"data": "Premium content!",
"paid": request.state.payment.amount
}

Pre-built Payment Router (NEW in v0.2)

Eliminate boilerplate by using the pre-built payment router:

Python
from orvion import OrvionClient
from orvion.fastapi import create_payment_router
client = OrvionClient(api_key=os.environ["ORVION_API_KEY"])
# Add all payment endpoints with one line!
app.include_router(
create_payment_router(client),
prefix="/api/payments",
)
# This adds:
# POST /api/payments/confirm - Confirm wallet payment
# POST /api/payments/cancel/{id} - Cancel pending charge
# GET /api/payments/state/{id} - Get charge state for UI
# GET /api/payments/charge/{id} - Get full charge details

OrvionMiddleware

The middleware handles route registration, payment verification, and request state management.

Configuration

FieldTypeRequiredDescriptionExample
api_keystrOptionalYour Orvion API key (required)
base_urlstr?OptionalCustom API base URL (default: https://api.orvion.sh)
cache_ttl_secondsfloatOptionalRoute cache TTL (default: 60.0)
transaction_headerstrOptionalHeader for transaction ID (default: 'X-Transaction-Id')
customer_headerstrOptionalHeader for customer ID (default: 'X-Customer-Id')
register_on_first_requestboolOptionalAuto-register routes on first request (default: True)

@require_payment Decorator

Mark endpoints as payment-protected with configurable pricing.

Parameters

FieldTypeRequiredDescriptionExample
amountstr?OptionalPrice per request (e.g., '0.10')
currencystrOptionalCurrency code (default: 'USD')
namestr?OptionalFriendly name for dashboard/402 response
descriptionstr?OptionalDescription shown in 402 response
allow_anonymousbool?OptionalAllow requests without customer ID (default: True)
customer_resolverCallable?OptionalFunction to extract customer ID from request
hosted_checkoutboolOptionalEnable hosted checkout mode - redirects to pay.orvion.sh (default: False)
return_urlstr?OptionalReturn URL for hosted checkout. Auto-derived using convention: /api/foo → /foo

Always place @require_payment under @app.get/@app.post. The decorator order matters!

Examples

Python
# Basic usage
@app.get("/api/basic")
@require_payment(amount="0.01", currency="USDC")
async def basic_endpoint(request: Request):
return {"data": "..."}
# With metadata
@app.get("/api/premium")
@require_payment(
amount="1.00",
currency="USDC",
name="Premium API Access",
description="Full access to premium features",
)
async def premium_endpoint(request: Request):
return {"data": "..."}
# Require customer identification
@app.get("/api/user-data")
@require_payment(
amount="0.05",
currency="USDC",
allow_anonymous=False,
customer_resolver=lambda req: req.headers.get("X-User-Id"),
)
async def user_data(request: Request):
return {"user": request.state.payment.customer_ref}
# Hosted checkout mode - redirects to pay.orvion.sh
@app.get("/premium")
@require_payment(
amount="1.00",
currency="USDC",
hosted_checkout=True,
)
async def premium_hosted(request: Request):
return {"message": "Premium content after hosted checkout!"}

New Client Methods (v0.2)

The SDK now includes powerful client methods for payment operations:

Python
from orvion import OrvionClient
async with OrvionClient(api_key="your-api-key") as client:
# Health check & API key validation
health = await client.health_check()
print(f"Organization: {health.organization_id}")
# Create a charge
charge = await client.create_charge(
amount="0.10",
currency="USDC",
)
# Get charge state (optimized for UI widgets)
state = await client.get_charge_state(charge.id)
print(f"Status: {state.status}")
# Confirm wallet payment
result = await client.confirm_payment(
transaction_id=charge.id,
tx_hash="blockchain_tx_signature...",
)
# Cancel a pending charge
cancelled = await client.cancel_charge(charge.id)

Accessing Payment Info

After successful payment verification, payment details are available on request.state.payment:

Python
@app.get("/api/premium")
@require_payment(amount="0.10", currency="USDC")
async def premium(request: Request):
payment = request.state.payment
return {
"transaction_id": payment.transaction_id,
"amount": payment.amount,
"currency": payment.currency,
"customer_ref": payment.customer_ref,
}

Route Registration

Routes are automatically registered with Orvion when your app starts.

Explicit Startup Registration

For immediate registration at startup, use sync_routes() in your lifespan:

Python
from contextlib import asynccontextmanager
from orvion import OrvionClient
from orvion.fastapi import OrvionMiddleware, sync_routes
@asynccontextmanager
async def lifespan(app: FastAPI):
client = OrvionClient(api_key=os.environ["ORVION_API_KEY"])
# Verify API key on startup
health = await client.health_check()
if health.api_key_valid:
print(f"✓ Connected to org: {health.organization_id}")
# Register routes
count = await sync_routes(app, client)
print(f"✓ Registered {count} protected routes")
yield
await client.close()
app = FastAPI(lifespan=lifespan)
app.add_middleware(
OrvionMiddleware,
api_key=os.environ["ORVION_API_KEY"],
register_on_first_request=False, # Already synced
)

Complete Example

Python
from fastapi import FastAPI, Request
from orvion import OrvionClient
from orvion.fastapi import (
OrvionMiddleware,
require_payment,
create_payment_router,
)
import os
app = FastAPI(title="My Premium API")
client = OrvionClient(api_key=os.environ["ORVION_API_KEY"])
# Add middleware
app.add_middleware(OrvionMiddleware, api_key=os.environ["ORVION_API_KEY"])
# Add pre-built payment router
app.include_router(create_payment_router(client), prefix="/api/payments")
# Free endpoint
@app.get("/")
async def root():
return {"message": "Welcome! Visit /api/premium for paid content."}
# Premium endpoint - 0.01 USDC
@app.get("/api/premium")
@require_payment(
amount="0.01",
currency="USDC",
name="Premium Content",
)
async def premium(request: Request):
return {
"message": "Welcome to premium!",
"paid": request.state.payment.amount,
}

Hosted Checkout Mode

For web applications where users interact via browser, use hosted checkout mode:

Python
# API endpoint - protected with payment
@app.get("/api/premium")
@require_payment(
amount="1.00",
currency="USDC",
hosted_checkout=True, # Redirect to pay.orvion.sh
)
async def premium_api(request: Request):
return {"message": "Premium content!"}
# Frontend page - serves the UI
@app.get("/premium")
async def premium_page():
return FileResponse("static/premium.html")

Automatic URL Convention

The SDK uses a convention-based approach for seamless UX:

  • API at /api/premiumFrontend at /premium
  • The /api prefix is automatically stripped
  • Users are redirected to the frontend page after payment (not the API)

No return_url configuration needed! Just add hosted_checkout=True and follow the convention.

Python
# That's all you need! No return_url required.
@app.get("/api/premium")
@require_payment(amount="1.00", currency="USDC", hosted_checkout=True)
async def premium(request: Request):
return {"data": "premium"}
# Flow:
# 1. User on /premium clicks "Pay" → redirects to /api/premium
# 2. SDK creates charge, redirects to pay.orvion.sh
# 3. After payment, user returns to /api/premium?charge_id=xxx
# 4. SDK verifies, then redirects to /premium?charge_id=xxx&status=succeeded
# 5. Frontend shows success message

Before using hosted checkout, configure allowed domains in Settings → Domains to enable return URL validation.

See the Hosted Checkout Guide for complete documentation.


Related Documentation