Python SDK

Beta

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

Installation

pip install orvion

Quick Start

from fastapi import FastAPI, Request
from orvion.fastapi import OrvionMiddleware, require_payment
import os

app = FastAPI()

# Add the middleware
app.add_middleware(
    OrvionMiddleware,
    api_key=os.environ["ORVION_API_KEY"],
)

# Protect an endpoint
@app.get("/api/premium")
@require_payment(amount="0.10", currency="USDC")
async def premium(request: Request):
    return {"message": "Premium content!"}

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)

Example

app.add_middleware(
    OrvionMiddleware,
    api_key=os.environ["ORVION_API_KEY"],
    cache_ttl_seconds=120.0,  # Cache routes for 2 minutes
    transaction_header="X-Payment-Id",  # Custom header name
)

@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_checkoutboolOptionalRedirect to pay.orvion.sh instead of 402 (default: False)
return_urlstr?OptionalReturn URL after hosted checkout payment

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

Examples

# 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": "..."}

# Hosted checkout mode
@app.get("/premium")
@require_payment(
    amount="1.00",
    currency="USDC",
    hosted_checkout=True,
)
async def premium_hosted(request: Request):
    return {"message": "Premium content!"}

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

The SDK provides a pre-built router that adds all payment endpoints automatically - no boilerplate needed!

Usage

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 the following endpoints:

| Endpoint | Method | Description | |----------|--------|-------------| | /api/payments/confirm | POST | Confirm wallet payment with tx_hash | | /api/payments/cancel/{id} | POST | Cancel a pending charge | | /api/payments/state/{id} | GET | Get charge state for payment widgets | | /api/payments/charge/{id} | GET | Get full charge details |

Available Routers

from orvion.fastapi import (
    create_payment_router,   # Payment operations
    create_health_router,    # Health check endpoint
    create_full_router,      # All endpoints combined
)

# Payment router only
app.include_router(create_payment_router(client), prefix="/api/payments")

# Health router only
app.include_router(create_health_router(client), prefix="/api")

# Combined router (payments + health)
app.include_router(create_full_router(client), prefix="/api/orvion")

OrvionClient

The client provides direct API access for all payment operations.

New Methods (v0.2)

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}")
    print(f"API Key Valid: {health.api_key_valid}")
    
    # Create a charge
    charge = await client.create_charge(
        amount="0.10",
        currency="USDC",
        description="API access",
    )
    
    # Get charge details
    charge = await client.get_charge(charge.id)
    
    # Get charge state (optimized for UI widgets)
    state = await client.get_charge_state(charge.id)
    print(f"Status: {state.status}")
    print(f"Recipient: {state.recipient_address}")
    
    # Confirm wallet payment
    result = await client.confirm_payment(
        transaction_id=charge.id,
        tx_hash="blockchain_tx_signature...",
    )
    print(f"Success: {result.success}")
    
    # Cancel a pending charge
    cancelled = await client.cancel_charge(charge.id)
    
    # Verify a payment
    result = await client.verify_charge(
        transaction_id=charge.id,
        resource_ref="my-resource",
    )

All Client Methods

| Method | Description | |--------|-------------| | create_charge() | Create a new payment charge | | verify_charge() | Verify a payment transaction | | get_charge() | Get charge details by ID | | get_charge_state() | Get UI-optimized charge state | | confirm_payment() | Confirm wallet payment with tx_hash | | cancel_charge() | Cancel a pending charge | | health_check() | Validate API key & get org info | | get_routes() | Get protected route configurations | | match_route() | Match a request to a protected route | | register_route() | Register a new protected route |


Models

Core Models

from orvion import (
    Charge,           # Payment charge
    VerifyResult,     # Payment verification result
    PaymentInfo,      # Payment info attached to request
    RouteConfig,      # Protected route configuration
)

New Models (v0.2)

from orvion import (
    ConfirmResult,      # Result of wallet payment confirmation
    ChargeState,        # UI state for payment widgets
    HealthInfo,         # API health & organization info
    WalletPaymentInfo,  # Wallet payment details
    PaymentMethodInfo,  # Available payment methods
)

Example: Parsing Payment Methods

from orvion import PaymentMethodInfo

# Parse payment methods from x402_requirements
charge = await client.create_charge(amount="0.10", currency="USDC")
methods = PaymentMethodInfo.from_x402_requirements(charge.x402_requirements)

print(f"Available methods: {methods.methods}")  # ["solana"]
if methods.solana:
    print(f"Recipient: {methods.solana.recipient_address}")
    print(f"Token: {methods.solana.token_address}")
    print(f"Amount: {methods.solana.amount}")

Route Registration

Routes are automatically registered with Orvion when your app starts.

Default: First Request

By default, routes register on the first request to your app:

app.add_middleware(
    OrvionMiddleware,
    api_key=API_KEY,
    # register_on_first_request=True (default)
)

Explicit Startup Registration

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

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 all protected 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 (Minimal)

This example shows how thin your app can be with the SDK:

from fastapi import FastAPI, Request
from orvion import OrvionClient
from orvion.fastapi import (
    OrvionMiddleware,
    require_payment,
    create_payment_router,
)
import os

app = FastAPI()
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 (confirm, cancel, state, charge endpoints)
app.include_router(create_payment_router(client), prefix="/api/payments")

# Protected endpoint - that's it!
@app.get("/api/premium")
@require_payment(amount="0.01", currency="USDC")
async def premium(request: Request):
    return {"access": "granted", "message": "Welcome to premium!"}

Error Handling

from orvion import OrvionClient, OrvionAPIError, OrvionAuthError

try:
    client = OrvionClient(api_key="your-api-key")
    charge = await client.create_charge(amount="0.10", currency="USDC")
except OrvionAuthError:
    print("Invalid API key")
except OrvionAPIError as e:
    print(f"API Error: {e.status_code} - {e.message}")
except Exception as e:
    print(f"Unexpected error: {e}")

Related Documentation