solvapay-python 0.9.0__py3-none-any.whl → 0.9.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 CHANGED
@@ -37,7 +37,7 @@ from solvapay.exceptions import (
37
37
  )
38
38
  from solvapay.models import BalanceResponse, Merchant, Plan, PlatformConfig, Product
39
39
  from solvapay.paywall import PaywallRequired
40
- from solvapay.webhooks import verify_webhook
40
+ from solvapay.webhooks import sign_webhook, verify_webhook
41
41
 
42
42
  # Register stable exports in MANIFEST (HLD V1.2).
43
43
  # stable(X) returns X unchanged — isinstance() continues to work (HLD SM1).
@@ -54,6 +54,7 @@ stable(APIServerError)
54
54
  stable(APIConnectionError)
55
55
  stable(APITimeoutError)
56
56
  stable(PaywallRequired)
57
+ stable(sign_webhook)
57
58
  stable(verify_webhook)
58
59
  stable(BalanceResponse)
59
60
  stable(Product)
@@ -99,10 +100,36 @@ __all__ = [
99
100
  "SolvaPayAPIError",
100
101
  "SolvaPayError",
101
102
  "WebhookEvent",
103
+ # adapters exposed lazily via __getattr__ below (PEP 562)
104
+ "adapters",
102
105
  "deprecated",
103
106
  "experimental",
104
107
  "paywall",
108
+ "sign_webhook",
105
109
  "stable",
106
110
  "verify_webhook",
107
111
  ]
108
- __version__ = "0.9.0"
112
+ __version__ = "0.9.2"
113
+
114
+ # PEP 562 — lazy adapter submodule access.
115
+ # Adapters drag in optional heavy deps (fastmcp, langchain-core, fastapi).
116
+ # __getattr__ defers their load until first access; they never load on bare
117
+ # `import solvapay` for users who don't use framework adapters.
118
+ _LAZY_MODULES = {
119
+ "adapters": "solvapay.adapters",
120
+ }
121
+
122
+
123
+ def __getattr__(name: str) -> object:
124
+ if name in _LAZY_MODULES:
125
+ import importlib
126
+
127
+ mod = importlib.import_module(_LAZY_MODULES[name])
128
+ globals()[name] = mod
129
+ return mod
130
+ raise AttributeError(f"module 'solvapay' has no attribute {name!r}")
131
+
132
+
133
+ def __dir__() -> list[str]:
134
+ # PEP 562: include lazy names alongside normal module attrs.
135
+ return sorted(set(globals()) | set(_LAZY_MODULES))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solvapay-python
3
- Version: 0.9.0
3
+ Version: 0.9.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
@@ -21,15 +21,17 @@ Classifier: Topic :: Office/Business :: Financial
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.10
24
- Requires-Dist: httpx>=0.27
25
- Requires-Dist: pydantic<2.13,>=2.6
24
+ Requires-Dist: httpx<0.29,>=0.27
25
+ Requires-Dist: pydantic<2.14,>=2.6
26
26
  Provides-Extra: asgi
27
+ Provides-Extra: bench
28
+ Requires-Dist: pytest-benchmark<6,>=4.0; extra == 'bench'
27
29
  Provides-Extra: fastapi
28
30
  Requires-Dist: fastapi>=0.110; extra == 'fastapi'
29
31
  Provides-Extra: langchain
30
- Requires-Dist: langchain-core<0.4,>=0.3; extra == 'langchain'
32
+ Requires-Dist: langchain-core<1.5,>=0.3; extra == 'langchain'
31
33
  Provides-Extra: mcp
32
- Requires-Dist: fastmcp<0.5,>=0.4; extra == 'mcp'
34
+ Requires-Dist: fastmcp<4,>=3.2.0; extra == 'mcp'
33
35
  Provides-Extra: retry
34
36
  Requires-Dist: tenacity<10,>=8.2; extra == 'retry'
35
37
  Description-Content-Type: text/markdown
@@ -138,16 +140,23 @@ if limits.within_limits:
138
140
 
139
141
  ## Idempotency
140
142
 
141
- All mutating ops accept `idempotency_key`. Use `solvapay.idempotency.from_payload` to derive deterministic keys from request content:
143
+ All mutating ops accept `idempotency_key`. Use `solvapay.idempotency.from_payload` to derive deterministic keys:
142
144
 
143
145
  ```python
144
146
  from solvapay.idempotency import from_payload
145
147
 
148
+ # Default: key includes UTC date — rolls at midnight, bounds replay past server TTL
146
149
  key = from_payload("track_usage", customer_ref, product_ref, "requests", units)
147
150
  sv.usage.track(..., idempotency_key=key) # retry-safe
151
+
152
+ # Hourly bucket (high-frequency ops)
153
+ key = from_payload("charge", customer_ref, time_bucket="hour")
154
+
155
+ # Pure payload hash — caller manages TTL externally
156
+ key = from_payload("idempotent_op", ref, time_bucket=None)
148
157
  ```
