Orvion SDK for x402 V2 payment-protected APIs
Project description
Orvion Python SDK
Python SDK for integrating x402 payment-protected APIs with Orvion.
Installation
pip install orvion
# With FastAPI support
pip install orvion[fastapi]
Quick Start
FastAPI Integration
import os
from fastapi import FastAPI, Request
from orvion.fastapi import OrvionMiddleware, require_payment
app = FastAPI()
# Initialize Orvion middleware
app.add_middleware(
OrvionMiddleware,
api_key=os.environ["ORVION_API_KEY"],
)
# Protect an endpoint with payment requirement
@app.get("/api/premium/data")
@require_payment() # Uses price from dashboard
async def premium_data(request: Request):
# Access payment info
payment = request.state.payment
return {
"data": "Premium content!",
"paid": payment.amount,
"transaction_id": payment.transaction_id,
}
# Override price in code
@app.get("/api/reports/generate")
@require_payment(amount="5.00", currency="USD")
async def generate_report(request: Request):
return {"report": "..."}
# Require customer identification (no anonymous access)
@app.get("/api/ai/chat")
@require_payment(allow_anonymous=False)
async def ai_chat(request: Request):
customer_id = request.state.payment.customer_ref
return {"response": "..."}
Standalone Client
from orvion import OrvionClient
client = OrvionClient(api_key="your_api_key")
# Create a charge
charge = await client.create_charge(
amount="0.10",
currency="USD",
customer_ref="user_123",
resource_ref="protected_route:abc-123",
)
print(f"Charge ID: {charge.id}")
print(f"Payment URL: {charge.x402_requirements}")
# Verify a payment
result = await client.verify_charge(
transaction_id="tx_abc123",
resource_ref="protected_route:abc-123",
)
if result.verified:
print("Payment verified!")
# Process a facilitator payment (x402 V2)
from orvion import build_evm_authorization, build_evm_payment_payload
authorization = build_evm_authorization(
from_address="0x...",
to_address="0x...",
value="1000000",
valid_after="1716150000",
valid_before="1716153600",
nonce="0x...",
)
payment_payload = build_evm_payment_payload(
signature="0x...",
authorization=authorization,
network="base",
)
result = await client.process_payment(
transaction_id=charge.id,
payment_payload=payment_payload,
)
if result.success:
print(f"Payment confirmed: {result.tx_hash}")
# For Solana, build a payload from a signed transaction
from orvion import build_solana_payment_payload
solana_payload = build_solana_payment_payload(
transaction="base64_tx",
network="solana-devnet",
)
Configuration
Environment Variables
ORVION_API_KEY- Your Orvion API keyORVION_BASE_URL- API base URL (default: https://api.orvion.sh)
Middleware Options
app.add_middleware(
OrvionMiddleware,
api_key="your_api_key",
base_url="https://api.orvion.sh", # Custom API URL
cache_ttl_seconds=60, # Route config cache TTL
transaction_header="X-Transaction-Id", # Custom header name
customer_header="X-Customer-Id", # Custom header name
)
Decorator Options
@require_payment(
amount="0.10", # Override dashboard price
currency="USD",
allow_anonymous=False, # Require customer identification
customer_resolver=lambda req: req.state.user.id, # Custom customer ID extraction
hosted_checkout=True, # Enable hosted checkout mode (redirects to pay.orvion.sh)
)
Hosted Checkout Mode
For web applications, use hosted checkout mode for a seamless payment experience:
# API endpoint - protected with payment
@app.get("/api/premium")
@require_payment(
amount="1.00",
currency="USDC",
hosted_checkout=True, # Just add this!
)
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 strips /api prefix automatically:
- API at
/api/premium→ Frontend at/premium - After payment, users are redirected to
/premium?charge_id=xxx&status=succeeded - No
return_urlconfiguration needed!
Testing
The SDK includes comprehensive testing utilities and is fully tested with pytest.
Testing Utilities
from orvion.testing import mock_orvion, fake_payment
# Mock all Orvion calls
with mock_orvion(always_approve=True):
response = client.get("/api/premium/data")
assert response.status_code == 200
# Create fake payment for testing
request.state.payment = fake_payment(
amount="0.10",
currency="USD",
transaction_id="test_tx_123",
)
Running Tests
# Install dev dependencies
pip install -e ".[dev,fastapi]"
# Run all tests
pytest
# Run tests with coverage
pytest --cov=orvion --cov-report=html
# Run specific test categories
pytest tests/unit/ # Unit tests only
pytest tests/integration/ # Integration tests only
pytest tests/e2e/ # E2E tests (requires ORVION_STAGING_API_KEY)
# Run with linting and type checking
ruff check orvion tests
mypy orvion
Test Structure
The SDK uses a three-layer testing approach:
- Unit Tests (
tests/unit/) - Pure logic tests for matcher, cache, and models - Integration Tests (
tests/integration/) - HTTP mocking with respx for client and middleware - E2E Tests (
tests/e2e/) - Smoke tests against staging API
HTTP Mocking with RESPX
The SDK uses respx for HTTP mocking in integration tests:
import respx
from httpx import Response
# Mock API endpoints
with respx.mock(base_url="https://api.orvion.sh") as mock:
mock.post("/v1/charges").mock(
return_value=Response(
200,
json={
"id": "custom_charge",
"amount": "1.00",
"currency": "USD",
"status": "pending",
},
)
)
charge = await client.create_charge(amount="1.00", currency="USD")
assert charge.id == "custom_charge"
FastAPI Middleware Testing
Test FastAPI middleware with TestClient:
from fastapi import FastAPI
from fastapi.testclient import TestClient
from orvion.fastapi import OrvionMiddleware, require_payment
app = FastAPI()
app.add_middleware(OrvionMiddleware, api_key="test_key")
@app.get("/api/premium/data")
@require_payment()
async def premium_data(request: Request):
return {"data": "premium", "payment": request.state.payment}
# Test
client = TestClient(app)
response = client.get(
"/api/premium/data",
headers={"X-Payment-Transaction": "valid_tx_123"}
)
assert response.status_code == 200
assert response.json()["payment"] is not None
Telemetry
Opt-in telemetry is available for monitoring SDK usage:
from orvion import OrvionClient
from orvion.telemetry import TelemetryConfig
client = OrvionClient(
api_key="your_key",
telemetry=TelemetryConfig(
enabled=True,
service_name="my-app",
service_version="1.0.0",
),
)
Privacy: Telemetry is opt-in and never collects PII (no customer refs, transaction IDs, or amounts).
Code Quality
The SDK maintains high code quality standards:
# Format code
black orvion tests
# Sort imports
isort orvion tests
# Type checking
mypy orvion
# Linting
ruff check orvion tests
License
MIT
Project details
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 orvion-0.5.3.tar.gz.
File metadata
- Download URL: orvion-0.5.3.tar.gz
- Upload date:
- Size: 34.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5c53c5552fd473468fafd0b16e19ed95dbc1d9f7f5be7814c729d1f2a0fb4fd
|
|
| MD5 |
e71313709597a5e29c4b858f3688a7d4
|
|
| BLAKE2b-256 |
6466c03fa0e3c1fbdefd887b04956e219eb59e06fe40a7fb6d0f07caf1953ff6
|
File details
Details for the file orvion-0.5.3-py3-none-any.whl.
File metadata
- Download URL: orvion-0.5.3-py3-none-any.whl
- Upload date:
- Size: 35.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
641c58c60919ea67d02b6d939397fb4deefd171eb7b76adac067eff1ebd5a025
|
|
| MD5 |
a941d57f360fefeeb482cf55ffe139b4
|
|
| BLAKE2b-256 |
4ddfcdb5e49e1735a0033c3979dc4b3aa06b368e5d8f395b8ffa81829362eaca
|