solvapay-python 0.7.1__py3-none-any.whl → 0.7.2__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.
- solvapay/__init__.py +1 -1
- solvapay/paywall.py +23 -18
- {solvapay_python-0.7.1.dist-info → solvapay_python-0.7.2.dist-info}/METADATA +82 -3
- {solvapay_python-0.7.1.dist-info → solvapay_python-0.7.2.dist-info}/RECORD +6 -6
- {solvapay_python-0.7.1.dist-info → solvapay_python-0.7.2.dist-info}/WHEEL +0 -0
- {solvapay_python-0.7.1.dist-info → solvapay_python-0.7.2.dist-info}/licenses/LICENSE +0 -0
solvapay/__init__.py
CHANGED
solvapay/paywall.py
CHANGED
|
@@ -124,24 +124,29 @@ def require_async(
|
|
|
124
124
|
f"@paywall.require_async expected str kwarg '{customer_ref_arg}', "
|
|
125
125
|
f"got {type(customer_ref).__name__}"
|
|
126
126
|
)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
127
|
+
_owns_client = not isinstance(client, AsyncSolvaPay)
|
|
128
|
+
sv: AsyncSolvaPay = AsyncSolvaPay() if _owns_client else client # type: ignore[assignment]
|
|
129
|
+
try:
|
|
130
|
+
limits = await sv.check_limits(
|
|
131
|
+
customer_ref=customer_ref,
|
|
132
|
+
product_ref=product,
|
|
133
|
+
plan_ref=plan,
|
|
134
|
+
)
|
|
135
|
+
if not limits.within_limits:
|
|
136
|
+
checkout_url = limits.checkout_url
|
|
137
|
+
if checkout_url is None:
|
|
138
|
+
try:
|
|
139
|
+
session = await sv.create_checkout_session(
|
|
140
|
+
customer_ref=customer_ref, product_ref=product, plan_ref=plan
|
|
141
|
+
)
|
|
142
|
+
checkout_url = session.checkout_url
|
|
143
|
+
except SolvaPayError:
|
|
144
|
+
pass
|
|
145
|
+
raise PaywallRequired(checkout_url=checkout_url)
|
|
146
|
+
return await fn(*args, **kwargs)
|
|
147
|
+
finally:
|
|
148
|
+
if _owns_client:
|
|
149
|
+
await sv.aclose()
|
|
145
150
|
|
|
146
151
|
return wrapper
|
|
147
152
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solvapay-python
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Community Python SDK for SolvaPay (agent-native payment rails)
|
|
5
5
|
Project-URL: Homepage, https://github.com/dhruv-sanan/solvapay-python
|
|
6
6
|
Project-URL: Issues, https://github.com/dhruv-sanan/solvapay-python/issues
|
|
@@ -35,12 +35,15 @@ Description-Content-Type: text/markdown
|
|
|
35
35
|
|
|
36
36
|
Community Python SDK for [SolvaPay](https://solvapay.com) — payment rails for the agentic economy.
|
|
37
37
|
|
|
38
|
-
> **Status:** v0.
|
|
38
|
+
> **Status:** v0.7.2, community-maintained. Available on PyPI. Pending official adoption.
|
|
39
39
|
> Mirrors the most-used surface of [@solvapay/core](https://github.com/solvapay/solvapay-sdk).
|
|
40
40
|
|
|
41
41
|
Python is the dominant language for agent frameworks (LangChain, FastMCP, CrewAI, AutoGen). SolvaPay's official SDK is TypeScript-only. This SDK brings first-class Python support so agent developers can gate tools behind paywalls without switching ecosystems.
|
|
42
42
|
|
|
43
|
-
> **New in v0.
|
|
43
|
+
> **New in v0.7.2:** Async resource leak fix in `@paywall.require_async` — `AsyncSolvaPay` now properly closed when owned by the decorator. Example dep fixes.
|
|
44
|
+
> **v0.7.1:** Full error hierarchy (`AuthenticationError`, `NotFoundError`, `RateLimitError`, `APIConnectionError`, `APITimeoutError`), idempotency keys on all mutating ops, `py.typed` PEP 561 marker, structured HTTP logging.
|
|
45
|
+
> **New in v0.7.0:** Real-API alignment (wire-format fixes), `paywall_state.gate()` enrichment helper, marketplace Streamlit demo.
|
|
46
|
+
> **v0.6:** Admin endpoints (products, plans, merchant, platform config). Published to PyPI.
|
|
44
47
|
> **v0.5:** Paywall state classifier (`paywall_state` module) and LangChain `monetize_tool` decorator — gate any LangChain tool behind a SolvaPay paywall with one line.
|
|
45
48
|
> **v0.4:** Async client (`AsyncSolvaPay`), lifecycle ops, typed webhook events.
|
|
46
49
|
|
|
@@ -150,12 +153,69 @@ if not limits.within_limits:
|
|
|
150
153
|
print(d.checkout_url) # "https://solvapay.com/c/..."
|
|
151
154
|
```
|
|
152
155
|
|
|
156
|
+
For real-API calls use `gate()` instead — it enriches the bare `/v1/sdk/limits` response (which has no `plan` or `checkout_url`) in one call:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from solvapay.paywall_state import gate
|
|
160
|
+
|
|
161
|
+
decision = gate(sv, customer_ref="cus_x", product_ref="prd_y")
|
|
162
|
+
# decision.state — PaywallState.UPGRADE_REQUIRED (etc)
|
|
163
|
+
# decision.checkout_url — minted via create_checkout_session if needed
|
|
164
|
+
# decision.message — TS-style copy with URL inlined
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Error handling
|
|
168
|
+
|
|
169
|
+
v0.7.1 ships a structured exception hierarchy under `SolvaPayError`:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
import time
|
|
173
|
+
from solvapay import (
|
|
174
|
+
SolvaPayError, # catch-all
|
|
175
|
+
APIError, # base for all HTTP errors — has .status_code, .request_id
|
|
176
|
+
AuthenticationError, # 401
|
|
177
|
+
NotFoundError, # 404
|
|
178
|
+
RateLimitError, # 429 — adds .retry_after
|
|
179
|
+
APIConnectionError, # network failure
|
|
180
|
+
APITimeoutError, # request timeout
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
sv.get_customer("cus_missing")
|
|
185
|
+
except NotFoundError as e:
|
|
186
|
+
print(e.status_code, e.request_id)
|
|
187
|
+
except RateLimitError as e:
|
|
188
|
+
time.sleep(float(e.retry_after or 1))
|
|
189
|
+
except SolvaPayError:
|
|
190
|
+
raise
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Use `except SolvaPayError` as the catch-all. Prefer specific subclasses over checking `.status_code`.
|
|
194
|
+
|
|
195
|
+
## Idempotency keys
|
|
196
|
+
|
|
197
|
+
All mutating ops accept an optional `idempotency_key` to make retries safe:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from solvapay.idempotency import from_payload
|
|
201
|
+
|
|
202
|
+
key = from_payload("checkout", customer_ref, product_ref)
|
|
203
|
+
session = sv.create_checkout_session(
|
|
204
|
+
customer_ref=customer_ref,
|
|
205
|
+
product_ref="prd_xyz",
|
|
206
|
+
idempotency_key=key,
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`from_payload(*parts)` hashes its args to a 32-hex-char deterministic key. Pass the same key on retry — SolvaPay deduplicates server-side.
|
|
211
|
+
|
|
153
212
|
## Examples
|
|
154
213
|
|
|
155
214
|
| Path | What it shows |
|
|
156
215
|
|---|---|
|
|
157
216
|
| [`examples/fastmcp-paywall/`](examples/fastmcp-paywall/) | FastMCP server with two paywalled tools. Demo for `@paywall.require` + MCP. |
|
|
158
217
|
| [`examples/langchain-paywall/`](examples/langchain-paywall/) | LangChain agent with `monetize_tool`. Shows paywall response in agent trace. |
|
|
218
|
+
| [`examples/marketplace/`](examples/marketplace/) | Streamlit demo — paywalled AI-agent marketplace. Real sandbox, real Gemini LLM, two demo customers (one subscribed, one free tier). Shows `paywall_state.gate()` in action. |
|
|
159
219
|
|
|
160
220
|
## TS ↔ Python parity
|
|
161
221
|
|
|
@@ -199,6 +259,22 @@ session = sv.create_checkout_session(
|
|
|
199
259
|
| `cancel_purchase` | `POST /v1/sdk/purchases/{ref}/cancel` | Cancel a subscription |
|
|
200
260
|
| `reactivate_purchase` | `POST /v1/sdk/purchases/{ref}/reactivate` | Reactivate cancelled purchase |
|
|
201
261
|
|
|
262
|
+
**Admin (new in v0.6):**
|
|
263
|
+
|
|
264
|
+
| Python | Verb + path | Description |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| `list_products` | `GET /v1/sdk/products` | List all products |
|
|
267
|
+
| `get_product` | `GET /v1/sdk/products/{ref}` | Fetch product |
|
|
268
|
+
| `create_product` | `POST /v1/sdk/products` | Create product |
|
|
269
|
+
| `delete_product` | `DELETE /v1/sdk/products/{ref}` | Delete product |
|
|
270
|
+
| `clone_product` | `POST /v1/sdk/products/{ref}/clone` | Clone product |
|
|
271
|
+
| `list_plans` | `GET /v1/sdk/products/{ref}/plans` | List plans |
|
|
272
|
+
| `create_plan` | `POST /v1/sdk/products/{ref}/plans` | Create plan |
|
|
273
|
+
| `update_plan` | `PUT /v1/sdk/products/{ref}/plans/{ref}` | Update plan |
|
|
274
|
+
| `delete_plan` | `DELETE /v1/sdk/products/{ref}/plans/{ref}` | Delete plan |
|
|
275
|
+
| `get_merchant` | `GET /v1/sdk/merchant` | Merchant info |
|
|
276
|
+
| `get_platform_config` | `GET /v1/sdk/platform-config` | Platform config |
|
|
277
|
+
|
|
202
278
|
All methods available on both `SolvaPay` (sync) and `AsyncSolvaPay` (async).
|
|
203
279
|
|
|
204
280
|
## Webhook handler (FastAPI)
|
|
@@ -261,6 +337,9 @@ async def handle_webhook(request: Request) -> dict:
|
|
|
261
337
|
- v0.4 — async client (`AsyncSolvaPay`), lifecycle ops, typed webhook events ✅
|
|
262
338
|
- v0.5 — paywall state classifier, LangChain `monetize_tool` decorator ✅
|
|
263
339
|
- v0.6 — admin endpoints (products, plans, merchant, platform config), PyPI publish ✅
|
|
340
|
+
- v0.7.0 — real-API wire-format fixes, `paywall_state.gate()`, marketplace demo ✅
|
|
341
|
+
- v0.7.1 — structured error hierarchy, idempotency keys, `py.typed`, structured logging ✅
|
|
342
|
+
- v0.7.2 — async resource leak fix (`require_async`), example dep fixes ✅
|
|
264
343
|
|
|
265
344
|
## Contributing
|
|
266
345
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
solvapay/__init__.py,sha256=
|
|
1
|
+
solvapay/__init__.py,sha256=1GgHO6-iQViSW0vZGKghMV1NMHDtWEy-gM-SRr06Ch0,1774
|
|
2
2
|
solvapay/_async_client.py,sha256=4RtP3K5NlkqOSfqDNS26Hzit4aUbh68HSvCDUVrmYoY,13509
|
|
3
3
|
solvapay/_config.py,sha256=yPU_GM77pXwPfIzuncuLRNv4322q5bB9qaawN3izEB8,606
|
|
4
4
|
solvapay/_http.py,sha256=ug396rug0EsKCgs_rgi8iYMkfm0hKOlQQ2sEjyUz22o,6709
|
|
@@ -9,11 +9,11 @@ solvapay/fastapi.py,sha256=vouWqbc_Yurdg8dIZ7zhO4-J_QxpYnyNgwyLHgDEoHs,2125
|
|
|
9
9
|
solvapay/idempotency.py,sha256=LzsLw6TeaeLJbMVqZSEQ-038f6oIUomCmQDBqzlKMGE,514
|
|
10
10
|
solvapay/langchain.py,sha256=VOrze5-7COE4--kn8JqbyrmwhWwv3XwN3Ol3YMpffgc,2353
|
|
11
11
|
solvapay/models.py,sha256=SsmkSLH5xp-Hgf2GjhDcrqGt5FoWKtXJkDZUQE7wwWI,6810
|
|
12
|
-
solvapay/paywall.py,sha256=
|
|
12
|
+
solvapay/paywall.py,sha256=OQCX8XT73IWDaFyRHDz_19ZYOkXY891O_zDbpI73D7U,5771
|
|
13
13
|
solvapay/paywall_state.py,sha256=pfP_-5B6LRcWrvItwgDwh-9f-DSowYaFDKXxGJvYPrg,7048
|
|
14
14
|
solvapay/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
solvapay/webhooks.py,sha256=6EuoISjJzrKEUIoNpt__l2FnJvQR4T7XnT7fzOFHl5U,2738
|
|
16
|
-
solvapay_python-0.7.
|
|
17
|
-
solvapay_python-0.7.
|
|
18
|
-
solvapay_python-0.7.
|
|
19
|
-
solvapay_python-0.7.
|
|
16
|
+
solvapay_python-0.7.2.dist-info/METADATA,sha256=2Fbd-hzM9PsqJEEBiWXbYuIh6OWrq5CUlZieDfMueCg,12767
|
|
17
|
+
solvapay_python-0.7.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
18
|
+
solvapay_python-0.7.2.dist-info/licenses/LICENSE,sha256=wJURmEXLdSdApQdHG-RCwBoZVka1Oux8zNrLxGC30S8,1068
|
|
19
|
+
solvapay_python-0.7.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|