149
158
 
150
- Retried POSTs **must reuse the same key**. Key should change only when the logical request changes.
159
+ Retried POSTs **must reuse the same key**. A bucket roll (midnight / hour boundary) produces a new key — the server treats it as a new request.
151
160
 
152
161
  ---
153
162
 
@@ -174,7 +183,7 @@ except SolvaPayError as e:
174
183
  ... # catch-all
175
184
  ```
176
185
 
177
- No built-in retries by design. Layer `tenacity` manually. `solvapay[retry]` RetryTransport ships in v0.9.
186
+ No built-in retries by default. `solvapay[retry]` ships `RetryTransport` exponential backoff with jitter, 3 attempts, respects `OpSpec.retry_safety` (won't retry non-idempotent ops without an idempotency key). Or layer `tenacity` manually.
178
187
 
179
188
  ---
180
189
 
@@ -193,6 +202,30 @@ pipeline = WebhookPipeline(
193
202
  envelope = pipeline.process(body=request.body, signature=request.headers["sv-signature"])
194
203
  ```
195
204
 
205
+ **Secret rotation** — pass multiple secrets; primary tried first, secondary on mismatch:
206
+
207
+ ```python
208
+ pipeline = WebhookPipeline(["whsec_new...", "whsec_old..."])
209
+ ```
210
+
211
+ **Sign a webhook** (testing / outbound fanout) — available at the top level since v0.9.2:
212
+
213
+ ```python
214
+ from solvapay import sign_webhook # top-level (v0.9.2+)
215
+ # from solvapay.webhooks import sign_webhook # subpackage path also works
216
+
217
+ header = sign_webhook(body=b'{"type":"purchase.created"}', secret="whsec_...")
218
+ # → "t=1716470000,v1=abc123..."
219
+ ```
220
+
221
+ **ASGI adapter** — mount directly in FastAPI / Starlette / Litestar:
222
+
223
+ ```python
224
+ from solvapay.adapters.asgi import webhook_app
225
+
226
+ app.mount("/webhook", webhook_app(pipeline, on_event=handle))
227
+ ```
228
+
196
229
  **Typed events** — discriminated union over 13 event types:
197
230
 
198
231
  ```python
@@ -279,6 +312,9 @@ pip install solvapay-python # core
279
312
  pip install 'solvapay-python[mcp]' # + FastMCP adapter (FastMCP ≥0.4)
280
313
  pip install 'solvapay-python[langchain]' # + LangChain adapter (langchain-core ≥0.3)
281
314
  pip install 'solvapay-python[fastapi]' # + FastAPI webhook router
315
+ pip install 'solvapay-python[asgi]' # + raw ASGI webhook adapter (no extra deps)
316
+ pip install 'solvapay-python[retry]' # + RetryTransport (tenacity)
317
+ pip install 'solvapay-python[bench]' # + pytest-benchmark (dev/perf testing)
282
318
  ```
283
319
 
284
320
  ## Environment variables
@@ -302,17 +338,43 @@ pip install 'solvapay-python[fastapi]' # + FastAPI webhook router
302
338
 
303
339
  ---
304
340
 
341
+ ## API version pinning
342
+
343
+ Pin the API version your code was written against — prevents silent breakage when the server evolves:
344
+
345
+ ```python
346
+ sv = SolvaPay(api_version="2026-05-22") # sends Solvapay-Version header
347
+ sv = SolvaPay(api_version=None) # omit header (use server default)
348
+ ```
349
+
350
+ Default is `"2026-05-22"` (v0.9 ship date). Bump only on major SDK versions.
351
+
352
+ ---
353
+
305
354
  ## Roadmap
306
355
 
307
356
  | Version | Theme |
308
357
  |---------|-------|
309
358
  | v0.8 ✅ | V1 architecture spine — Transport kernel, OpSpec registry, paywall/webhook packages, `@payable_tool`, stability manifest, layer DAG CI gate |
310
- | v0.9 | Production polish — API-version pinning, idempotency TTL, `RetryTransport`, `RecordingTransport`, contract tests, lint automation, doc site (MkDocs Material), supply-chain hygiene |
311
- | v1.0 | Gated on founder signalOpenAPI-generated models, full secret rotation, production hardening |
359
+ | v0.9 | Production polish — API-version pinning, idempotency TTL, `RetryTransport`, `RecordingTransport`, ASGI adapter, secret rotation, `sign_webhook`, contract tests, lint automation, MkDocs site, supply-chain hygiene |
360
+ | v0.9.1 | Security & supply-chain qualityPyPI attestations (PEP 740 / Sigstore), CycloneDX SBOM on releases, `bandit` + `osv-scanner` CI, Hypothesis-driven secret-redaction property tests, constant-time `verify_webhook` smoke test, explicit PCI-scope statement |
361
+ | v0.9.2 ✅ | Performance & stability quality — cold-import baseline harness, PEP 562 lazy adapter imports, `solvapay.sign_webhook` top-level re-export, microbenchmark harness (`solvapay[bench]`), troubleshooting docs |
362
+ | v1.0 | Gated on founder signal — OpenAPI-generated models, WSGI/Lambda adapters, V2 planning |
312
363
 
