modexiaagentpay 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.
- modexiaagentpay-0.1.0/LICENSE +21 -0
- modexiaagentpay-0.1.0/PKG-INFO +97 -0
- modexiaagentpay-0.1.0/README.md +66 -0
- modexiaagentpay-0.1.0/pyproject.toml +41 -0
- modexiaagentpay-0.1.0/setup.cfg +4 -0
- modexiaagentpay-0.1.0/src/modexia/__init__.py +8 -0
- modexiaagentpay-0.1.0/src/modexia/client.py +240 -0
- modexiaagentpay-0.1.0/src/modexiaagentpay.egg-info/PKG-INFO +97 -0
- modexiaagentpay-0.1.0/src/modexiaagentpay.egg-info/SOURCES.txt +10 -0
- modexiaagentpay-0.1.0/src/modexiaagentpay.egg-info/dependency_links.txt +1 -0
- modexiaagentpay-0.1.0/src/modexiaagentpay.egg-info/requires.txt +6 -0
- modexiaagentpay-0.1.0/src/modexiaagentpay.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Modaniels
|
|
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,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modexiaagentpay
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modexia AgentPay — Python SDK for agent wallets & payments (USDC)
|
|
5
|
+
Author-email: Modaniels <modaniels@modexia.software>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Modexia/ModexiaAgentpay
|
|
8
|
+
Project-URL: Repository, https://github.com/Modexia/ModexiaAgentpay
|
|
9
|
+
Project-URL: Documentation, https://github.com/Modexia/ModexiaAgentpay#readme
|
|
10
|
+
Project-URL: Changelog, https://github.com/Modexia/ModexiaAgentpay/releases
|
|
11
|
+
Keywords: modexia,agentpay,sdk,payments,usdc
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: requests>=2.31.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: build; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Modexia Python SDK
|
|
33
|
+
|
|
34
|
+
Lightweight Python client for interacting with the Modexia AgentPay API.
|
|
35
|
+
|
|
36
|
+
Features
|
|
37
|
+
- Simple programmatic access to agent wallets and payments
|
|
38
|
+
- Reliable retry/backoff for HTTP calls
|
|
39
|
+
- Small surface area: `ModexiaClient` with `transfer` + `retrieve_balance` helpers
|
|
40
|
+
|
|
41
|
+
Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# install locally (editable)
|
|
45
|
+
pip install -e packages/SDKs/pythonSdk
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Quick start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from modexia import ModexiaClient
|
|
52
|
+
|
|
53
|
+
client = ModexiaClient(api_key="mx_test_...")
|
|
54
|
+
|
|
55
|
+
# read balance
|
|
56
|
+
print(client.retrieve_balance())
|
|
57
|
+
|
|
58
|
+
# make a transfer (wait=True polls for on-chain confirmation)
|
|
59
|
+
receipt = client.transfer(recipient="0xabc...", amount=5.0, wait=True)
|
|
60
|
+
print(receipt)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
API (short)
|
|
64
|
+
- ModexiaClient(api_key: str, timeout: int = 15)
|
|
65
|
+
- retrieve_balance() -> str
|
|
66
|
+
- transfer(recipient: str, amount: float, idempotency_key: Optional[str] = None, wait: bool = True) -> dict
|
|
67
|
+
- smart_fetch(url, ...) -> requests.Response
|
|
68
|
+
|
|
69
|
+
Errors / Exceptions
|
|
70
|
+
- ModexiaAuthError — authentication problems
|
|
71
|
+
- ModexiaPaymentError — payment/server errors
|
|
72
|
+
- ModexiaNetworkError — network/connection failures
|
|
73
|
+
|
|
74
|
+
Testing
|
|
75
|
+
|
|
76
|
+
Run the unit tests with pytest from the repository root:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pytest -q packages/SDKs/pythonSdk
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Contributing
|
|
83
|
+
|
|
84
|
+
Open a PR against the `develop` branch. Keep API names stable — this package uses
|
|
85
|
+
`ModexiaClient` and `transfer(...)` as the canonical surface.
|
|
86
|
+
|
|
87
|
+
Install (PyPI)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install modexiaagentpay
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If you prefer to try the local copy while iterating:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install -e packages/SDKs/pythonSdk
|
|
97
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Modexia Python SDK
|
|
2
|
+
|
|
3
|
+
Lightweight Python client for interacting with the Modexia AgentPay API.
|
|
4
|
+
|
|
5
|
+
Features
|
|
6
|
+
- Simple programmatic access to agent wallets and payments
|
|
7
|
+
- Reliable retry/backoff for HTTP calls
|
|
8
|
+
- Small surface area: `ModexiaClient` with `transfer` + `retrieve_balance` helpers
|
|
9
|
+
|
|
10
|
+
Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# install locally (editable)
|
|
14
|
+
pip install -e packages/SDKs/pythonSdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Quick start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from modexia import ModexiaClient
|
|
21
|
+
|
|
22
|
+
client = ModexiaClient(api_key="mx_test_...")
|
|
23
|
+
|
|
24
|
+
# read balance
|
|
25
|
+
print(client.retrieve_balance())
|
|
26
|
+
|
|
27
|
+
# make a transfer (wait=True polls for on-chain confirmation)
|
|
28
|
+
receipt = client.transfer(recipient="0xabc...", amount=5.0, wait=True)
|
|
29
|
+
print(receipt)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
API (short)
|
|
33
|
+
- ModexiaClient(api_key: str, timeout: int = 15)
|
|
34
|
+
- retrieve_balance() -> str
|
|
35
|
+
- transfer(recipient: str, amount: float, idempotency_key: Optional[str] = None, wait: bool = True) -> dict
|
|
36
|
+
- smart_fetch(url, ...) -> requests.Response
|
|
37
|
+
|
|
38
|
+
Errors / Exceptions
|
|
39
|
+
- ModexiaAuthError — authentication problems
|
|
40
|
+
- ModexiaPaymentError — payment/server errors
|
|
41
|
+
- ModexiaNetworkError — network/connection failures
|
|
42
|
+
|
|
43
|
+
Testing
|
|
44
|
+
|
|
45
|
+
Run the unit tests with pytest from the repository root:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pytest -q packages/SDKs/pythonSdk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Contributing
|
|
52
|
+
|
|
53
|
+
Open a PR against the `develop` branch. Keep API names stable — this package uses
|
|
54
|
+
`ModexiaClient` and `transfer(...)` as the canonical surface.
|
|
55
|
+
|
|
56
|
+
Install (PyPI)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install modexiaagentpay
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If you prefer to try the local copy while iterating:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install -e packages/SDKs/pythonSdk
|
|
66
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3", "wheel", "build"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modexiaagentpay"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Modexia AgentPay — Python SDK for agent wallets & payments (USDC)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
keywords = ["modexia", "agentpay", "sdk", "payments", "usdc"]
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "Modaniels", email = "modaniels@modexia.software" }
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
]
|
|
29
|
+
dependencies = ["requests>=2.31.0"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/Modexia/ModexiaAgentpay"
|
|
33
|
+
Repository = "https://github.com/Modexia/ModexiaAgentpay"
|
|
34
|
+
Documentation = "https://github.com/Modexia/ModexiaAgentpay#readme"
|
|
35
|
+
Changelog = "https://github.com/Modexia/ModexiaAgentpay/releases"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = ["pytest>=7.0", "build", "twine"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Modexia Python SDK client.
|
|
2
|
+
|
|
3
|
+
This module provides `ModexiaClient` — a small, high‑level HTTP client for
|
|
4
|
+
interacting with the Modexia AgentPay HTTP API. It implements reliable
|
|
5
|
+
request retrying, basic authentication via `x-modexia-key`, convenience
|
|
6
|
+
helpers for reading balance and creating payments, and a `smart_fetch`
|
|
7
|
+
helper that can auto-negotiate paywalled resources.
|
|
8
|
+
|
|
9
|
+
Public surface
|
|
10
|
+
- ModexiaClient: main client class
|
|
11
|
+
- ModexiaAuthError / ModexiaPaymentError / ModexiaNetworkError: exceptions
|
|
12
|
+
|
|
13
|
+
The client is intentionally lightweight and synchronous so it is easy to use
|
|
14
|
+
from scripts, server-side code, and tests.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import requests
|
|
18
|
+
import uuid
|
|
19
|
+
import re
|
|
20
|
+
import os
|
|
21
|
+
import time
|
|
22
|
+
import logging
|
|
23
|
+
from typing import Optional, Dict, Any
|
|
24
|
+
from requests.adapters import HTTPAdapter
|
|
25
|
+
from urllib3.util.retry import Retry
|
|
26
|
+
|
|
27
|
+
# --- EXCEPTIONS ---
|
|
28
|
+
class ModexiaError(Exception): pass
|
|
29
|
+
class ModexiaAuthError(ModexiaError): pass
|
|
30
|
+
class ModexiaPaymentError(ModexiaError): pass
|
|
31
|
+
class ModexiaNetworkError(ModexiaError): pass
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger("modexia")
|
|
34
|
+
logger.addHandler(logging.NullHandler())
|
|
35
|
+
|
|
36
|
+
class ModexiaClient:
|
|
37
|
+
"""Official Modexia Python client.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
client = ModexiaClient(api_key="mx_test_...")
|
|
41
|
+
client.retrieve_balance()
|
|
42
|
+
client.transfer(recipient, amount=1.0)
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
api_key: API key used for `x-modexia-key` header.
|
|
46
|
+
base_url: resolved base URL (live/test/local) for requests.
|
|
47
|
+
session: configured `requests.Session` with retry logic.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
VERSION = "0.1.0"
|
|
51
|
+
DEFAULT_TIMEOUT = 15
|
|
52
|
+
|
|
53
|
+
URLS = {
|
|
54
|
+
"live": "https://api.modexia.software",
|
|
55
|
+
"test": "https://sandbox.modexia.software",
|
|
56
|
+
"local": "http://localhost:3000"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def __init__(self, api_key: str, timeout: int = DEFAULT_TIMEOUT):
|
|
60
|
+
"""Create a new `ModexiaClient`.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
api_key: Modexia API key (mx_test_... or mx_live_...)
|
|
64
|
+
timeout: per-request timeout in seconds.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ModexiaAuthError: if initial handshake (/user/me) fails.
|
|
68
|
+
ModexiaNetworkError: on network-level failures.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
self.api_key = api_key
|
|
72
|
+
self.timeout = timeout
|
|
73
|
+
|
|
74
|
+
# determine environment from key (simple heuristic)
|
|
75
|
+
if api_key.startswith("mx_live_"):
|
|
76
|
+
self.base_url = self.URLS["live"]
|
|
77
|
+
else:
|
|
78
|
+
self.base_url = self.URLS["local"]
|
|
79
|
+
|
|
80
|
+
# HTTP session w/ sensible headers and retry policy
|
|
81
|
+
self.session = requests.Session()
|
|
82
|
+
self.session.headers.update({
|
|
83
|
+
"x-modexia-key": self.api_key,
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"User-Agent": f"Modexia-Python/{self.VERSION}"
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
retries = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504])
|
|
89
|
+
self.session.mount('http://', HTTPAdapter(max_retries=retries))
|
|
90
|
+
self.session.mount('https://', HTTPAdapter(max_retries=retries))
|
|
91
|
+
|
|
92
|
+
# Handshake: validate API key and cache identity information
|
|
93
|
+
self.identity = self._validate_session()
|
|
94
|
+
|
|
95
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
96
|
+
"""Perform an HTTP request against the Modexia API and return JSON.
|
|
97
|
+
|
|
98
|
+
This is a thin wrapper around `requests.Session.request` which:
|
|
99
|
+
- applies the configured timeout and session headers
|
|
100
|
+
- raises `ModexiaAuthError` for 401/403
|
|
101
|
+
- raises `ModexiaPaymentError` for 4xx/5xx (except 402 paywall)
|
|
102
|
+
- raises `ModexiaNetworkError` for network errors
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Parsed JSON response as a dict (empty dict for no-content).
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
ModexiaAuthError, ModexiaPaymentError, ModexiaNetworkError
|
|
109
|
+
"""
|
|
110
|
+
url = f"{self.base_url}{endpoint}"
|
|
111
|
+
try:
|
|
112
|
+
response = self.session.request(method, url, timeout=self.timeout, **kwargs)
|
|
113
|
+
|
|
114
|
+
if response.status_code in [401, 403]:
|
|
115
|
+
raise ModexiaAuthError(f"Unauthorized: {response.text}")
|
|
116
|
+
|
|
117
|
+
if response.status_code >= 400 and response.status_code != 402:
|
|
118
|
+
try: err = response.json().get('error', response.text)
|
|
119
|
+
except: err = response.text
|
|
120
|
+
raise ModexiaPaymentError(err)
|
|
121
|
+
|
|
122
|
+
# Return the dictionary, not the response object
|
|
123
|
+
return response.json() if response.content else {}
|
|
124
|
+
|
|
125
|
+
except requests.exceptions.RequestException as e:
|
|
126
|
+
raise ModexiaNetworkError(f"Connection failed: {str(e)}")
|
|
127
|
+
|
|
128
|
+
def _validate_session(self) -> Dict[str, Any]:
|
|
129
|
+
"""Validate API key by calling `GET /api/v1/user/me`.
|
|
130
|
+
|
|
131
|
+
Returns the parsed `data` payload from the server and caches it on
|
|
132
|
+
the client instance as `identity`.
|
|
133
|
+
"""
|
|
134
|
+
res = self._request("GET", "/api/v1/user/me")
|
|
135
|
+
# Ensure we extract the 'data' wrapper if your server uses it
|
|
136
|
+
data = res.get('data', res)
|
|
137
|
+
logger.info(f"Connected to Modexia as: {data.get('username')}")
|
|
138
|
+
return data
|
|
139
|
+
|
|
140
|
+
def retrieve_balance(self) -> str:
|
|
141
|
+
"""Return the current wallet balance (as a decimal string).
|
|
142
|
+
|
|
143
|
+
The server exposes balance via `/api/v1/user/me`; this helper returns
|
|
144
|
+
the `balance` field or string `'0'` when missing.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
data = self._validate_session()
|
|
148
|
+
return data.get("balance", "0")
|
|
149
|
+
|
|
150
|
+
def transfer(self, recipient: str, amount: float, idempotency_key: Optional[str] = None, wait: bool = True) -> Dict[str, Any]:
|
|
151
|
+
"""Create a payment from the authenticated agent to `recipient`.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
recipient: provider/recipient blockchain address (string).
|
|
155
|
+
amount: USD token amount (human decimal, e.g. 1.50).
|
|
156
|
+
idempotency_key: optional idempotency token; autogenerated when
|
|
157
|
+
not provided.
|
|
158
|
+
wait: if True, poll the transaction status until it completes or
|
|
159
|
+
times out and return the final status dict.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Server response (or final status dict when `wait=True`).
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
ModexiaPaymentError on server-declared failures.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
ikey = idempotency_key or str(uuid.uuid4())
|
|
169
|
+
payload = {"providerAddress": recipient, "amount": str(amount), "idempotencyKey": ikey}
|
|
170
|
+
|
|
171
|
+
data = self._request("POST", "/api/v1/agent/pay", json=payload)
|
|
172
|
+
|
|
173
|
+
if wait and data.get("success"):
|
|
174
|
+
return self._poll_status(data.get("txId"))
|
|
175
|
+
|
|
176
|
+
return data
|
|
177
|
+
|
|
178
|
+
def _poll_status(self, tx_id: str) -> Dict[str, Any]:
|
|
179
|
+
"""Poll the server for transaction status until timeout.
|
|
180
|
+
|
|
181
|
+
The method repeatedly queries `/api/v1/agent/transaction/{tx_id}` and
|
|
182
|
+
returns once the server reports a completion or raises when the
|
|
183
|
+
transaction fails.
|
|
184
|
+
|
|
185
|
+
Returns a short summary dict on success, e.g. `{"success": True,
|
|
186
|
+
"status": "COMPLETE", "txHash": "0x..."}`.
|
|
187
|
+
"""
|
|
188
|
+
start = time.time()
|
|
189
|
+
while (time.time() - start) < 30:
|
|
190
|
+
data = self._request("GET", f"/api/v1/agent/transaction/{tx_id}")
|
|
191
|
+
|
|
192
|
+
state = data.get("state", "").upper()
|
|
193
|
+
# Be flexible with the string
|
|
194
|
+
if state in ["COMPLETE", "COMPLETED"]:
|
|
195
|
+
return {"success": True, "txId": tx_id, "status": "COMPLETE", "txHash": data.get("txHash")}
|
|
196
|
+
|
|
197
|
+
if state == "FAILED":
|
|
198
|
+
raise ModexiaPaymentError(f"Transfer Failed: {data.get('errorReason')}")
|
|
199
|
+
|
|
200
|
+
time.sleep(2)
|
|
201
|
+
return {"success": True, "status": "PENDING", "txId": tx_id}
|
|
202
|
+
|
|
203
|
+
def smart_fetch(self, url: str, params: Optional[Dict] = None, headers: Optional[Dict] = None) -> requests.Response:
|
|
204
|
+
"""Fetch an external resource and auto-pay 402 paywalls.
|
|
205
|
+
|
|
206
|
+
Performs a plain GET; if the remote origin responds with 402 and
|
|
207
|
+
a `WWW-Authenticate` header describing an `amount` and `destination`,
|
|
208
|
+
the client will attempt to pay that amount via `transfer()` and retry
|
|
209
|
+
the request with a payment proof header.
|
|
210
|
+
|
|
211
|
+
Returns the final `requests.Response`.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
if headers is None: headers = {}
|
|
215
|
+
response = requests.get(url, params=params, headers=headers, timeout=self.timeout)
|
|
216
|
+
|
|
217
|
+
if response.status_code == 402:
|
|
218
|
+
receipt = self._negotiate_paywall(response)
|
|
219
|
+
if receipt:
|
|
220
|
+
headers['Authorization'] = f"L402 {receipt.get('txId')}"
|
|
221
|
+
headers['X-Payment-Proof'] = receipt.get('txId')
|
|
222
|
+
return requests.get(url, params=params, headers=headers, timeout=self.timeout)
|
|
223
|
+
|
|
224
|
+
return response
|
|
225
|
+
|
|
226
|
+
def _negotiate_paywall(self, response_obj) -> Optional[Dict]:
|
|
227
|
+
"""Parse a 402 paywall `WWW-Authenticate` header and pay it.
|
|
228
|
+
|
|
229
|
+
Returns the receipt dict returned by `transfer()` when payment
|
|
230
|
+
succeeded, otherwise `None`.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
auth_header = response_obj.headers.get("WWW-Authenticate", "")
|
|
234
|
+
amt = re.search(r'amount="([^"]+)"', auth_header)
|
|
235
|
+
dst = re.search(r'destination="([^"]+)"', auth_header)
|
|
236
|
+
|
|
237
|
+
if amt and dst:
|
|
238
|
+
return self.transfer(dst.group(1), float(amt.group(1)))
|
|
239
|
+
|
|
240
|
+
return None
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modexiaagentpay
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modexia AgentPay — Python SDK for agent wallets & payments (USDC)
|
|
5
|
+
Author-email: Modaniels <modaniels@modexia.software>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Modexia/ModexiaAgentpay
|
|
8
|
+
Project-URL: Repository, https://github.com/Modexia/ModexiaAgentpay
|
|
9
|
+
Project-URL: Documentation, https://github.com/Modexia/ModexiaAgentpay#readme
|
|
10
|
+
Project-URL: Changelog, https://github.com/Modexia/ModexiaAgentpay/releases
|
|
11
|
+
Keywords: modexia,agentpay,sdk,payments,usdc
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: requests>=2.31.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: build; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Modexia Python SDK
|
|
33
|
+
|
|
34
|
+
Lightweight Python client for interacting with the Modexia AgentPay API.
|
|
35
|
+
|
|
36
|
+
Features
|
|
37
|
+
- Simple programmatic access to agent wallets and payments
|
|
38
|
+
- Reliable retry/backoff for HTTP calls
|
|
39
|
+
- Small surface area: `ModexiaClient` with `transfer` + `retrieve_balance` helpers
|
|
40
|
+
|
|
41
|
+
Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# install locally (editable)
|
|
45
|
+
pip install -e packages/SDKs/pythonSdk
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Quick start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from modexia import ModexiaClient
|
|
52
|
+
|
|
53
|
+
client = ModexiaClient(api_key="mx_test_...")
|
|
54
|
+
|
|
55
|
+
# read balance
|
|
56
|
+
print(client.retrieve_balance())
|
|
57
|
+
|
|
58
|
+
# make a transfer (wait=True polls for on-chain confirmation)
|
|
59
|
+
receipt = client.transfer(recipient="0xabc...", amount=5.0, wait=True)
|
|
60
|
+
print(receipt)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
API (short)
|
|
64
|
+
- ModexiaClient(api_key: str, timeout: int = 15)
|
|
65
|
+
- retrieve_balance() -> str
|
|
66
|
+
- transfer(recipient: str, amount: float, idempotency_key: Optional[str] = None, wait: bool = True) -> dict
|
|
67
|
+
- smart_fetch(url, ...) -> requests.Response
|
|
68
|
+
|
|
69
|
+
Errors / Exceptions
|
|
70
|
+
- ModexiaAuthError — authentication problems
|
|
71
|
+
- ModexiaPaymentError — payment/server errors
|
|
72
|
+
- ModexiaNetworkError — network/connection failures
|
|
73
|
+
|
|
74
|
+
Testing
|
|
75
|
+
|
|
76
|
+
Run the unit tests with pytest from the repository root:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pytest -q packages/SDKs/pythonSdk
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Contributing
|
|
83
|
+
|
|
84
|
+
Open a PR against the `develop` branch. Keep API names stable — this package uses
|
|
85
|
+
`ModexiaClient` and `transfer(...)` as the canonical surface.
|
|
86
|
+
|
|
87
|
+
Install (PyPI)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install modexiaagentpay
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If you prefer to try the local copy while iterating:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install -e packages/SDKs/pythonSdk
|
|
97
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/modexia/__init__.py
|
|
5
|
+
src/modexia/client.py
|
|
6
|
+
src/modexiaagentpay.egg-info/PKG-INFO
|
|
7
|
+
src/modexiaagentpay.egg-info/SOURCES.txt
|
|
8
|
+
src/modexiaagentpay.egg-info/dependency_links.txt
|
|
9
|
+
src/modexiaagentpay.egg-info/requires.txt
|
|
10
|
+
src/modexiaagentpay.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
modexia
|