Metadata-Version: 2.5
Name: pympp
Version: 0.14.0
Summary: Python SDK for the Machine Payments Protocol (MPP)
Project-URL: Homepage, https://github.com/tempoxyz/pympp
Project-URL: Documentation, https://github.com/tempoxyz/pympp#readme
Author: Tempo
License: MIT OR Apache-2.0
License-File: LICENSE-APACHE
License-File: LICENSE-MIT
Classifier: License :: OSI Approved :: Apache Software License
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: rfc8785>=0.1.4
Provides-Extra: dev
Requires-Dist: hypothesis>=6.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff==0.16.6; extra == 'dev'
Requires-Dist: starlette>=0.37; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: anyio>=4.5; extra == 'mcp'
Requires-Dist: mcp<2,>=1.1; extra == 'mcp'
Requires-Dist: propcache==0.5.2; extra == 'mcp'
Requires-Dist: pyjwt==2.15.1; extra == 'mcp'
Requires-Dist: uvicorn==0.52.4; extra == 'mcp'
Requires-Dist: yarl==1.24.5; extra == 'mcp'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: server
Requires-Dist: pydantic>=2.0; extra == 'server'
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.20; extra == 'sqlite'
Provides-Extra: stripe
Requires-Dist: attrs>=23.0; extra == 'stripe'
Requires-Dist: eth-abi<6,>=5.0; extra == 'stripe'
Requires-Dist: eth-account>=0.11; extra == 'stripe'
Requires-Dist: eth-hash[pycryptodome]>=0.7; extra == 'stripe'
Requires-Dist: propcache==0.5.2; extra == 'stripe'
Requires-Dist: pydantic>=2.0; extra == 'stripe'
Requires-Dist: pyjwt==2.15.1; extra == 'stripe'
Requires-Dist: pytempo>=0.2.1; extra == 'stripe'
Requires-Dist: rlp>=4.0; extra == 'stripe'
Requires-Dist: stripe>=8.0.0; extra == 'stripe'
Requires-Dist: urllib3==2.8.0; extra == 'stripe'
Requires-Dist: yarl==1.24.5; extra == 'stripe'
Provides-Extra: tempo
Requires-Dist: attrs>=23.0; extra == 'tempo'
Requires-Dist: eth-abi<6,>=5.0; extra == 'tempo'
Requires-Dist: eth-account>=0.11; extra == 'tempo'
Requires-Dist: eth-hash[pycryptodome]>=0.7; extra == 'tempo'
Requires-Dist: propcache==0.5.2; extra == 'tempo'
Requires-Dist: pydantic>=2.0; extra == 'tempo'
Requires-Dist: pyjwt==2.15.1; extra == 'tempo'
Requires-Dist: pytempo>=0.2.1; extra == 'tempo'
Requires-Dist: rlp>=4.0; extra == 'tempo'
Requires-Dist: urllib3==2.8.0; extra == 'tempo'
Requires-Dist: yarl==1.24.5; extra == 'tempo'
Description-Content-Type: text/markdown

# pympp

Python SDK for the [**Machine Payments Protocol**](https://mpp.dev)

[![PyPI](https://img.shields.io/pypi/v/pympp.svg)](https://pypi.org/project/pympp/)
[![License](https://img.shields.io/pypi/l/pympp.svg)](LICENSE)

## Documentation

Full documentation, API reference, and guides are available at **[mpp.dev/sdk/python](https://mpp.dev/sdk/python)**.

## Install

```bash
pip install pympp
```

## Quick Start

### Server

```python
from mpp import Credential, Receipt
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent

server = Mpp.create(
    method=tempo(
        intents={"charge": ChargeIntent()},
        recipient="0x742d35Cc6634c0532925a3b844bC9e7595F8fE00",
    ),
)


@app.get("/paid")
@server.pay(amount="0.50")
async def handler(request, credential: Credential, receipt: Receipt):
    return {"data": "...", "payer": credential.source}
```

If the endpoint already uses `Authorization` (API keys, Bearer tokens), create the server with `requires_auth=True`. Challenges then advertise `header="Payment-Authorization"`, and clients send the Payment credential in that header instead of `Authorization`.

```python
server = Mpp.create(method=tempo(...), requires_auth=True)

result = await server.charge(
    authorization=request.headers.get("Authorization"),
    amount="0.50",
    payment_authorization=request.headers.get("Payment-Authorization"),
)
```

### Client

```python
from mpp.client import Client
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent

account = TempoAccount.from_key("0x...")

async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
    response = await client.get("https://mpp.dev/api/ping/paid")
```

Custom transports can reuse method matching and credential creation without
depending on HTTPX:

```python
from mpp.client import PaymentTransport
from mpp.runtime import PaymentRuntime

payments = PaymentRuntime([method])
challenge, method = payments.match_challenge(challenges)
credential = await payments.create_credential(challenge, method)

transport = PaymentTransport(runtime=payments)
```

## Examples

Stripe charge and composed-offer inputs accept `payment_intent_options` as a
mapping or a sync/async callable receiving `PaymentIntentContext`. The supported
fields are `customer`, `receipt_email`, `metadata`, and
`hooks.inputs.tax.calculation`. The input stays out of the signed challenge.
Resolvers run in the terminal payment operation, after available credential
validation; `validate_credential()` never invokes them.

```python
async def options(context):
    return {"metadata": {"order_id": "order_123"}}


result = await server.charge(
    authorization,
    "0.50",
    payment_intent_options=options,
)
# Also supported in compose offer mappings and server.pay(...).
```

Both Stripe rails include SDK/challenge analytics. Existing configured metadata
retains its precedence, including the forced `machine_payment="true"` flag;
the new options' metadata overrides all generated/configured metadata. Crypto
recording retries a definitive optional-field rejection once without optional
fields, and remains best-effort after settlement.

Custom methods may implement the optional
`prepare_intent(intent, input) -> (intent, request_input)` hook to consume private
method-specific input before request construction. Return an isolated intent
view; never mutate a shared intent or execute deferred work during preparation.
Unconsumed unknown offer options are still rejected. Existing methods need no
changes, and the intent verification signatures and event payloads are unchanged.

| Example | Description |
|---------|-------------|
| [api-server](./examples/api-server/) | Payment-gated API server |
| [fetch](./examples/fetch/) | CLI tool for fetching URLs with automatic payment handling |
| [mcp-server](./examples/mcp-server/) | MCP server with payment-protected tools |

## Protocol

Built on the ["Payment" HTTP Authentication Scheme](https://datatracker.ietf.org/doc/draft-ryan-httpauth-payment/). See [mpp-specs](https://tempoxyz.github.io/mpp-specs/) for the full specification.

## License

MIT OR Apache-2.0
