solvapay-python 0.7.2__tar.gz → 0.9.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.
- solvapay_python-0.9.0/.github/ISSUE_TEMPLATE/bug.yml +27 -0
- solvapay_python-0.9.0/.github/ISSUE_TEMPLATE/feature.yml +23 -0
- solvapay_python-0.9.0/.github/ISSUE_TEMPLATE/question.yml +14 -0
- solvapay_python-0.9.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- solvapay_python-0.9.0/.github/dependabot.yml +18 -0
- solvapay_python-0.9.0/.github/workflows/ci.yml +53 -0
- solvapay_python-0.9.0/.github/workflows/contract.yml +22 -0
- solvapay_python-0.9.0/.github/workflows/docs.yml +21 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/.gitignore +1 -0
- solvapay_python-0.9.0/CHANGELOG.md +121 -0
- solvapay_python-0.9.0/CODEOWNERS +1 -0
- solvapay_python-0.9.0/CODE_OF_CONDUCT.md +27 -0
- solvapay_python-0.9.0/CONTRIBUTING.md +71 -0
- solvapay_python-0.9.0/PKG-INFO +318 -0
- solvapay_python-0.9.0/README.md +282 -0
- solvapay_python-0.9.0/SECURITY.md +36 -0
- solvapay_python-0.9.0/SUPPORT.md +10 -0
- solvapay_python-0.9.0/assets/agent-marketplace.png +0 -0
- solvapay_python-0.9.0/changelog.d/README.md +35 -0
- solvapay_python-0.9.0/docs/architecture/layers.md +46 -0
- solvapay_python-0.9.0/docs/errors.md +50 -0
- solvapay_python-0.9.0/docs/guides/fastapi.md +53 -0
- solvapay_python-0.9.0/docs/guides/langchain.md +45 -0
- solvapay_python-0.9.0/docs/guides/mcp.md +50 -0
- solvapay_python-0.9.0/docs/idempotency.md +83 -0
- solvapay_python-0.9.0/docs/index.md +63 -0
- solvapay_python-0.9.0/docs/migration.md +49 -0
- solvapay_python-0.9.0/docs/reference/client.md +5 -0
- solvapay_python-0.9.0/docs/reference/exceptions.md +3 -0
- solvapay_python-0.9.0/docs/reference/models.md +3 -0
- solvapay_python-0.9.0/docs/reference/paywall.md +9 -0
- solvapay_python-0.9.0/docs/reference/webhooks.md +15 -0
- solvapay_python-0.9.0/docs/rfcs/0001-spending-policy.md +60 -0
- solvapay_python-0.9.0/docs/rfcs/0002-openapi-investigation.md +35 -0
- solvapay_python-0.9.0/docs/webhooks.md +79 -0
- solvapay_python-0.9.0/examples/marketplace/.gitignore +4 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/.env.example +3 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/.gitignore +4 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/README.md +52 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/agent_langchain.py +43 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/model.py +13 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/pyproject.toml +9 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/script_async.py +43 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/server_mcp.py +28 -0
- solvapay_python-0.9.0/examples/multi-framework-paywall/tool.py +36 -0
- solvapay_python-0.9.0/mkdocs.yml +44 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/pyproject.toml +57 -2
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/__init__.py +31 -1
- solvapay_python-0.9.0/src/solvapay/_async_client.py +329 -0
- solvapay_python-0.9.0/src/solvapay/_http.py +21 -0
- solvapay_python-0.9.0/src/solvapay/_stability.py +60 -0
- solvapay_python-0.9.0/src/solvapay/_transport/__init__.py +106 -0
- solvapay_python-0.9.0/src/solvapay/_transport/_recipe.py +63 -0
- solvapay_python-0.9.0/src/solvapay/_transport/httpx_transport.py +401 -0
- solvapay_python-0.9.0/src/solvapay/_transport/middleware.py +409 -0
- solvapay_python-0.9.0/src/solvapay/adapters/__init__.py +3 -0
- solvapay_python-0.9.0/src/solvapay/adapters/asgi.py +119 -0
- solvapay_python-0.9.0/src/solvapay/adapters/langchain.py +88 -0
- solvapay_python-0.9.0/src/solvapay/adapters/mcp.py +158 -0
- solvapay_python-0.9.0/src/solvapay/client.py +305 -0
- solvapay_python-0.9.0/src/solvapay/idempotency.py +37 -0
- solvapay_python-0.9.0/src/solvapay/langchain.py +15 -0
- solvapay_python-0.9.0/src/solvapay/operations/__init__.py +7 -0
- solvapay_python-0.9.0/src/solvapay/operations/_registry.py +117 -0
- solvapay_python-0.9.0/src/solvapay/operations/checkout.py +75 -0
- solvapay_python-0.9.0/src/solvapay/operations/customers.py +256 -0
- solvapay_python-0.9.0/src/solvapay/operations/limits.py +77 -0
- solvapay_python-0.9.0/src/solvapay/operations/merchant.py +71 -0
- solvapay_python-0.9.0/src/solvapay/operations/plans.py +167 -0
- solvapay_python-0.9.0/src/solvapay/operations/products.py +141 -0
- solvapay_python-0.9.0/src/solvapay/operations/purchases.py +103 -0
- solvapay_python-0.9.0/src/solvapay/operations/usage.py +75 -0
- solvapay_python-0.9.0/src/solvapay/paywall/__init__.py +32 -0
- solvapay_python-0.9.0/src/solvapay/paywall/core.py +152 -0
- solvapay_python-0.9.0/src/solvapay/paywall/decorators.py +126 -0
- solvapay_python-0.9.0/src/solvapay/paywall/meta.py +18 -0
- solvapay_python-0.9.0/src/solvapay/paywall/policy.py +14 -0
- solvapay_python-0.9.0/src/solvapay/paywall/resolvers.py +60 -0
- solvapay_python-0.9.0/src/solvapay/paywall/state.py +23 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/__init__.py +43 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/envelope.py +16 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/pipeline.py +77 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/replay.py +74 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/rotation.py +36 -0
- solvapay_python-0.9.0/src/solvapay/webhooks/sign.py +37 -0
- solvapay_python-0.7.2/src/solvapay/webhooks.py → solvapay_python-0.9.0/src/solvapay/webhooks/verify.py +4 -22
- solvapay_python-0.9.0/tests/_stability/__init__.py +0 -0
- solvapay_python-0.9.0/tests/_stability/test_stable_returns_identity.py +75 -0
- solvapay_python-0.9.0/tests/_transport/__init__.py +0 -0
- solvapay_python-0.9.0/tests/_transport/test_aclose_cascade.py +100 -0
- solvapay_python-0.9.0/tests/_transport/test_error_wrapping.py +66 -0
- solvapay_python-0.9.0/tests/_transport/test_headers_case_insensitive.py +52 -0
- solvapay_python-0.9.0/tests/_transport/test_middleware_composition.py +51 -0
- solvapay_python-0.9.0/tests/_transport/test_protocol_conformance.py +46 -0
- solvapay_python-0.9.0/tests/_transport/test_recording_transport.py +68 -0
- solvapay_python-0.9.0/tests/_transport/test_retry_transport.py +78 -0
- solvapay_python-0.9.0/tests/adapters/__init__.py +0 -0
- solvapay_python-0.9.0/tests/adapters/test_asgi.py +91 -0
- solvapay_python-0.9.0/tests/adapters/test_langchain_protocol.py +99 -0
- solvapay_python-0.9.0/tests/adapters/test_mcp.py +165 -0
- solvapay_python-0.9.0/tests/contract/README.md +38 -0
- solvapay_python-0.9.0/tests/contract/__init__.py +0 -0
- solvapay_python-0.9.0/tests/contract/cassettes/.gitkeep +0 -0
- solvapay_python-0.9.0/tests/contract/test_contract_ops.py +61 -0
- solvapay_python-0.9.0/tests/operations/__init__.py +0 -0
- solvapay_python-0.9.0/tests/operations/test_namespace_api.py +59 -0
- solvapay_python-0.9.0/tests/operations/test_path_interpolation.py +54 -0
- solvapay_python-0.9.0/tests/operations/test_retry_safety_enum.py +57 -0
- solvapay_python-0.9.0/tests/paywall/__init__.py +0 -0
- solvapay_python-0.9.0/tests/paywall/test_checkout_mint_error_surfaces.py +52 -0
- solvapay_python-0.9.0/tests/paywall/test_payable_tool_meta.py +59 -0
- solvapay_python-0.9.0/tests/paywall/test_resolvers.py +47 -0
- solvapay_python-0.9.0/tests/paywall/test_split_classes.py +34 -0
- solvapay_python-0.9.0/tests/test_api_version.py +57 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_idempotency.py +33 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_invariants.py +2 -2
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_langchain.py +21 -16
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_paywall.py +5 -5
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_paywall_state.py +42 -39
- solvapay_python-0.9.0/tests/webhooks/__init__.py +0 -0
- solvapay_python-0.9.0/tests/webhooks/test_async_cache.py +48 -0
- solvapay_python-0.9.0/tests/webhooks/test_clock_skew_vs_replay_ttl.py +58 -0
- solvapay_python-0.9.0/tests/webhooks/test_rotation.py +47 -0
- solvapay_python-0.9.0/tests/webhooks/test_seen_cache_atomic.py +54 -0
- solvapay_python-0.9.0/tests/webhooks/test_sign.py +37 -0
- solvapay_python-0.9.0/tests/webhooks/test_webhook_pipeline.py +66 -0
- solvapay_python-0.9.0/tools/api_baseline.json +82 -0
- solvapay_python-0.9.0/tools/api_diff.py +60 -0
- solvapay_python-0.9.0/tools/importlinter.cfg +42 -0
- solvapay_python-0.9.0/tools/lint_invariants.py +208 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/uv.lock +1444 -120
- solvapay_python-0.7.2/.github/workflows/ci.yml +0 -24
- solvapay_python-0.7.2/CHANGELOG.md +0 -68
- solvapay_python-0.7.2/PKG-INFO +0 -357
- solvapay_python-0.7.2/README.md +0 -326
- solvapay_python-0.7.2/src/solvapay/_async_client.py +0 -387
- solvapay_python-0.7.2/src/solvapay/_http.py +0 -220
- solvapay_python-0.7.2/src/solvapay/client.py +0 -380
- solvapay_python-0.7.2/src/solvapay/idempotency.py +0 -16
- solvapay_python-0.7.2/src/solvapay/langchain.py +0 -67
- solvapay_python-0.7.2/src/solvapay/paywall.py +0 -153
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/.github/workflows/publish.yml +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/.python-version +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/LICENSE +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/.env.example +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/.gitignore +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/README.md +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/claim.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/pyproject.toml +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/server.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/fastmcp-paywall/uv.lock +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/langchain-paywall/.env.example +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/langchain-paywall/.gitignore +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/langchain-paywall/README.md +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/langchain-paywall/agent.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/langchain-paywall/pyproject.toml +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/.env.example +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/.streamlit/config.toml +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/PLAN.md +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/README.md +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/agents.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/app.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/demo_customers.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/requirements.txt +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/sdk_gateway.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/examples/marketplace/ui_components.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/_config.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/events.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/exceptions.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/fastapi.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/models.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/paywall_state.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/src/solvapay/py.typed +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/__init__.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/conftest.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_admin.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_async_client.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_checkout.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_config.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_customer.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_errors.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_http.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_lifecycle.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_limits.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_packaging.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_redaction.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_webhook_events.py +0 -0
- {solvapay_python-0.7.2 → solvapay_python-0.9.0}/tests/test_webhooks.py +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Something broken in the SDK
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: Please fill in the details below.
|
|
8
|
+
- type: input
|
|
9
|
+
id: version
|
|
10
|
+
attributes:
|
|
11
|
+
label: SDK version
|
|
12
|
+
placeholder: "0.8.0"
|
|
13
|
+
validations:
|
|
14
|
+
required: true
|
|
15
|
+
- type: textarea
|
|
16
|
+
id: description
|
|
17
|
+
attributes:
|
|
18
|
+
label: What happened?
|
|
19
|
+
description: What did you expect vs what actually happened?
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: repro
|
|
24
|
+
attributes:
|
|
25
|
+
label: Minimal repro
|
|
26
|
+
description: Minimal Python snippet to reproduce
|
|
27
|
+
render: python
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new capability
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What problem does this solve?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: proposal
|
|
13
|
+
attributes:
|
|
14
|
+
label: Proposed solution
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: dropdown
|
|
18
|
+
id: priority
|
|
19
|
+
attributes:
|
|
20
|
+
label: Priority
|
|
21
|
+
options:
|
|
22
|
+
- Nice to have
|
|
23
|
+
- Blocking a use-case
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: Question
|
|
2
|
+
description: How do I...?
|
|
3
|
+
labels: [question]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: question
|
|
7
|
+
attributes:
|
|
8
|
+
label: What are you trying to do?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: tried
|
|
13
|
+
attributes:
|
|
14
|
+
label: What have you tried?
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## What
|
|
2
|
+
|
|
3
|
+
<!-- Brief description of the change -->
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
<!-- Problem it solves or feature it adds -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Tests added / updated
|
|
12
|
+
- [ ] `CHANGELOG.md` entry added
|
|
13
|
+
- [ ] Layer DAG respected (`uv run lint-imports` passes)
|
|
14
|
+
- [ ] If new public exports: `tools/api_baseline.json` updated
|
|
15
|
+
- [ ] Docs updated (if public API changed)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
labels:
|
|
9
|
+
- dependencies
|
|
10
|
+
|
|
11
|
+
- package-ecosystem: github-actions
|
|
12
|
+
directory: /
|
|
13
|
+
schedule:
|
|
14
|
+
interval: weekly
|
|
15
|
+
open-pull-requests-limit: 5
|
|
16
|
+
labels:
|
|
17
|
+
- dependencies
|
|
18
|
+
- ci
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
include:
|
|
13
|
+
# Linux — full Python matrix
|
|
14
|
+
- os: ubuntu-latest
|
|
15
|
+
python-version: "3.10"
|
|
16
|
+
- os: ubuntu-latest
|
|
17
|
+
python-version: "3.11"
|
|
18
|
+
- os: ubuntu-latest
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
# macOS — 3.12 only
|
|
21
|
+
- os: macos-latest
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
# Windows — 3.12 only
|
|
24
|
+
- os: windows-latest
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: astral-sh/setup-uv@v3
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
- name: Set Python ${{ matrix.python-version }}
|
|
33
|
+
run: uv python install ${{ matrix.python-version }}
|
|
34
|
+
- run: uv sync --all-extras --dev
|
|
35
|
+
- run: uv run ruff check src tests
|
|
36
|
+
- run: uv run ruff format --check src tests
|
|
37
|
+
- run: uv run mypy src
|
|
38
|
+
- run: uv run pytest -v
|
|
39
|
+
- run: uv run python tools/api_diff.py
|
|
40
|
+
- run: uv run lint-imports --config tools/importlinter.cfg
|
|
41
|
+
- run: uv run python tools/lint_invariants.py
|
|
42
|
+
- name: pip-audit
|
|
43
|
+
run: uv run pip-audit
|
|
44
|
+
- name: Check changelog fragment on PRs touching src/
|
|
45
|
+
if: github.event_name == 'pull_request'
|
|
46
|
+
run: |
|
|
47
|
+
changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
|
|
48
|
+
if echo "$changed" | grep -q "^src/"; then
|
|
49
|
+
if ! echo "$changed" | grep -q "^changelog.d/"; then
|
|
50
|
+
echo "ERROR: src/ modified without a changelog.d/ fragment. Add one — see changelog.d/README.md"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: contract
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: "0 2 * * *" # nightly at 02:00 UTC
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
contract:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v3
|
|
14
|
+
with:
|
|
15
|
+
enable-cache: true
|
|
16
|
+
- run: uv sync --all-extras --dev
|
|
17
|
+
- name: Run contract tests
|
|
18
|
+
run: uv run pytest tests/contract/ -m contract -v
|
|
19
|
+
env:
|
|
20
|
+
SOLVAPAY_SANDBOX_KEY: ${{ secrets.SOLVAPAY_SANDBOX_KEY }}
|
|
21
|
+
SOLVAPAY_TEST_CUSTOMER_REF: ${{ secrets.SOLVAPAY_TEST_CUSTOMER_REF }}
|
|
22
|
+
SOLVAPAY_TEST_PRODUCT_REF: ${{ secrets.SOLVAPAY_TEST_PRODUCT_REF }}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
deploy:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v3
|
|
17
|
+
with:
|
|
18
|
+
enable-cache: true
|
|
19
|
+
- run: uv sync --group docs
|
|
20
|
+
- name: Deploy to GitHub Pages
|
|
21
|
+
run: uv run mkdocs gh-deploy --force
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.0 — 2026-05-23
|
|
4
|
+
|
|
5
|
+
Production polish + C1 adversarial-review closure. API-version pinning, idempotency TTL, webhook secret rotation, ASGI adapter, retry/recording transports, contract tests, lint automation, doc site, supply-chain hardening.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **API-version pinning**: `SolvaPay(api_version="2026-05-22")` and `AsyncSolvaPay(api_version=...)` send `Solvapay-Version` header. Default pinned to `"2026-05-22"`. `api_version=None` omits the header. (HLD V1.13)
|
|
9
|
+
- **Idempotency time-bucket encoding**: `from_payload(*parts, time_bucket="day"|"hour"|None)`. Default `"day"` appends UTC date so keys roll at midnight, bounding replay ambiguity past server TTL. (HLD V1.14)
|
|
10
|
+
- **Webhook secret rotation**: `MultiSecretVerifier` full implementation — tries primary then secondary on signature mismatch; both comparisons constant-time. (HLD V1.7)
|
|
11
|
+
- **`AsyncInMemorySeenEventCache`**: async replay-dedup cache using `asyncio.Lock` for async webhook pipelines. (HLD V1.7)
|
|
12
|
+
- **`sign_webhook(body, secret, *, timestamp)`**: public helper to produce a valid `sv-signature` header for testing and outbound webhook fanout.
|
|
13
|
+
- **ASGI webhook adapter** (`solvapay[asgi]`): `webhook_app(pipeline, on_event, path)` returns a raw ASGI app mountable in Starlette, FastAPI, Litestar, or BlackSheep. (HLD V1.10)
|
|
14
|
+
- **`RetryTransport`**: middleware that retries `APIConnectionError`, `APITimeoutError`, `APIServerError`, `RateLimitError` — exponential backoff with jitter, max 3 attempts. Consults `OpSpec.retry_safety`; refuses to retry `NEVER` ops. (HLD V1.4, `solvapay[retry]`)
|
|
15
|
+
- **`RecordingTransport`**: records `(RequestSpec, ResponseSpec)` pairs to JSON cassettes; replays on subsequent runs. Enables offline contract tests. (HLD V1.4)
|
|
16
|
+
- **Sandbox contract tests** (`tests/contract/`): one test per op against real sandbox via `RecordingTransport`. Nightly CI workflow (`contract.yml`).
|
|
17
|
+
- **Lint invariants** (`tools/lint_invariants.py`): AST checks for 10 gotchas (future annotations, `hmac.compare_digest`, no `logging.basicConfig`, no `asyncio.run()`, alias discipline, etc.). Run in CI on every push.
|
|
18
|
+
- **Towncrier changelog automation**: `changelog.d/` fragments, PR gate requiring a fragment when `src/` changes, `towncrier>=23` dev dep.
|
|
19
|
+
- **MkDocs Material doc site**: `mkdocs.yml` + `docs/` skeleton (Quickstart, Reference, Architecture, Guides, Errors, Idempotency, Webhooks, Migration). Deploys to GitHub Pages on tag. (HLD V1.12)
|
|
20
|
+
- **Dependabot**: weekly Python + GitHub Actions dependency updates (`.github/dependabot.yml`).
|
|
21
|
+
- **`pip-audit` in CI**: dependency CVE scan on every push.
|
|
22
|
+
- **CI matrix expanded**: Ubuntu 3.10/3.11/3.12 + macOS 3.12 + Windows 3.12. (HLD V1.15)
|
|
23
|
+
- **`ResourceWarning` on unclosed `AsyncSolvaPay`**: `__del__` emits `ResourceWarning` if client is not closed and event loop is still running.
|
|
24
|
+
- **OpenAPI investigation RFC**: `docs/rfcs/0002-openapi-investigation.md`.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- `pytest.ini_options.filterwarnings`: adds `ignore::DeprecationWarning:solvapay` so tests exercising deprecated flat-shim API do not fail on expected warnings.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 0.8.0 — 2026-05-23
|
|
32
|
+
|
|
33
|
+
V1 architecture spine + AI-agent moat. 10 atomic commits establishing locked architecture invariants per HLD §V1.1–V1.19.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- **Transport Protocol kernel** (`solvapay._transport`): `Transport`/`AsyncTransport` Protocol shapes, `RequestSpec`/`ResponseSpec` frozen dataclasses, case-insensitive `Headers`, canonical middleware stack (`Redacting` → `Logging` → `IdempotencyHeader` → `ContextPropagating`), `default_stack()` recipe.
|
|
37
|
+
- **Operations registry + resource-namespace API**: `OpSpec` + `RetrySafety` enum, `REGISTRY`, path interpolation with `urllib.parse.quote`. All clients gain eager namespace attrs: `sv.customers`, `sv.checkout`, `sv.limits`, `sv.purchases`, `sv.usage`, `sv.products`, `sv.plans`, `sv.merchant`.
|
|
38
|
+
- **Paywall package** (`solvapay.paywall`): `Paywall`/`AsyncPaywall` split with type-narrowed construction, `PaywallRequired.checkout_mint_error`, `PayableToolMeta(_meta_version=1)`, `KwargsResolver`/`PositionalResolver`/`PydanticBodyResolver`, `@require`/`@require_async`/`@payable_tool` decorators.
|
|
39
|
+
- **Webhook pipeline** (`solvapay.webhooks`): `WebhookPipeline` with two-knob config (`max_clock_skew_seconds`, `replay_ttl_seconds`), atomic `SeenEventCache.try_claim` (`threading.Lock`), `MultiSecretVerifier` interface, `WebhookEnvelope` dataclass.
|
|
40
|
+
- **`solvapay.adapters.mcp`** (optional `solvapay[mcp]`): `@payable_tool` decorator + four schema flavors (`payable_tool_mcp_schema`, `payable_tool_openai_function`, `payable_tool_anthropic_tool`, `payable_tool_langchain_args_schema`), `register_payable_tool_fastmcp`.
|
|
41
|
+
- **Stability manifest** (`solvapay._stability`): `stable(X)→X` identity decorator registers in `MANIFEST`; `experimental` emits once-per-process `RuntimeWarning`; `deprecated(removed_in=)`. CI gate: `tools/api_diff.py` fails on `@stable` symbol removal without prior `@deprecated`.
|
|
42
|
+
- **Import-linter DAG gate**: `tools/importlinter.cfg` (3 contracts), `docs/architecture/layers.md`. CI fails on layer violation.
|
|
43
|
+
- **LangChain adapter refactor** (`solvapay.adapters.langchain`): Protocol duck-typing — no hard `langchain_core` import at module level; absorbs 0.3→0.4 churn.
|
|
44
|
+
- **Multi-framework demo** (`examples/multi-framework-paywall/`): one `@payable_tool`-decorated function runs across FastMCP, LangChain, and raw async — "one tool, three runtimes, one paywall."
|
|
45
|
+
- **Governance scaffold**: `CODEOWNERS`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `SUPPORT.md`, `CONTRIBUTING.md`, GitHub issue templates, PR template, `docs/rfcs/0001-spending-policy.md`.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- **Flat methods deprecated**: `sv.ensure_customer()`, `sv.check_limits()`, etc. now emit `DeprecationWarning(stacklevel=2)` per call. Use `sv.customers.ensure()`, `sv.limits.check()`, etc. Flat shims removed in v2.0.
|
|
49
|
+
- **`langchain-core` upper bound**: `langchain-core>=0.3,<0.4` now enforced in both dev and optional deps.
|
|
50
|
+
- **`solvapay.langchain`** replaced by `solvapay.adapters.langchain`; old path is a deprecated shim.
|
|
51
|
+
|
|
52
|
+
### Deprecated
|
|
53
|
+
- `SolvaPayAPIError` — use `APIError`. Alias fires `DeprecationWarning`; removed in v2.0.
|
|
54
|
+
- All flat client methods (`sv.ensure_customer`, `sv.check_limits`, etc.) — use resource-namespace API.
|
|
55
|
+
|
|
56
|
+
## 0.7.2 — 2026-05-18
|
|
57
|
+
|
|
58
|
+
Bug fixes and documentation update.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
- **`paywall.require_async`**: `AsyncSolvaPay()` instantiated without a caller-supplied `client=` was not closed after the decorated function returned, leaking the underlying `httpx.AsyncClient` connection pool. Wrapped in `try/finally`; `await sv.aclose()` called when the decorator owns the client.
|
|
62
|
+
- **`examples/fastmcp-paywall/pyproject.toml`**: dependency used wrong PyPI dist name (`solvapay`) and a stale `@v0.3.0` pin. Updated to `solvapay-python>=0.7.2`.
|
|
63
|
+
- **`examples/langchain-paywall/pyproject.toml`**: same wrong dist name and stale `@v0.5.0` pin. Updated to `solvapay-python[langchain]>=0.7.2`.
|
|
64
|
+
|
|
65
|
+
### Docs
|
|
66
|
+
- **README** updated to v0.7.1 surface: `paywall_state.gate()`, error hierarchy, idempotency keys, admin ops table, marketplace example, roadmap entries for v0.7.0 and v0.7.1.
|
|
67
|
+
|
|
68
|
+
### Internal
|
|
69
|
+
- `tests/test_lifecycle.py` reformatted (ruff format compliance)
|
|
70
|
+
|
|
71
|
+
## 0.7.1 — 2026-05-17
|
|
72
|
+
|
|
73
|
+
Payments-grade hardening: structured errors, idempotency keys, py.typed, structured logging.
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
- **Structured error hierarchy** (`exceptions.py`): `APIError` base with `status_code`, `request_id`, `error_code`, `error_message`. Subclasses: `AuthenticationError` (401), `PermissionError` (403), `NotFoundError` (404), `RateLimitError` (429, adds `.retry_after`), `InvalidRequestError` (4xx), `APIServerError` (5xx), `APIConnectionError`, `APITimeoutError`. `SolvaPayAPIError` aliased to `APIError` for back-compat.
|
|
77
|
+
- **Idempotency keys** on all mutating ops: `create_checkout_session`, `ensure_customer`, `track_usage`, `cancel_purchase`, `reactivate_purchase`, `create_product`, `clone_product`, `create_plan` all accept `idempotency_key: str | None = None`. Header casing: `Idempotency-Key` (matches TS SDK).
|
|
78
|
+
- **`solvapay.idempotency.from_payload(*parts)`** — SHA256 of stable-serialized payload parts → 32-hex-char deterministic key.
|
|
79
|
+
- **PEP 561 `py.typed` marker** — downstream mypy users no longer see `Any` on `solvapay.*` imports.
|
|
80
|
+
- **Structured logging** at `solvapay.http` logger: INFO on success (method, path, status, request_id, duration_ms), WARNING on 4xx/5xx (adds body_excerpt ≤200 chars). Never calls `logging.basicConfig`. Optional `logger=` injection on `SolvaPay`/`AsyncSolvaPay` constructors for `loguru`/`structlog`.
|
|
81
|
+
- **Secret redaction**: `Authorization` header never appears in logs. Verified by `tests/test_redaction.py`.
|
|
82
|
+
- **pyproject classifiers**: `Development Status :: 4 - Beta`, `Framework :: AsyncIO`, `Topic :: Office/Business :: Financial`, `Typing :: Typed`.
|
|
83
|
+
|
|
84
|
+
### Internal
|
|
85
|
+
- User-Agent bumped to `solvapay-python/0.7.1`
|
|
86
|
+
- 142 tests (up from 125), `mypy --strict` clean, `ruff` clean
|
|
87
|
+
|
|
88
|
+
## 0.7.0 — 2026-05-17
|
|
89
|
+
|
|
90
|
+
Real-API alignment after testing against the SolvaPay sandbox revealed wire-format mismatches.
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
- **`Customer.customer_ref`** now accepts `reference` (real API) in addition to `customerRef` via `validation_alias=AliasChoices(...)`.
|
|
94
|
+
- **`ensure_customer()`** reads `reference` from API response; falls back to `customerRef`. Raises if neither present.
|
|
95
|
+
- **`BalanceResponse`** rewritten: `credits`, `display_currency`, `credits_per_minor_unit`, `display_exchange_rate`; `balance` and `currency` kept as computed properties.
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
- **`paywall_state.gate()`** — one-call enrichment helper (limits + checkout URL + plan via `get_customer`).
|
|
99
|
+
- **`paywall.require` / `require_async`**: auto-mints checkout URL when `LimitResponse.checkout_url is None`.
|
|
100
|
+
- **`examples/marketplace/`** — Streamlit demo with 4 paywalled AI agents (Google Gemini) against real SolvaPay sandbox.
|
|
101
|
+
|
|
102
|
+
### Internal
|
|
103
|
+
- User-Agent `solvapay-python/0.7.0`. 125 tests (up from 121). `mypy --strict` clean.
|
|
104
|
+
|
|
105
|
+
## 0.6.0
|
|
106
|
+
- Admin endpoints (products, plans, merchant config). GitHub Actions trusted-publish to PyPI.
|
|
107
|
+
|
|
108
|
+
## 0.5.0
|
|
109
|
+
- `paywall_state` classifier (ACTIVATION_REQUIRED / TOPUP_REQUIRED / UPGRADE_REQUIRED). LangChain `monetize_tool`.
|
|
110
|
+
|
|
111
|
+
## 0.4.0
|
|
112
|
+
- Full async client. 5 lifecycle operations. 13 typed webhook event classes.
|
|
113
|
+
|
|
114
|
+
## 0.3.0
|
|
115
|
+
- FastMCP example: AI agent with two paywalled tools.
|
|
116
|
+
|
|
117
|
+
## 0.2.0
|
|
118
|
+
- `@paywall.require` decorator. FastAPI webhook router.
|
|
119
|
+
|
|
120
|
+
## 0.1.0
|
|
121
|
+
- Sync client, HMAC-SHA256 webhook verification, Pydantic v2 models, CI on 3 Python versions.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @dhruv-sanan
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We pledge to make participation in our community a harassment-free experience for everyone.
|
|
6
|
+
|
|
7
|
+
## Our Standards
|
|
8
|
+
|
|
9
|
+
**Positive behavior:**
|
|
10
|
+
- Using welcoming and inclusive language
|
|
11
|
+
- Being respectful of differing viewpoints
|
|
12
|
+
- Gracefully accepting constructive criticism
|
|
13
|
+
- Focusing on what is best for the community
|
|
14
|
+
|
|
15
|
+
**Unacceptable behavior:**
|
|
16
|
+
- Harassment, trolling, or personal attacks
|
|
17
|
+
- Publishing others' private information without permission
|
|
18
|
+
- Other conduct which could reasonably be considered inappropriate
|
|
19
|
+
|
|
20
|
+
## Enforcement
|
|
21
|
+
|
|
22
|
+
Community leaders may remove, edit, or reject contributions that violate this Code of Conduct.
|
|
23
|
+
Report issues to `dhruv.sanan@greyorange.com`.
|
|
24
|
+
|
|
25
|
+
## Attribution
|
|
26
|
+
|
|
27
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Local development
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/dhruv-sanan/solvapay-python
|
|
7
|
+
cd solvapay-python
|
|
8
|
+
uv sync --all-extras --dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Running checks
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv run ruff check src tests # lint
|
|
15
|
+
uv run ruff format --check src tests # format gate
|
|
16
|
+
uv run mypy src # type check
|
|
17
|
+
uv run pytest -v # tests
|
|
18
|
+
uv run python tools/api_diff.py # stability manifest gate
|
|
19
|
+
uv run lint-imports --config tools/importlinter.cfg # layer DAG gate
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Layer DAG rules
|
|
23
|
+
|
|
24
|
+
The SDK has a strict layer hierarchy — see [docs/architecture/layers.md](docs/architecture/layers.md).
|
|
25
|
+
|
|
26
|
+
**Hard rules:**
|
|
27
|
+
- `_transport` must not import `client`, `paywall`, `adapters`, or `operations`
|
|
28
|
+
- `operations` must not import `paywall` or `adapters`
|
|
29
|
+
- `client` must not import `adapters` or `experimental`
|
|
30
|
+
|
|
31
|
+
CI fails on violations via `import-linter`.
|
|
32
|
+
|
|
33
|
+
## Changelog fragment (required)
|
|
34
|
+
|
|
35
|
+
Every PR that modifies `src/` must include a towncrier fragment in `changelog.d/`.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Create a fragment (replace 42 with your issue/PR number)
|
|
39
|
+
echo "Add RetryTransport middleware for automatic retry on transient errors." > changelog.d/42.feature
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
See `changelog.d/README.md` for naming conventions and types.
|
|
43
|
+
|
|
44
|
+
PRs that touch `src/` without a `changelog.d/` entry will fail CI.
|
|
45
|
+
|
|
46
|
+
## PR checklist
|
|
47
|
+
|
|
48
|
+
- [ ] Tests added for new behavior
|
|
49
|
+
- [ ] `changelog.d/<issue>.<type>` fragment added (required for `src/` changes)
|
|
50
|
+
- [ ] `docs/` updated if public API changed
|
|
51
|
+
- [ ] Layer DAG respected (`uv run lint-imports` passes)
|
|
52
|
+
- [ ] Stability manifest updated if new public exports added (`uv run python tools/api_diff.py`)
|
|
53
|
+
|
|
54
|
+
## Commit style
|
|
55
|
+
|
|
56
|
+
[Conventional Commits](https://www.conventionalcommits.org/):
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
feat: add AsyncPaywall class
|
|
60
|
+
fix(webhooks): set replay_ttl_seconds default correctly
|
|
61
|
+
refactor: extract _transport package
|
|
62
|
+
docs: update README with MCP quickstart
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Adding a new resource namespace
|
|
66
|
+
|
|
67
|
+
1. Add `OpSpec` entries to `src/solvapay/operations/_registry.py`
|
|
68
|
+
2. Create `src/solvapay/operations/<resource>.py` with sync + async methods
|
|
69
|
+
3. Wire namespace attr in `SolvaPay.__init__` and `AsyncSolvaPay.__init__`
|
|
70
|
+
4. Add deprecated flat shim in `client.py` / `_async_client.py`
|
|
71
|
+
5. Tests in `tests/operations/`
|