313
364
  ---
314
365
 
315
366
  ## Status
316
367
 
317
- **v0.8.0** — V1 architecture spine + AI-agent moat. `mypy --strict` clean (43 files). 261 tests. 89% line coverage.
368
+ **v0.9.2** — Performance & stability quality patch. Cold-import baseline harness (`tests/test_cold_import.py`, 1.5x regression gate). PEP 562 lazy adapter imports — bare `import solvapay` no longer loads framework adapter modules. `solvapay.sign_webhook` promoted to top-level (alongside `verify_webhook`). Microbenchmark harness under `tests/benchmarks/` (opt-in `solvapay[bench]`). New docs: `docs/troubleshooting.md` (top-10 errors) and `docs/architecture/cold-start.md`. 305 tests. 87% line coverage. `mypy --strict` clean (45 files).
369
+
370
+ **v0.9.1** — Security & supply-chain quality patch. PyPI uploads now carry PEP 740 attestations (Sigstore-backed via OIDC trusted publishing); each GitHub release ships a CycloneDX SBOM (`sbom.cdx.json`). `mypy --strict` clean (45 files). 302 tests. 87% line / 85% branch coverage.
371
+
372
+ **Supply chain & security posture:**
373
+ - PyPI trusted publishing with PEP 740 attestations (Sigstore)
374
+ - CycloneDX 1.6 SBOM attached to every release
375
+ - CI runs `bandit -r src/ -lll`, `osv-scanner --lockfile=uv.lock`, and `pip-audit` (cross-DB vuln check) on PR and nightly cron
376
+ - Constant-time HMAC comparison (`hmac.compare_digest`) regression-smoked
377
+ - Hypothesis-driven property tests assert no API key, hex token, or webhook secret leaks into log output at any level
378
+ - Tightened upper bounds on volatile deps (`httpx<0.29`, `pydantic<2.14`, `langchain-core<1.5`)
379
+
318
380
  Community SDK, not official. Proposal: [solvapay/solvapay-sdk#187](https://github.com/solvapay/solvapay-sdk/issues/187).
@@ -1,4 +1,4 @@
1
- solvapay/__init__.py,sha256=2_muEPRVjPE7yZApCj83y2hQviJ6OUHdaKwWjje-QOw,2610
1
+ solvapay/__init__.py,sha256=SCzaOP-4tqWo90KW1d_L6YpDACNQqahD85iHeRa3X9w,3488
2
2
  solvapay/_async_client.py,sha256=TdeCyvnfZQCJzkLnYC73WNEG_fYjKWb226Gct1wt7pI,10759
3
3
  solvapay/_config.py,sha256=yPU_GM77pXwPfIzuncuLRNv4322q5bB9qaawN3izEB8,606
4
4
  solvapay/_http.py,sha256=rtjU-MOmxsh3xO-HT7KtjYaTUagPlPqV5eBv-o51Y1Q,408
@@ -44,7 +44,7 @@ solvapay/webhooks/replay.py,sha256=DIv52mCwi1GRtAl7q_O_JWuFSJQMwcQ24JkOuD1uLmU,2
44
44
  solvapay/webhooks/rotation.py,sha256=qYNMBBSp6Zvg59gPc2SUC1cqNIAizrBJ50MRf5B_ztg,1248
45
45
  solvapay/webhooks/sign.py,sha256=X7rA0iZkDBsXo3nvxK8YnQ-zpLfMhCMY-r6ts_MUM90,1150
46
46
  solvapay/webhooks/verify.py,sha256=TIkho3IG-S9IpXWvBLmDHdGfG9_XxUK6w87kNwkLQYo,1954
47
- solvapay_python-0.9.0.dist-info/METADATA,sha256=HKW0WzV46vAPLNe6BItN9of81caqnEkREEUZHMWK6q8,10006
48
- solvapay_python-0.9.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
49
- solvapay_python-0.9.0.dist-info/licenses/LICENSE,sha256=wJURmEXLdSdApQdHG-RCwBoZVka1Oux8zNrLxGC30S8,1068
50
- solvapay_python-0.9.0.dist-info/RECORD,,
47
+ solvapay_python-0.9.2.dist-info/METADATA,sha256=pJb_W5Y_kURJU9HPlqoFUwlVX_Lt0lGd5JyVGTcDafk,13711
48
+ solvapay_python-0.9.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
49
+ solvapay_python-0.9.2.dist-info/licenses/LICENSE,sha256=wJURmEXLdSdApQdHG-RCwBoZVka1Oux8zNrLxGC30S8,1068
50
+ solvapay_python-0.9.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.30.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any