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.
- payagent-0.1.0/.gitignore +27 -0
- payagent-0.1.0/LICENSE +21 -0
- payagent-0.1.0/PKG-INFO +248 -0
- payagent-0.1.0/README.md +203 -0
- payagent-0.1.0/examples/agent_buyer.py +47 -0
- payagent-0.1.0/examples/agent_seller_fastapi.py +56 -0
- payagent-0.1.0/examples/escrow_job.py +67 -0
- payagent-0.1.0/pyproject.toml +99 -0
- payagent-0.1.0/src/payagent/__init__.py +63 -0
- payagent-0.1.0/src/payagent/client.py +146 -0
- payagent-0.1.0/src/payagent/escrow.py +204 -0
- payagent-0.1.0/src/payagent/exceptions.py +43 -0
- payagent-0.1.0/src/payagent/guardrails.py +237 -0
- payagent-0.1.0/src/payagent/paywall.py +246 -0
- payagent-0.1.0/src/payagent/providers/__init__.py +16 -0
- payagent-0.1.0/src/payagent/providers/base.py +66 -0
- payagent-0.1.0/src/payagent/providers/fiat.py +178 -0
- payagent-0.1.0/src/payagent/providers/solana.py +147 -0
- payagent-0.1.0/src/payagent/providers/x402.py +180 -0
- payagent-0.1.0/src/payagent/py.typed +0 -0
- payagent-0.1.0/src/payagent/wallet.py +198 -0
- payagent-0.1.0/tests/test_client.py +113 -0
- payagent-0.1.0/tests/test_escrow.py +111 -0
- payagent-0.1.0/tests/test_guardrails.py +114 -0
- payagent-0.1.0/tests/test_paywall.py +103 -0
- payagent-0.1.0/tests/test_providers.py +63 -0
- payagent-0.1.0/tests/test_wallet.py +96 -0
|
@@ -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.
|
payagent-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/payagent/)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
[](https://github.com/pomoq-dev/payagent/actions)
|
|
53
|
+
[](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
|
payagent-0.1.0/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# payagent
|
|
2
|
+
|
|
3
|
+
**Universal Payment & Monetization Engine for AI Agents**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/payagent/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/pomoq-dev/payagent/actions)
|
|
8
|
+
[](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())
|