Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified e2e/clients/go-http/main
Binary file not shown.
4 changes: 2 additions & 2 deletions e2e/clients/httpx/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions e2e/clients/requests/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from dotenv import load_dotenv
from eth_account import Account

# Import from new x402 package
from x402 import x402Client
# Import from new x402 package (sync variant for requests)
from x402 import x402ClientSync
from x402.http import decode_payment_response_header
from x402.http.clients import x402_requests
from x402.mechanisms.evm import EthAccountSigner
Expand Down Expand Up @@ -36,8 +36,8 @@


def main():
# Create x402 client
client = x402Client()
# Create x402 client (sync for requests)
client = x402ClientSync()

# Register EVM exact scheme if private key is available
if evm_private_key:
Expand Down
4 changes: 2 additions & 2 deletions e2e/clients/requests/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified e2e/facilitators/go/go
Binary file not shown.
6 changes: 1 addition & 5 deletions e2e/facilitators/python/bazaar.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Bazaar catalog for storing discovered x402 resources.

This module provides a simple in-memory catalog for discovered resources
during e2e testing, following the same pattern as TypeScript and Go implementations.
This module provides a simple in-memory catalog for discovered resources during e2e testing
"""

from datetime import datetime
Expand Down Expand Up @@ -45,9 +44,6 @@ def to_dict(self) -> dict[str, Any]:

class BazaarCatalog:
"""Catalog for storing discovered x402 resources.

This follows the same interface as the TypeScript and Go implementations
in the e2e test suite.
"""

def __init__(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions e2e/facilitators/python/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ async def verify(request: VerifyRequest):
# Hooks will automatically:
# - Track verified payment (on_after_verify)
# - Extract and catalog discovery info (on_after_verify)
response = facilitator.verify(payload, requirements)
response = await facilitator.verify(payload, requirements)

return {
"isValid": response.is_valid,
Expand Down Expand Up @@ -213,7 +213,7 @@ async def settle(request: SettleRequest):
# - Validate payment was verified (on_before_settle - will abort if not)
# - Check verification timeout (on_before_settle)
# - Clean up tracking (on_after_settle / on_settle_failure)
response = facilitator.settle(payload, requirements)
response = await facilitator.settle(payload, requirements)

return {
"success": response.success,
Expand Down
4 changes: 2 additions & 2 deletions e2e/servers/fastapi/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions e2e/servers/flask/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

from dotenv import load_dotenv

# Import from new x402 package
from x402 import x402ResourceServer
from x402.http import FacilitatorConfig, HTTPFacilitatorClient
# Import from new x402 package (sync variants for Flask)
from x402 import x402ResourceServerSync
from x402.http import FacilitatorConfig, HTTPFacilitatorClientSync
from x402.http.middleware.flask import PaymentMiddleware
from x402.mechanisms.evm.exact import register_exact_evm_server
from x402.mechanisms.svm.exact import register_exact_svm_server
Expand Down Expand Up @@ -47,17 +47,17 @@

app = Flask(__name__)

# Create HTTP facilitator client
# Create HTTP facilitator client (sync for Flask)
if FACILITATOR_URL:
print(f"Using remote facilitator at: {FACILITATOR_URL}")
config = FacilitatorConfig(url=FACILITATOR_URL)
facilitator = HTTPFacilitatorClient(config)
facilitator = HTTPFacilitatorClientSync(config)
else:
print("Using default facilitator")
facilitator = HTTPFacilitatorClient()
facilitator = HTTPFacilitatorClientSync()

# Create resource server
server = x402ResourceServer(facilitator)
# Create resource server (sync for Flask)
server = x402ResourceServerSync(facilitator)

# Register EVM and SVM exact schemes
register_exact_evm_server(server, EVM_NETWORK)
Expand Down
4 changes: 2 additions & 2 deletions e2e/servers/flask/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified e2e/servers/gin/gin
Binary file not shown.
27 changes: 20 additions & 7 deletions examples/python/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
# x402 Python Examples

Simple examples for the x402 Python SDK.
Examples for the x402 Python SDK.

## Quick Start

```bash
cd clients
pip install httpx requests python-dotenv eth-account web3
cp .env-example .env
# Edit .env with your EVM_PRIVATE_KEY
python example.py
cd clients/httpx
cp .env-local .env
# Edit .env with your EVM_PRIVATE_KEY and/or SVM_PRIVATE_KEY
uv sync
uv run python main.py
```

## V2 SDK (Recommended)

- **[clients/example.py](./clients/example.py)** - Simple client example showing httpx (async) and requests (sync)
### Clients
- **[clients/httpx/](./clients/httpx/)** - Async HTTP client with httpx
- **[clients/requests/](./clients/requests/)** - Sync HTTP client with requests
- **[clients/custom/](./clients/custom/)** - Manual payment handling
- **[clients/advanced/](./clients/advanced/)** - Hooks, selectors, and builder patterns

### Servers
- **[servers/fastapi/](./servers/fastapi/)** - FastAPI server with payment middleware
- **[servers/flask/](./servers/flask/)** - Flask server with payment middleware
- **[servers/custom/](./servers/custom/)** - Manual payment handling
- **[servers/advanced/](./servers/advanced/)** - Dynamic pricing, hooks, and more

### Facilitator
- **[facilitator/](./facilitator/)** - Payment facilitator service

## Legacy SDK

Expand Down
8 changes: 4 additions & 4 deletions examples/python/clients/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ This directory contains examples demonstrating how to use the x402 v2 SDK with d

```bash
cd httpx
cp .env-example .env
# Edit .env with your PRIVATE_KEY
cp .env-local .env
# Edit .env with your EVM_PRIVATE_KEY and/or SVM_PRIVATE_KEY
uv sync
uv run python main.py
```
Expand All @@ -29,8 +29,8 @@ uv run python main.py

```bash
cd requests
cp .env-example .env
# Edit .env with your PRIVATE_KEY
cp .env-local .env
# Edit .env with your EVM_PRIVATE_KEY and/or SVM_PRIVATE_KEY
uv sync
uv run python main.py
```
Expand Down
16 changes: 2 additions & 14 deletions examples/python/clients/advanced/.env-local
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
# Copy this file to .env and fill in your values
# cp .env-local .env

# EVM private key (with 0x prefix)
# For testing, you can generate one at: https://vanity-eth.tk/
PRIVATE_KEY=

# Solana private key (base58 encoded, 64 bytes)
# For testing, you can generate one with: solana-keygen new
SOLANA_PRIVATE_KEY=

# x402 resource server URL
EVM_PRIVATE_KEY=
SVM_PRIVATE_KEY=
RESOURCE_SERVER_URL=http://localhost:4021

# Endpoint path on the resource server
ENDPOINT_PATH=/weather
8 changes: 5 additions & 3 deletions examples/python/clients/advanced/builder_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,23 @@ async def run_builder_pattern_example(
settle_response = http_client.get_payment_settle_response(
lambda name: response.headers.get(name)
)
print(f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}")
print(
f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}"
)
except ValueError:
print("\nNo payment response header found")


async def main() -> None:
"""Main entry point."""
private_key = os.getenv("PRIVATE_KEY")
private_key = os.getenv("EVM_PRIVATE_KEY")
mainnet_key = os.getenv("MAINNET_PRIVATE_KEY") # Optional: separate mainnet key
testnet_key = os.getenv("TESTNET_PRIVATE_KEY") # Optional: separate testnet key
base_url = os.getenv("RESOURCE_SERVER_URL", "http://localhost:4021")
endpoint_path = os.getenv("ENDPOINT_PATH", "/weather")

if not private_key:
print("Error: PRIVATE_KEY environment variable is required")
print("Error: EVM_PRIVATE_KEY environment variable is required")
print("Please copy .env-local to .env and fill in the values.")
sys.exit(1)

Expand Down
14 changes: 8 additions & 6 deletions examples/python/clients/advanced/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
load_dotenv()


def before_payment_creation_hook(
async def before_payment_creation_hook(
context: PaymentCreationContext,
) -> AbortResult | None:
"""Hook called before payment creation.
Expand All @@ -57,7 +57,7 @@ def before_payment_creation_hook(
return None # Continue with payment creation


def after_payment_creation_hook(context: PaymentCreatedContext) -> None:
async def after_payment_creation_hook(context: PaymentCreatedContext) -> None:
"""Hook called after successful payment creation.

Use this for logging, metrics, or other side effects.
Expand All @@ -70,7 +70,7 @@ def after_payment_creation_hook(context: PaymentCreatedContext) -> None:
print()


def payment_creation_failure_hook(
async def payment_creation_failure_hook(
context: PaymentCreationFailureContext,
) -> None:
"""Hook called when payment creation fails.
Expand Down Expand Up @@ -127,19 +127,21 @@ async def run_hooks_example(private_key: str, url: str) -> None:
settle_response = http_client.get_payment_settle_response(
lambda name: response.headers.get(name)
)
print(f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}")
print(
f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}"
)
except ValueError:
print("\nNo payment response header found")


