payagent 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ .eggs/
6
+ dist/
7
+ build/
8
+ .venv/
9
+ venv/
10
+ .env
11
+ .env.*
12
+ !.env.example
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+ .DS_Store
19
+ *.md.bak
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ .python-version
24
+ *.db
25
+ *.sqlite
26
+ *.sqlite3
27
+ agent_pay_spend.db
payagent-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 poqdev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: payagent
3
+ Version: 0.1.0
4
+ Summary: Universal Payment & Monetization Engine for AI Agents — HTTP 402, multi-rail wallets, budget guardrails, escrow, and @paywall for FastAPI/MCP.
5
+ Project-URL: Homepage, https://github.com/pomoq-dev/payagent
6
+ Project-URL: Repository, https://github.com/pomoq-dev/payagent
7
+ Project-URL: Issues, https://github.com/pomoq-dev/payagent/issues
8
+ Project-URL: Changelog, https://github.com/pomoq-dev/payagent/releases
9
+ Author-email: poqdev <poqdev@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,crypto-wallet,escrow,http-402,mcp,monetization,payagent,payments,paywall,solana,x402
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Framework :: FastAPI
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Office/Business :: Financial
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
29
+ Requires-Dist: httpx>=0.27
30
+ Requires-Dist: pydantic>=2.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: build; extra == 'dev'
33
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
34
+ Requires-Dist: httpx>=0.27; extra == 'dev'
35
+ Requires-Dist: mypy>=1.8; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
37
+ Requires-Dist: pytest>=7; extra == 'dev'
38
+ Requires-Dist: respx>=0.21; extra == 'dev'
39
+ Requires-Dist: starlette>=0.36; extra == 'dev'
40
+ Requires-Dist: twine; extra == 'dev'
41
+ Provides-Extra: fastapi
42
+ Requires-Dist: fastapi>=0.110; extra == 'fastapi'
43
+ Requires-Dist: starlette>=0.36; extra == 'fastapi'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # payagent
47
+
48
+ **Universal Payment & Monetization Engine for AI Agents**
49
+
50
+ [![PyPI](https://img.shields.io/pypi/v/payagent.svg)](https://pypi.org/project/payagent/)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
52
+ [![CI](https://img.shields.io/github/actions/workflow/status/pomoq-dev/payagent/test.yml?branch=main&label=CI)](https://github.com/pomoq-dev/payagent/actions)
53
+ [![Python](https://img.shields.io/pypi/pyversions/payagent.svg)](https://pypi.org/project/payagent/)
54
+
55
+ Enable AI agents to **pay each other** for APIs, data, and compute — and let developers **monetize MCP tools / endpoints** with a one-line decorator.
56
+
57
+ | Capability | What you get |
58
+ |------------|----------------|
59
+ | **HTTP 402** | Client auto-detects `Payment Required`, settles, retries with proof |
60
+ | **Multi-rail wallet** | x402 (Base/EVM), Solana SOL/USDC, fiat adapter (Payman-style) |
61
+ | **Guardrails** | Per-tx / daily / monthly limits, domain allowlist, HITL threshold |
62
+ | **Escrow** | Lock → job → validate → release or refund |
63
+ | **`@paywall`** | Monetize FastAPI / Starlette / MCP HTTP routes in ~2 lines |
64
+
65
+ ---
66
+
67
+ ## Architecture
68
+
69
+ ```text
70
+ ┌─────────────┐ AgentPayClient ┌──────────────────────────────┐
71
+ │ Agent A │ ──────────────────────► │ Agent B (FastAPI / MCP) │
72
+ │ (buyer) │ GET /premium-data │ │
73
+ │ │ ◄──── HTTP 402 ──────── │ @paywall(price_usd=0.05) │
74
+ │ AgentWallet│ │ X-PAYMENT-ADDRESS / AMOUNT │
75
+ │ + policy │ ── pay(USDC) via x402 ─►│ │
76
+ │ │ ── retry + X-PAYMENT-PROOF ──► verify → 200 + data │
77
+ └─────────────┘ └──────────────────────────────┘
78
+
79
+ ├── providers/x402.py (Base / EVM)
80
+ ├── providers/solana.py (SOL / SPL USDC)
81
+ ├── providers/fiat.py (Payman / Stripe-like REST)
82
+ ├── guardrails.py (budgets + allowlist + HITL)
83
+ └── escrow.py (conditional release)
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Install
89
+
90
+ ```bash
91
+ pip install payagent
92
+ # or
93
+ uv add payagent
94
+
95
+ # FastAPI seller extras
96
+ pip install 'payagent[fastapi]'
97
+ ```
98
+
99
+ From GitHub:
100
+
101
+ ```bash
102
+ pip install "git+https://github.com/pomoq-dev/payagent.git@v0.1.0"
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Quickstart
108
+
109
+ ### 1) Buyer — auto-pay on HTTP 402
110
+
111
+ ```python
112
+ import asyncio
113
+ from payagent import AgentPayClient, AgentWallet, SpendingPolicy
114
+
115
+ async def main():
116
+ wallet = AgentWallet.mock(
117
+ policy=SpendingPolicy(max_per_tx=0.50, daily_limit=10.0, monthly_limit=100.0),
118
+ balance=50.0,
119
+ )
120
+ async with AgentPayClient(wallet) as client:
121
+ resp = await client.get("https://api.seller.example/premium-data")
122
+ print(resp.status_code, resp.json())
123
+ print("settled via", client.last_payment)
124
+
125
+ asyncio.run(main())
126
+ ```
127
+
128
+ ### 2) Seller — monetize with `@paywall`
129
+
130
+ ```python
131
+ from fastapi import FastAPI, Request
132
+ from payagent import paywall
133
+
134
+ app = FastAPI()
135
+
136
+ @app.get("/premium-data")
137
+ @paywall(price_usd=0.05, recipient_address="0xYourAddress", currency="USDC")
138
+ async def premium(request: Request):
139
+ return {"data": "secret sauce"}
140
+ ```
141
+
142
+ ### 3) Guardrails
143
+
144
+ ```python
145
+ from payagent import AgentWallet, SpendingPolicy
146
+
147
+ policy = SpendingPolicy(
148
+ max_per_tx=0.50,
149
+ daily_limit=10.0,
150
+ monthly_limit=100.0,
151
+ allowlist_domains=["api.partner.com", "mcp.internal"],
152
+ require_human_approval_above=2.0,
153
+ )
154
+ wallet = AgentWallet.mock(policy=policy)
155
+ ```
156
+
157
+ ### 4) Escrow
158
+
159
+ ```python
160
+ from payagent import AgentWallet, EscrowSession, SpendingPolicy
161
+
162
+ wallet = AgentWallet.mock(policy=SpendingPolicy(max_per_tx=5.0, daily_limit=50.0))
163
+ escrow = EscrowSession(wallet, validator_fn=lambda r: r.get("ok") is True)
164
+
165
+ result = await escrow.run(
166
+ job=lambda: {"ok": True, "answer": 42},
167
+ amount=0.25,
168
+ currency="USDC",
169
+ recipient="0xProvider",
170
+ )
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Feature support matrix
176
+
177
+ | Rail | Module | Currencies | Live chain | Mock (tests) |
178
+ |------|--------|------------|------------|--------------|
179
+ | **x402 / Base / EVM** | `X402Provider` | USDC, ETH, WETH, BASE-USDC | optional RPC + key | default `mock=True` |
180
+ | **Solana** | `SolanaProvider` | SOL, USDC, SOL-USDC | optional RPC + key | default `mock=True` |
181
+ | **Fiat** | `FiatProvider` | USD, EUR, GBP, FIAT | Payman-style REST | default `mock=True` |
182
+ | **HTTP 402 client** | `AgentPayClient` | via wallet routing | httpx | `respx` in tests |
183
+ | **Paywall** | `@paywall` | any string currency | proof verify | mock verifier |
184
+ | **Escrow** | `EscrowSession` | any wallet currency | same rails | full unit coverage |
185
+
186
+ ---
187
+
188
+ ## Project layout
189
+
190
+ ```text
191
+ src/payagent/
192
+ client.py # 402 interceptor client
193
+ wallet.py # multi-provider AgentWallet
194
+ guardrails.py # SpendingPolicy + ledger
195
+ escrow.py # conditional payments
196
+ paywall.py # @paywall decorator
197
+ exceptions.py
198
+ providers/
199
+ base.py x402.py solana.py fiat.py
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Configuration
205
+
206
+ Copy `.env.example` → `.env` (never commit secrets):
207
+
208
+ ```bash
209
+ SOLANA_PRIVATE_KEY=...
210
+ BASE_PRIVATE_KEY=0x...
211
+ PAYMAN_API_KEY=...
212
+ PAYAGENT_DAILY_LIMIT=10.0
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Develop
218
+
219
+ ```bash
220
+ uv venv -p 3.13 .venv && source .venv/bin/activate
221
+ uv pip install -e ".[dev]"
222
+
223
+ pytest -q
224
+ mypy src/payagent
225
+ python -m build
226
+ ```
227
+
228
+ ```bash
229
+ python examples/agent_buyer.py
230
+ python examples/escrow_job.py
231
+ # uvicorn examples.agent_seller_fastapi:app --port 8000
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Publish (PyPI)
237
+
238
+ 1. Create public repo `payagent` under `pomoq-dev`.
239
+ 2. PyPI Trusted Publisher or secret `PYPI_API_TOKEN`.
240
+ 3. GitHub Release `v0.1.0` → Actions uploads the wheel.
241
+
242
+ Local: `PYPI_TOKEN=pypi-… ./scripts/publish.sh`
243
+
244
+ ---
245
+
246
+ ## License
247
+
248
+ MIT © poqdev
@@ -0,0 +1,203 @@
1
+ # payagent
2
+
3
+ **Universal Payment & Monetization Engine for AI Agents**
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/payagent.svg)](https://pypi.org/project/payagent/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![CI](https://img.shields.io/github/actions/workflow/status/pomoq-dev/payagent/test.yml?branch=main&label=CI)](https://github.com/pomoq-dev/payagent/actions)
8
+ [![Python](https://img.shields.io/pypi/pyversions/payagent.svg)](https://pypi.org/project/payagent/)
9
+
10
+ Enable AI agents to **pay each other** for APIs, data, and compute — and let developers **monetize MCP tools / endpoints** with a one-line decorator.
11
+
12
+ | Capability | What you get |
13
+ |------------|----------------|
14
+ | **HTTP 402** | Client auto-detects `Payment Required`, settles, retries with proof |
15
+ | **Multi-rail wallet** | x402 (Base/EVM), Solana SOL/USDC, fiat adapter (Payman-style) |
16
+ | **Guardrails** | Per-tx / daily / monthly limits, domain allowlist, HITL threshold |
17
+ | **Escrow** | Lock → job → validate → release or refund |
18
+ | **`@paywall`** | Monetize FastAPI / Starlette / MCP HTTP routes in ~2 lines |
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ ```text
25
+ ┌─────────────┐ AgentPayClient ┌──────────────────────────────┐
26
+ │ Agent A │ ──────────────────────► │ Agent B (FastAPI / MCP) │
27
+ │ (buyer) │ GET /premium-data │ │
28
+ │ │ ◄──── HTTP 402 ──────── │ @paywall(price_usd=0.05) │
29
+ │ AgentWallet│ │ X-PAYMENT-ADDRESS / AMOUNT │
30
+ │ + policy │ ── pay(USDC) via x402 ─►│ │
31
+ │ │ ── retry + X-PAYMENT-PROOF ──► verify → 200 + data │
32
+ └─────────────┘ └──────────────────────────────┘
33
+
34
+ ├── providers/x402.py (Base / EVM)
35
+ ├── providers/solana.py (SOL / SPL USDC)
36
+ ├── providers/fiat.py (Payman / Stripe-like REST)
37
+ ├── guardrails.py (budgets + allowlist + HITL)
38
+ └── escrow.py (conditional release)
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install payagent
47
+ # or
48
+ uv add payagent
49
+
50
+ # FastAPI seller extras
51
+ pip install 'payagent[fastapi]'
52
+ ```
53
+
54
+ From GitHub:
55
+
56
+ ```bash
57
+ pip install "git+https://github.com/pomoq-dev/payagent.git@v0.1.0"
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Quickstart
63
+
64
+ ### 1) Buyer — auto-pay on HTTP 402
65
+
66
+ ```python
67
+ import asyncio
68
+ from payagent import AgentPayClient, AgentWallet, SpendingPolicy
69
+
70
+ async def main():
71
+ wallet = AgentWallet.mock(
72
+ policy=SpendingPolicy(max_per_tx=0.50, daily_limit=10.0, monthly_limit=100.0),
73
+ balance=50.0,
74
+ )
75
+ async with AgentPayClient(wallet) as client:
76
+ resp = await client.get("https://api.seller.example/premium-data")
77
+ print(resp.status_code, resp.json())
78
+ print("settled via", client.last_payment)
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ ### 2) Seller — monetize with `@paywall`
84
+
85
+ ```python
86
+ from fastapi import FastAPI, Request
87
+ from payagent import paywall
88
+
89
+ app = FastAPI()
90
+
91
+ @app.get("/premium-data")
92
+ @paywall(price_usd=0.05, recipient_address="0xYourAddress", currency="USDC")
93
+ async def premium(request: Request):
94
+ return {"data": "secret sauce"}
95
+ ```
96
+
97
+ ### 3) Guardrails
98
+
99
+ ```python
100
+ from payagent import AgentWallet, SpendingPolicy
101
+
102
+ policy = SpendingPolicy(
103
+ max_per_tx=0.50,
104
+ daily_limit=10.0,
105
+ monthly_limit=100.0,
106
+ allowlist_domains=["api.partner.com", "mcp.internal"],
107
+ require_human_approval_above=2.0,
108
+ )
109
+ wallet = AgentWallet.mock(policy=policy)
110
+ ```
111
+
112
+ ### 4) Escrow
113
+
114
+ ```python
115
+ from payagent import AgentWallet, EscrowSession, SpendingPolicy
116
+
117
+ wallet = AgentWallet.mock(policy=SpendingPolicy(max_per_tx=5.0, daily_limit=50.0))
118
+ escrow = EscrowSession(wallet, validator_fn=lambda r: r.get("ok") is True)
119
+
120
+ result = await escrow.run(
121
+ job=lambda: {"ok": True, "answer": 42},
122
+ amount=0.25,
123
+ currency="USDC",
124
+ recipient="0xProvider",
125
+ )
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Feature support matrix
131
+
132
+ | Rail | Module | Currencies | Live chain | Mock (tests) |
133
+ |------|--------|------------|------------|--------------|
134
+ | **x402 / Base / EVM** | `X402Provider` | USDC, ETH, WETH, BASE-USDC | optional RPC + key | default `mock=True` |
135
+ | **Solana** | `SolanaProvider` | SOL, USDC, SOL-USDC | optional RPC + key | default `mock=True` |
136
+ | **Fiat** | `FiatProvider` | USD, EUR, GBP, FIAT | Payman-style REST | default `mock=True` |
137
+ | **HTTP 402 client** | `AgentPayClient` | via wallet routing | httpx | `respx` in tests |
138
+ | **Paywall** | `@paywall` | any string currency | proof verify | mock verifier |
139
+ | **Escrow** | `EscrowSession` | any wallet currency | same rails | full unit coverage |
140
+
141
+ ---
142
+
143
+ ## Project layout
144
+
145
+ ```text
146
+ src/payagent/
147
+ client.py # 402 interceptor client
148
+ wallet.py # multi-provider AgentWallet
149
+ guardrails.py # SpendingPolicy + ledger
150
+ escrow.py # conditional payments
151
+ paywall.py # @paywall decorator
152
+ exceptions.py
153
+ providers/
154
+ base.py x402.py solana.py fiat.py
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Configuration
160
+
161
+ Copy `.env.example` → `.env` (never commit secrets):
162
+
163
+ ```bash
164
+ SOLANA_PRIVATE_KEY=...
165
+ BASE_PRIVATE_KEY=0x...
166
+ PAYMAN_API_KEY=...
167
+ PAYAGENT_DAILY_LIMIT=10.0
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Develop
173
+
174
+ ```bash
175
+ uv venv -p 3.13 .venv && source .venv/bin/activate
176
+ uv pip install -e ".[dev]"
177
+
178
+ pytest -q
179
+ mypy src/payagent
180
+ python -m build
181
+ ```
182
+
183
+ ```bash
184
+ python examples/agent_buyer.py
185
+ python examples/escrow_job.py
186
+ # uvicorn examples.agent_seller_fastapi:app --port 8000
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Publish (PyPI)
192
+
193
+ 1. Create public repo `payagent` under `pomoq-dev`.
194
+ 2. PyPI Trusted Publisher or secret `PYPI_API_TOKEN`.
195
+ 3. GitHub Release `v0.1.0` → Actions uploads the wheel.
196
+
197
+ Local: `PYPI_TOKEN=pypi-… ./scripts/publish.sh`
198
+
199
+ ---
200
+
201
+ ## License
202
+
203
+ MIT © poqdev
@@ -0,0 +1,47 @@
1
+ """Buyer agent: call a paid API with automatic HTTP 402 settlement.
2
+
3
+ Run (mock — no real keys needed)::
4
+
5
+ python examples/agent_buyer.py
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+
12
+ from payagent import AgentPayClient, AgentWallet, SpendingPolicy
13
+
14
+
15
+ async def main() -> None:
16
+ policy = SpendingPolicy(
17
+ max_per_tx=0.50,
18
+ daily_limit=10.0,
19
+ monthly_limit=100.0,
20
+ allowlist_domains=[], # empty = allow all when allow_all_when_empty_allowlist
21
+ require_human_approval_above=5.0,
22
+ )
23
+ wallet = AgentWallet.mock(policy=policy, balance=50.0)
24
+
25
+ # Point at your seller (see agent_seller_fastapi.py). Uses mock wallet locally.
26
+ # For a dry demo without a live server we just show wallet payment:
27
+ result = await wallet.pay(
28
+ amount=0.05,
29
+ currency="USDC",
30
+ recipient="0xSellerDemoAddress",
31
+ domain="api.example.com",
32
+ memo="demo-purchase",
33
+ )
34
+ print("Paid:", result.tx_hash, result.amount, result.currency, "via", result.provider)
35
+ print("Proof header:", result.as_proof_header())
36
+ print("Daily remaining:", wallet.enforcer.remaining_daily())
37
+
38
+ # Real 402 flow (uncomment when seller is running):
39
+ # async with AgentPayClient(wallet) as client:
40
+ # resp = await client.get("http://127.0.0.1:8000/premium-data")
41
+ # print(resp.status_code, resp.json())
42
+
43
+ await wallet.close()
44
+
45
+
46
+ if __name__ == "__main__":
47
+ asyncio.run(main())
@@ -0,0 +1,56 @@
1
+ """Seller: monetize a FastAPI endpoint with @paywall (2 lines).
2
+
3
+ Install extras::
4
+
5
+ pip install 'payagent[fastapi]' uvicorn
6
+
7
+ Run::
8
+
9
+ uvicorn examples.agent_seller_fastapi:app --reload --port 8000
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ from payagent import PaymentVerifier, X402Provider, paywall
17
+
18
+ try:
19
+ from fastapi import FastAPI, Request
20
+ except ImportError as exc: # pragma: no cover
21
+ raise SystemExit("Install fastapi: pip install 'payagent[fastapi]' uvicorn") from exc
22
+
23
+ app = FastAPI(title="payagent seller demo")
24
+
25
+ # Mock verifier — swap for AgentWallet / live provider in production
26
+ _verifier = PaymentVerifier(providers=[X402Provider(mock=True)], mock_accept_all=False)
27
+ # Accept any well-formed proof in this demo (see PaymentVerifier fallback),
28
+ # or set mock_accept_all=True for the simplest local loop.
29
+
30
+
31
+ @app.get("/health")
32
+ async def health() -> dict[str, str]:
33
+ return {"status": "ok"}
34
+
35
+
36
+ @app.get("/premium-data")
37
+ @paywall(
38
+ price_usd=0.05,
39
+ recipient_address="0xSellerDemoAddress",
40
+ currency="USDC",
41
+ network="base",
42
+ verifier=PaymentVerifier(mock_accept_all=True), # demo-only
43
+ )
44
+ async def premium_data(request: Request) -> dict[str, Any]:
45
+ return {
46
+ "data": "premium market signal",
47
+ "paid": True,
48
+ "client": request.client.host if request.client else None,
49
+ }
50
+
51
+
52
+ @app.get("/mcp-tool-result")
53
+ @paywall(price_usd=0.01, recipient_address="0xSellerDemoAddress")
54
+ async def mcp_style_tool() -> dict[str, str]:
55
+ """Same decorator works for MCP/HTTP tool endpoints."""
56
+ return {"tool": "search", "result": "top hit"}
@@ -0,0 +1,67 @@
1
+ """Escrow: lock funds → run job → validate → release or refund.
2
+
3
+ Run::
4
+
5
+ python examples/escrow_job.py
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ from typing import Any
12
+
13
+ from payagent import AgentWallet, EscrowSession, EscrowValidationError, SpendingPolicy
14
+
15
+
16
+ async def expensive_compute() -> dict[str, Any]:
17
+ # Pretend we called a remote model / data API
18
+ return {"status": "ok", "summary": "agent completed task", "tokens": 1200}
19
+
20
+
21
+ def validate_result(result: dict[str, Any]) -> bool:
22
+ return result.get("status") == "ok" and "summary" in result
23
+
24
+
25
+ async def main() -> None:
26
+ wallet = AgentWallet.mock(
27
+ policy=SpendingPolicy(max_per_tx=2.0, daily_limit=20.0, monthly_limit=200.0),
28
+ balance=100.0,
29
+ )
30
+ escrow: EscrowSession[dict[str, Any]] = EscrowSession(wallet, validator_fn=validate_result)
31
+
32
+ try:
33
+ out = await escrow.run(
34
+ job=expensive_compute,
35
+ amount=0.25,
36
+ currency="USDC",
37
+ recipient="0xComputeProvider",
38
+ domain="compute.example.com",
39
+ memo="summarize-doc",
40
+ )
41
+ print("Job OK:", out)
42
+ print("Escrow state:", escrow.record.state if escrow.record else None)
43
+ if escrow.record and escrow.record.release_result:
44
+ print("Released tx:", escrow.record.release_result.tx_hash)
45
+ except EscrowValidationError as exc:
46
+ print("Validation failed, refunded:", exc)
47
+
48
+ # Failure path demo
49
+ bad: EscrowSession[dict[str, Any]] = EscrowSession(
50
+ wallet,
51
+ validator_fn=lambda r: r.get("status") == "ok",
52
+ )
53
+ try:
54
+ await bad.run(
55
+ job=lambda: {"status": "error"},
56
+ amount=0.1,
57
+ currency="USDC",
58
+ recipient="0xComputeProvider",
59
+ )
60
+ except EscrowValidationError:
61
+ print("Bad job refunded, state:", bad.record.state if bad.record else None)
62
+
63
+ await wallet.close()
64
+
65
+
66
+ if __name__ == "__main__":
67
+ asyncio.run(main())