agentpay-python 0.1.0__py3-none-any.whl
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.
agentpay/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""AgentPay Python SDK — AI agent ödeme altyapısı."""
|
|
2
|
+
|
|
3
|
+
from .client import AgentPayClient
|
|
4
|
+
from .models import (
|
|
5
|
+
ClientInfo,
|
|
6
|
+
BalanceInfo,
|
|
7
|
+
PaywallResponse,
|
|
8
|
+
SpendResult,
|
|
9
|
+
TopupResponse,
|
|
10
|
+
RegisterResponse,
|
|
11
|
+
ForecastResult,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AgentPayClient",
|
|
16
|
+
"ClientInfo",
|
|
17
|
+
"BalanceInfo",
|
|
18
|
+
"PaywallResponse",
|
|
19
|
+
"SpendResult",
|
|
20
|
+
"TopupResponse",
|
|
21
|
+
"RegisterResponse",
|
|
22
|
+
"ForecastResult",
|
|
23
|
+
]
|
agentpay/client.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""AgentPay Python SDK istemci sınıfı."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import time
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from urllib.parse import urljoin
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from .models import (
|
|
12
|
+
BalanceInfo,
|
|
13
|
+
ClientInfo,
|
|
14
|
+
ForecastResult,
|
|
15
|
+
PaywallResponse,
|
|
16
|
+
RegisterResponse,
|
|
17
|
+
SpendResult,
|
|
18
|
+
TopupResponse,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentPayClient:
|
|
23
|
+
"""AgentPay API istemcisi.
|
|
24
|
+
|
|
25
|
+
Kullanım:
|
|
26
|
+
client = AgentPayClient(api_key="sg_...", base_url="https://api.aipp.dev")
|
|
27
|
+
bal = client.get_balance()
|
|
28
|
+
result = client.spend(amount=5.0, description="AI call #42")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
api_key: str,
|
|
34
|
+
base_url: str = "https://api.aipp.dev",
|
|
35
|
+
timeout: float = 30.0,
|
|
36
|
+
admin_token: Optional[str] = None,
|
|
37
|
+
):
|
|
38
|
+
self.api_key = api_key
|
|
39
|
+
self.base_url = base_url.rstrip("/")
|
|
40
|
+
self.admin_token = admin_token
|
|
41
|
+
self._client = httpx.Client(
|
|
42
|
+
base_url=self.base_url,
|
|
43
|
+
timeout=httpx.Timeout(timeout),
|
|
44
|
+
headers={"X-API-Key": api_key},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# ────────────────────────────────
|
|
48
|
+
# Auth / Kayıt
|
|
49
|
+
# ────────────────────────────────
|
|
50
|
+
|
|
51
|
+
def register(self, idempotency_key: Optional[str] = None) -> RegisterResponse:
|
|
52
|
+
"""Yeni API key + kredi al (auth gerekmez)."""
|
|
53
|
+
headers = {}
|
|
54
|
+
if idempotency_key:
|
|
55
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
56
|
+
resp = self._client.post("/v1/auth/register", headers=headers)
|
|
57
|
+
resp.raise_for_status()
|
|
58
|
+
data = resp.json()
|
|
59
|
+
return RegisterResponse(
|
|
60
|
+
api_key=data["api_key"],
|
|
61
|
+
credits=data["credits"],
|
|
62
|
+
client_id=data["client_id"],
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# ────────────────────────────────
|
|
66
|
+
# Bakiye
|
|
67
|
+
# ────────────────────────────────
|
|
68
|
+
|
|
69
|
+
def get_balance(self) -> BalanceInfo:
|
|
70
|
+
"""Mevcut bakiyeyi sorgula."""
|
|
71
|
+
resp = self._client.get("/v1/balance")
|
|
72
|
+
resp.raise_for_status()
|
|
73
|
+
data = resp.json()
|
|
74
|
+
return BalanceInfo(
|
|
75
|
+
balance=data["balance"],
|
|
76
|
+
credits_used=data["credits_used"],
|
|
77
|
+
credits_remaining=data["credits_remaining"],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def forecast(
|
|
81
|
+
self, estimated_cost_per_call: float = 0.01
|
|
82
|
+
) -> ForecastResult:
|
|
83
|
+
"""Bakiye yetecek mi tahmini."""
|
|
84
|
+
bal = self.get_balance()
|
|
85
|
+
will_exhaust = estimated_cost_per_call > 0 and (
|
|
86
|
+
bal.credits_remaining / estimated_cost_per_call < 100
|
|
87
|
+
)
|
|
88
|
+
return ForecastResult(
|
|
89
|
+
current_balance=bal.credits_remaining,
|
|
90
|
+
estimated_cost_per_call=estimated_cost_per_call,
|
|
91
|
+
estimated_calls_remaining=(
|
|
92
|
+
int(bal.credits_remaining / estimated_cost_per_call)
|
|
93
|
+
if estimated_cost_per_call > 0
|
|
94
|
+
else 0
|
|
95
|
+
),
|
|
96
|
+
will_exhaust=will_exhaust,
|
|
97
|
+
total_spent_all_time=bal.credits_used,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# ────────────────────────────────
|
|
101
|
+
# Harcama
|
|
102
|
+
# ────────────────────────────────
|
|
103
|
+
|
|
104
|
+
def spend(
|
|
105
|
+
self,
|
|
106
|
+
amount: float,
|
|
107
|
+
description: str = "",
|
|
108
|
+
idempotency_key: Optional[str] = None,
|
|
109
|
+
) -> SpendResult:
|
|
110
|
+
"""Kredi harca (idempotent)."""
|
|
111
|
+
payload: dict = {
|
|
112
|
+
"amount": amount,
|
|
113
|
+
"description": description or f"spend-{int(time.time())}",
|
|
114
|
+
}
|
|
115
|
+
headers = {}
|
|
116
|
+
if idempotency_key:
|
|
117
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
118
|
+
resp = self._client.post(
|
|
119
|
+
"/v1/spend", json=payload, headers=headers
|
|
120
|
+
)
|
|
121
|
+
resp.raise_for_status()
|
|
122
|
+
data = resp.json()
|
|
123
|
+
return SpendResult(
|
|
124
|
+
success=data.get("success", True),
|
|
125
|
+
credits_used=data.get("credits_used", amount),
|
|
126
|
+
credits_remaining=data.get("credits_remaining", 0),
|
|
127
|
+
message=data.get("message", ""),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# ────────────────────────────────
|
|
131
|
+
# Paywall (L402)
|
|
132
|
+
# ────────────────────────────────
|
|
133
|
+
|
|
134
|
+
def generate_paywall(
|
|
135
|
+
self,
|
|
136
|
+
amount: float,
|
|
137
|
+
description: str = "",
|
|
138
|
+
metadata: Optional[dict] = None,
|
|
139
|
+
) -> PaywallResponse:
|
|
140
|
+
"""LN faturası oluştur — müşteri ödeyince token alır."""
|
|
141
|
+
payload = {
|
|
142
|
+
"amount": amount,
|
|
143
|
+
"description": description or "AgentPay paywall",
|
|
144
|
+
}
|
|
145
|
+
if metadata:
|
|
146
|
+
payload["metadata"] = metadata
|
|
147
|
+
resp = self._client.post("/v1/paywall", json=payload)
|
|
148
|
+
resp.raise_for_status()
|
|
149
|
+
data = resp.json()
|
|
150
|
+
return PaywallResponse(
|
|
151
|
+
token=data["token"],
|
|
152
|
+
cost=data["cost"],
|
|
153
|
+
invoice=data["invoice"],
|
|
154
|
+
paid=False,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def verify_paywall(self, token: str) -> PaywallResponse:
|
|
158
|
+
"""Ödeme durumunu kontrol et."""
|
|
159
|
+
resp = self._client.get(f"/v1/paywall/{token}")
|
|
160
|
+
resp.raise_for_status()
|
|
161
|
+
data = resp.json()
|
|
162
|
+
return PaywallResponse(
|
|
163
|
+
token=token,
|
|
164
|
+
cost=data.get("cost", 0),
|
|
165
|
+
invoice=data.get("invoice", ""),
|
|
166
|
+
paid=data.get("paid", False),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# ────────────────────────────────
|
|
170
|
+
# Bakiye Yükleme
|
|
171
|
+
# ────────────────────────────────
|
|
172
|
+
|
|
173
|
+
def topup(self, usd_amount: float = 5.0) -> TopupResponse:
|
|
174
|
+
"""LN faturası ile kredi yükle."""
|
|
175
|
+
resp = self._client.post(
|
|
176
|
+
"/v1/topup", json={"usd_amount": usd_amount}
|
|
177
|
+
)
|
|
178
|
+
resp.raise_for_status()
|
|
179
|
+
data = resp.json()
|
|
180
|
+
return TopupResponse(
|
|
181
|
+
success=data.get("success", True),
|
|
182
|
+
invoice=data["invoice"],
|
|
183
|
+
credits=data.get("credits", usd_amount * 100),
|
|
184
|
+
usd_amount=usd_amount,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# ────────────────────────────────
|
|
188
|
+
# Admin (admin_token gerekli)
|
|
189
|
+
# ────────────────────────────────
|
|
190
|
+
|
|
191
|
+
def list_clients(self) -> list[ClientInfo]:
|
|
192
|
+
"""Tüm müşterileri listele (admin)."""
|
|
193
|
+
if not self.admin_token:
|
|
194
|
+
raise ValueError("admin_token required for list_clients")
|
|
195
|
+
resp = self._client.get(
|
|
196
|
+
"/v1/admin/clients",
|
|
197
|
+
headers={"Authorization": f"Bearer {self.admin_token}"},
|
|
198
|
+
)
|
|
199
|
+
resp.raise_for_status()
|
|
200
|
+
return [
|
|
201
|
+
ClientInfo(
|
|
202
|
+
id=c["id"],
|
|
203
|
+
api_key=c.get("api_key", ""),
|
|
204
|
+
credits_used=c.get("credits_used", 0),
|
|
205
|
+
is_admin=c.get("is_admin", False),
|
|
206
|
+
)
|
|
207
|
+
for c in resp.json()
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
def get_client(self, client_id: str) -> ClientInfo:
|
|
211
|
+
"""Müşteri detayı (admin)."""
|
|
212
|
+
if not self.admin_token:
|
|
213
|
+
raise ValueError("admin_token required for get_client")
|
|
214
|
+
resp = self._client.get(
|
|
215
|
+
f"/v1/admin/clients/{client_id}",
|
|
216
|
+
headers={"Authorization": f"Bearer {self.admin_token}"},
|
|
217
|
+
)
|
|
218
|
+
resp.raise_for_status()
|
|
219
|
+
c = resp.json()
|
|
220
|
+
return ClientInfo(
|
|
221
|
+
id=c["id"],
|
|
222
|
+
api_key=c.get("api_key", ""),
|
|
223
|
+
credits_used=c.get("credits_used", 0),
|
|
224
|
+
is_admin=c.get("is_admin", False),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def health(self) -> dict:
|
|
228
|
+
"""API sağlık kontrolü."""
|
|
229
|
+
resp = self._client.get("/health")
|
|
230
|
+
resp.raise_for_status()
|
|
231
|
+
return resp.json()
|
agentpay/models.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class ClientInfo:
|
|
7
|
+
"""Müşteri bilgisi."""
|
|
8
|
+
id: str
|
|
9
|
+
api_key: str
|
|
10
|
+
credits_used: float = 0.0
|
|
11
|
+
is_admin: bool = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class BalanceInfo:
|
|
16
|
+
"""Bakiye bilgisi."""
|
|
17
|
+
balance: float
|
|
18
|
+
credits_used: float
|
|
19
|
+
credits_remaining: float
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class PaywallResponse:
|
|
24
|
+
"""L402 paywall yanıtı."""
|
|
25
|
+
token: str
|
|
26
|
+
cost: float
|
|
27
|
+
invoice: str # Lightning fatura
|
|
28
|
+
paid: bool = False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class SpendResult:
|
|
33
|
+
"""Harcama sonucu."""
|
|
34
|
+
success: bool
|
|
35
|
+
credits_used: float
|
|
36
|
+
credits_remaining: float
|
|
37
|
+
message: str = ""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class TopupResponse:
|
|
42
|
+
"""Bakiye yükleme yanıtı."""
|
|
43
|
+
success: bool
|
|
44
|
+
invoice: str
|
|
45
|
+
credits: float
|
|
46
|
+
usd_amount: float
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class RegisterResponse:
|
|
51
|
+
"""Kayıt yanıtı."""
|
|
52
|
+
api_key: str
|
|
53
|
+
credits: float
|
|
54
|
+
client_id: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ForecastResult:
|
|
59
|
+
"""Bakiye tahmini."""
|
|
60
|
+
current_balance: float
|
|
61
|
+
estimated_cost_per_call: float
|
|
62
|
+
estimated_calls_remaining: int
|
|
63
|
+
will_exhaust: bool
|
|
64
|
+
total_spent_all_time: float = 0.0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentpay-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AgentPay Python SDK — AI agent ödeme altyapısı
|
|
5
|
+
Author-email: AIPP <aippcore@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://api.aipp.dev
|
|
8
|
+
Project-URL: Source, https://github.com/aippdev/agentpay-python
|
|
9
|
+
Project-URL: Documentation, https://api.aipp.dev/docs
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: httpx>=0.25.0
|
|
23
|
+
|
|
24
|
+
# AgentPay Python SDK
|
|
25
|
+
|
|
26
|
+
AI agent'lar için Lightning Network tabanlı ödeme altyapısı.
|
|
27
|
+
|
|
28
|
+
## Kurulum
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install agentpay-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Kullanım
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from agentpay import AgentPayClient
|
|
38
|
+
|
|
39
|
+
# Yeni API key al (50 ücretsiz kredi ile)
|
|
40
|
+
client = AgentPayClient(api_key="sg_...")
|
|
41
|
+
bal = client.get_balance()
|
|
42
|
+
print(f"Bakiye: {bal.credits_remaining} kredi")
|
|
43
|
+
|
|
44
|
+
# Kredi harca (idempotent)
|
|
45
|
+
result = client.spend(amount=5.0, description="AI çağrısı")
|
|
46
|
+
print(f"Harcandı: {result.credits_used}, Kalan: {result.credits_remaining}")
|
|
47
|
+
|
|
48
|
+
# Bakiye tahmini
|
|
49
|
+
forecast = client.forecast(estimated_cost_per_call=0.05)
|
|
50
|
+
if forecast.will_exhaust:
|
|
51
|
+
print("Bakiye azalıyor!")
|
|
52
|
+
print(f"Tahmini çağrı: {forecast.estimated_calls_remaining}")
|
|
53
|
+
|
|
54
|
+
# Paywall (L402) — LN faturası ile ödeme
|
|
55
|
+
pw = client.generate_paywall(amount=10.0, description="Premium içerik")
|
|
56
|
+
print(f"Fatura: {pw.invoice}")
|
|
57
|
+
|
|
58
|
+
# Topup — kredi yükle
|
|
59
|
+
top = client.topup(usd_amount=5.0)
|
|
60
|
+
print(f"Yükleme faturası: {top.invoice}")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API Referansı
|
|
64
|
+
|
|
65
|
+
Tüm metodlar ve modeller için: [api.aipp.dev/docs](https://api.aipp.dev/docs)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
agentpay/__init__.py,sha256=iEEAvrvocGGW9BCh5oN89zbHvRA8Ftd7Rd2_FNTsIqY,434
|
|
2
|
+
agentpay/client.py,sha256=6-DuXhDn5JJk81dacXHNSAQimFDPkBNCryiT27kmP9g,7955
|
|
3
|
+
agentpay/models.py,sha256=lCvwx_NRhDNR5ywrsU4L33zSONzv9sXynvpjHMSQhjs,1140
|
|
4
|
+
agentpay_python-0.1.0.dist-info/METADATA,sha256=6ue-5GHLgFBT4AolE7jo7mnd00MxNVDXfiKB3shmBdk,2019
|
|
5
|
+
agentpay_python-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
agentpay_python-0.1.0.dist-info/top_level.txt,sha256=-ZKxHLESIWgQ31Vn1cP5LAtq7d3iqrmzjYCw6E7zrz0,9
|
|
7
|
+
agentpay_python-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agentpay
|