Metadata-Version: 2.4
Name: blockchain0x-x402
Version: 0.0.1a0
Summary: Official Python port of the @blockchain0x/x402 wire primitives + client + server adapters
Project-URL: Homepage, https://blockchain0x.com
Project-URL: Documentation, https://docs.blockchain0x.com
Project-URL: Repository, https://github.com/Tosh-Labs/blockchain0x-x402-python
Project-URL: Source, https://github.com/Tosh-Labs/blockchain0x-app/tree/dev/packages/sdk-python-x402
Author-email: Blockchain0x <support@blockchain0x.com>
License: Apache-2.0
License-File: LICENSE
Keywords: blockchain0x,http-402,payments,stablecoin,usdc,x402
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# blockchain0x-x402 (Python)

[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
[![Python ≥ 3.9](https://img.shields.io/badge/python-%E2%89%A53.9-brightgreen.svg)](#requirements)

**Official Python port of [`@blockchain0x/x402`](https://www.npmjs.com/package/@blockchain0x/x402).**
Ships the wire primitives + a 402-aware fetch wrapper. Sibling
package to `blockchain0x`; install only when your service either
issues x402-aware HTTP calls (a payer) or verifies inbound x402
payments (a recipient).

> Pre-release: `0.0.1a0` ships the wire primitives + the `X402Client`
> 402-aware fetch wrapper + ASGI middleware (Starlette / FastAPI /
> Quart) + a Flask `before_request` hook.

## Why a separate package

x402 is a separable concern from the rest of the Blockchain0x SDK:

- A consumer who only verifies inbound webhooks does not need the
  x402 client stack.
- A service that exposes paid HTTP routes does not need the webhook
  verifier bundled with it.

Keeping them as two pip-installable packages lets each downstream
service depend only on what it actually uses.

## Install

```bash
pip install blockchain0x-x402
```

For the payer path (`X402Client`) you also need the main SDK:

```bash
pip install blockchain0x blockchain0x-x402
```

For the recipient/verifier path you only need `blockchain0x-x402`.

## Verify an inbound x402 payment (recipient)

```python
from blockchain0x_x402 import parse_payment_header, X402WireError

def verify(request):
    raw = request.headers.get("X-Payment")
    try:
        payment = parse_payment_header(raw)
    except X402WireError as e:
        return JSONResponse({"code": e.code}, status_code=400)
    # payment.payment_request_id, payment.tx_hash, payment.network
    # ...
```

The verifier:

- Accepts only `exact-usdc:<base64>` scheme; anything else rejects with
  `header.unknown_scheme`.
- Validates txHash, payerAddress, amountUsdc, and network shape; any
  drift rejects with `header.payload_malformed`.
- Lowercases hex fields so downstream comparisons against on-chain
  transaction logs are deterministic.

## Issue x402-aware HTTP calls (payer)

```python
import os
from blockchain0x import Client
from blockchain0x_x402 import X402Client

sdk = Client(api_key=os.environ["BLOCKCHAIN0X_API_KEY"])
x402 = X402Client(sdk=sdk, agent_id="agt_...")
response = x402.fetch("https://service-b.com/llm-query", method="POST")
response.raise_for_status()
```

The wrapper handles a 402 response transparently:

1. Parses the 402 body and picks the requirement matching the SDK's network.
2. Calls `sdk.payments.create(...)` to settle on-chain. The SDK
   auto-attaches an `Idempotency-Key` so a flaky retry does not
   double-spend.
3. Polls `sdk.transactions.get(payment_id)` every 1s for up to 30s
   until the transaction confirms.
4. Rebuilds the request with the `X-Payment` header and re-issues
   it once. The second 200 response is returned to the caller.

Failures surface as `X402ClientError` with stable codes:

- `no_matching_requirement` - the 402 had no `accepts` entry for the
  SDK's network mode.
- `settlement_timeout` - the on-chain payment did not confirm within
  the poll window.
- `chain_failed` - the payment row flipped to `failed` status before
  confirming.

## Expose a paid HTTP route (recipient, server-side)

Two ready-made adapters wrap the verifier into a framework-level
gate so paid routes do not have to repeat the parse + settle dance
themselves.

### Starlette / FastAPI / any ASGI app

```python
from starlette.applications import Starlette
from blockchain0x import Client
from blockchain0x_x402.server import X402Middleware, PricingEntry

sdk = Client(api_key=os.environ["BLOCKCHAIN0X_API_KEY"])

app = Starlette(routes=[...])
app.add_middleware(
    X402Middleware,
    sdk=sdk,
    pricing={
        "POST /llm-query": PricingEntry(
            amount_usdc="0.10",
            pay_to_address="0xabc...",
            payment_request_id="pr_demo",
        ),
    },
)
```

A miss in the pricing table is a no-op (the route is free). A hit
with no valid `X-Payment` short-circuits the response with HTTP 402
and the canonical `accepts[]` body the payer needs. A hit with a
valid payment attaches the parsed payload to `scope["x402_payment"]`
for downstream handlers and lets the request proceed.

### Flask

```python
from flask import Flask, g
from blockchain0x import Client
from blockchain0x_x402.server import x402_before_request_factory, PricingEntry

app = Flask(__name__)
app.before_request(x402_before_request_factory(
    sdk=Client(api_key=os.environ["BLOCKCHAIN0X_API_KEY"]),
    pricing={
        "POST /llm-query": PricingEntry(
            amount_usdc="0.10",
            pay_to_address="0xabc...",
            payment_request_id="pr_demo",
        ),
    },
))

@app.post("/llm-query")
def handler():
    # g.x402_payment is the verified ExactUsdcPayment payload.
    ...
```

Both adapters call `sdk.payment_requests.settle()` once per request
to anchor trust; the backend's settle route is the chain-of-custody
boundary. The `settle()` impl may be sync OR async - the adapter
awaits whichever shape it returns.

## Wire-format cross-compatibility

The Python `build_payment_header` output is byte-for-byte identical
to the Node SDK's. A Python payer can pay a Node server, a Node
payer can pay a Python server - the canonical JSON shape is:

```json
{
  "scheme": "exact-usdc",
  "version": 1,
  "paymentRequestId": "...",
  "txHash": "...",
  "payerAddress": "...",
  "amountUsdc": "...",
  "network": "..."
}
```

Keys are insertion-ordered; no whitespace between separators. The
hex fields (`txHash`, `payerAddress`) are lowercased before encoding.

## License

Apache-2.0.