async def main() -> None:
"""Main entry point."""
private_key = os.getenv("PRIVATE_KEY")
private_key = os.getenv("EVM_PRIVATE_KEY")
base_url = os.getenv("RESOURCE_SERVER_URL", "http://localhost:4021")
endpoint_path = os.getenv("ENDPOINT_PATH", "/weather")

if not private_key:
print("Error: PRIVATE_KEY environment variable is required")
print("Error: EVM_PRIVATE_KEY environment variable is required")
print("Please copy .env-local to .env and fill in the values.")
sys.exit(1)

Expand Down
11 changes: 7 additions & 4 deletions examples/python/clients/advanced/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ def validate_environment() -> tuple[str, str]:
Raises:
SystemExit: If required environment variables are missing.
"""
private_key = os.getenv("PRIVATE_KEY")
private_key = os.getenv("EVM_PRIVATE_KEY")
base_url = os.getenv("RESOURCE_SERVER_URL", "http://localhost:4021")
endpoint_path = os.getenv("ENDPOINT_PATH", "/weather")

if not private_key:
print("Error: PRIVATE_KEY environment variable is required")
print("Error: EVM_PRIVATE_KEY environment variable is required")
print("Please copy .env-local to .env and fill in your private key.")
sys.exit(1)

Expand All @@ -56,18 +56,21 @@ def validate_environment() -> tuple[str, str]:
async def run_hooks_example(private_key: str, url: str) -> None:
"""Run the hooks example."""
from hooks import run_hooks_example

await run_hooks_example(private_key, url)


async def run_preferred_network_example(private_key: str, url: str) -> None:
"""Run the preferred network example."""
from preferred_network import run_preferred_network_example

await run_preferred_network_example(private_key, url)


async def run_builder_pattern_example(private_key: str, url: str) -> None:
"""Run the builder pattern example."""
from builder_pattern import run_builder_pattern_example

await run_builder_pattern_example(private_key, url)


Expand All @@ -86,10 +89,10 @@ async def run_example(name: str, private_key: str, url: str) -> None:
private_key: EVM private key for signing.
url: URL to make the request to.
"""
print(f"\n{'='*60}")
print(f"\n{'=' * 60}")
print(f"Running: {name}")
print(f"Description: {EXAMPLES[name]}")
print(f"{'='*60}\n")
print(f"{'=' * 60}\n")

runner = EXAMPLE_RUNNERS[name]
await runner(private_key, url)
Expand Down
Loading