solvapay 0.1.0.dev4__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-0.1.0.dev4/.gitignore +50 -0
- solvapay-0.1.0.dev4/AGENTS.md +22 -0
- solvapay-0.1.0.dev4/PKG-INFO +183 -0
- solvapay-0.1.0.dev4/README.md +155 -0
- solvapay-0.1.0.dev4/conformance.lock +278 -0
- solvapay-0.1.0.dev4/conformance_runner/_agent_identity.py +242 -0
- solvapay-0.1.0.dev4/conformance_runner/_api_answer.py +23 -0
- solvapay-0.1.0.dev4/conformance_runner/agent_calls_runner.py +206 -0
- solvapay-0.1.0.dev4/conformance_runner/conformance_agent_mcp.py +427 -0
- solvapay-0.1.0.dev4/conformance_runner/conformance_merchant_sign_in.py +280 -0
- solvapay-0.1.0.dev4/conformance_runner/test_conformance.py +751 -0
- solvapay-0.1.0.dev4/package.json +6 -0
- solvapay-0.1.0.dev4/pyproject.toml +79 -0
- solvapay-0.1.0.dev4/scripts/check_version.py +41 -0
- solvapay-0.1.0.dev4/scripts/currency_prefixes.mjs +42 -0
- solvapay-0.1.0.dev4/scripts/generate_client.py +324 -0
- solvapay-0.1.0.dev4/scripts/pin_conformance.py +41 -0
- solvapay-0.1.0.dev4/scripts/preview_version.py +68 -0
- solvapay-0.1.0.dev4/scripts/sync_widget.py +53 -0
- solvapay-0.1.0.dev4/scripts/tax_ids.mjs +94 -0
- solvapay-0.1.0.dev4/src/solvapay/__init__.py +26 -0
- solvapay-0.1.0.dev4/src/solvapay/_currencies.py +170 -0
- solvapay-0.1.0.dev4/src/solvapay/_generated/__init__.py +1 -0
- solvapay-0.1.0.dev4/src/solvapay/_generated/models.py +3643 -0
- solvapay-0.1.0.dev4/src/solvapay/_generated/operations.py +1894 -0
- solvapay-0.1.0.dev4/src/solvapay/_http.py +243 -0
- solvapay-0.1.0.dev4/src/solvapay/_js.py +114 -0
- solvapay-0.1.0.dev4/src/solvapay/_tax_ids.py +442 -0
- solvapay-0.1.0.dev4/src/solvapay/agents/__init__.py +67 -0
- solvapay-0.1.0.dev4/src/solvapay/agents/calls.py +302 -0
- solvapay-0.1.0.dev4/src/solvapay/agents/identity.py +293 -0
- solvapay-0.1.0.dev4/src/solvapay/agents/mcp.py +614 -0
- solvapay-0.1.0.dev4/src/solvapay/agents/oauth.py +105 -0
- solvapay-0.1.0.dev4/src/solvapay/business_details.py +124 -0
- solvapay-0.1.0.dev4/src/solvapay/client.py +107 -0
- solvapay-0.1.0.dev4/src/solvapay/errors.py +33 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/__init__.py +90 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/app.py +243 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/auth.py +604 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/bootstrap.py +494 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/intent.py +474 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/mcp-app.html +119 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/narrate.py +1019 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/response.py +220 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/server.py +343 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/tools.py +273 -0
- solvapay-0.1.0.dev4/src/solvapay/mcp/transport.py +550 -0
- solvapay-0.1.0.dev4/src/solvapay/money.py +61 -0
- solvapay-0.1.0.dev4/src/solvapay/paywall/__init__.py +25 -0
- solvapay-0.1.0.dev4/src/solvapay/paywall/decide.py +450 -0
- solvapay-0.1.0.dev4/src/solvapay/paywall/gate.py +176 -0
- solvapay-0.1.0.dev4/src/solvapay/paywall/state.py +371 -0
- solvapay-0.1.0.dev4/src/solvapay/pricing.py +281 -0
- solvapay-0.1.0.dev4/src/solvapay/py.typed +0 -0
- solvapay-0.1.0.dev4/src/solvapay/solvapay.py +173 -0
- solvapay-0.1.0.dev4/src/solvapay/webhooks.py +93 -0
- solvapay-0.1.0.dev4/tests/test_agents.py +314 -0
- solvapay-0.1.0.dev4/tests/test_agents_mcp.py +348 -0
- solvapay-0.1.0.dev4/tests/test_client.py +185 -0
- solvapay-0.1.0.dev4/tests/test_mcp.py +327 -0
- solvapay-0.1.0.dev4/tests/test_mcp_app.py +514 -0
- solvapay-0.1.0.dev4/tests/test_mcp_intent.py +384 -0
- solvapay-0.1.0.dev4/tests/test_paywall.py +311 -0
- solvapay-0.1.0.dev4/uv.lock +2069 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
node_modules
|
|
2
|
+
dist
|
|
3
|
+
.cache
|
|
4
|
+
.next
|
|
5
|
+
.env
|
|
6
|
+
.env.*
|
|
7
|
+
!.env.example
|
|
8
|
+
!.env.example.*
|
|
9
|
+
!.env.platform-local.example
|
|
10
|
+
.npmrc
|
|
11
|
+
.pnpm-store
|
|
12
|
+
.turbo
|
|
13
|
+
.cursor/*
|
|
14
|
+
!.cursor/rules/
|
|
15
|
+
!.cursor/plans/
|
|
16
|
+
!.cursor/skills/
|
|
17
|
+
.DS_Store
|
|
18
|
+
*.tsbuildinfo
|
|
19
|
+
**/.demo-data/*
|
|
20
|
+
**/*.local.sh
|
|
21
|
+
|
|
22
|
+
# Deno — written by `deno check` during the CI validate gate. We pin
|
|
23
|
+
# workspace imports to `@preview` dist-tags on purpose (mutable; each
|
|
24
|
+
# gate run should pick up the latest preview), so committing a lockfile
|
|
25
|
+
# here would defeat the regression-detection window.
|
|
26
|
+
deno.lock
|
|
27
|
+
|
|
28
|
+
# Code coverage
|
|
29
|
+
coverage/
|
|
30
|
+
*.lcov
|
|
31
|
+
.nyc_output
|
|
32
|
+
|
|
33
|
+
# Generated documentation (TypeDoc output)
|
|
34
|
+
docs/api/
|
|
35
|
+
docs/api.json
|
|
36
|
+
|
|
37
|
+
# Scratch research notes — internal, not for the public repo
|
|
38
|
+
research-tmp/
|
|
39
|
+
|
|
40
|
+
# Scratch design explorations — internal, not for the public repo
|
|
41
|
+
tmp-designs/
|
|
42
|
+
|
|
43
|
+
# Python
|
|
44
|
+
__pycache__/
|
|
45
|
+
*.py[cod]
|
|
46
|
+
.venv/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
.mypy_cache/
|
|
49
|
+
.ruff_cache/
|
|
50
|
+
python/dist/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# AGENTS.md — python/
|
|
2
|
+
|
|
3
|
+
The `solvapay` Python SDK. It must behave like the TypeScript SDK; the
|
|
4
|
+
conformance cases in `../conformance` say how.
|
|
5
|
+
|
|
6
|
+
- **Never edit `../conformance` or `../openapi` from here.** Those belong to the
|
|
7
|
+
TypeScript SDK. If a case looks wrong, raise it; don't change it to pass.
|
|
8
|
+
- **Adopting new cases:** make them pass (add a mapping in
|
|
9
|
+
`conformance_runner/` for a new `fn`), then run
|
|
10
|
+
`uv run python scripts/pin_conformance.py`. Cases newer than the pin are
|
|
11
|
+
skipped as "not adopted"; publishing fails until the pin catches up.
|
|
12
|
+
- **Generated files, never by hand:** `src/solvapay/_generated/`
|
|
13
|
+
(`scripts/generate_client.py`), `src/solvapay/mcp/mcp-app.html`
|
|
14
|
+
(`scripts/sync_widget.py`), the currency and tax tables (`scripts/tax_ids.mjs`),
|
|
15
|
+
and `conformance.lock`.
|
|
16
|
+
- Check before committing: `uv run pytest`, `uv run ruff check .`,
|
|
17
|
+
`uv run ruff format --check .`, `uv run mypy`.
|
|
18
|
+
- **Versions:** `python/package.json` (private, never on npm) is the version
|
|
19
|
+
source. Bump it with a changeset naming `@solvapay/python`;
|
|
20
|
+
`pnpm changeset:version` copies the version into `pyproject.toml` and the
|
|
21
|
+
`uv.lock` files. Never edit the version in `pyproject.toml` by hand. It does
|
|
22
|
+
not follow `@solvapay/server`.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: solvapay
|
|
3
|
+
Version: 0.1.0.dev4
|
|
4
|
+
Summary: SolvaPay for Python: paywalls, usage billing, webhooks and paid MCP tools.
|
|
5
|
+
Project-URL: Homepage, https://solvapay.com
|
|
6
|
+
Project-URL: Documentation, https://docs.solvapay.com
|
|
7
|
+
Project-URL: Source, https://github.com/solvapay/solvapay-sdk/tree/main/python
|
|
8
|
+
Author: SolvaPay
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: agents,billing,mcp,payments,paywall,solvapay
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx<1,>=0.27
|
|
21
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
22
|
+
Provides-Extra: agents
|
|
23
|
+
Requires-Dist: pyjwt[crypto]<3,>=2.9; extra == 'agents'
|
|
24
|
+
Provides-Extra: mcp
|
|
25
|
+
Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
|
|
26
|
+
Requires-Dist: starlette>=0.40; extra == 'mcp'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# solvapay
|
|
30
|
+
|
|
31
|
+
SolvaPay for Python: paywalls, usage billing, webhooks and paid MCP tools.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install solvapay # API client, paywall, webhooks
|
|
35
|
+
pip install 'solvapay[mcp]' # paid tools for the official MCP Python SDK
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Python 3.10+. Typed (`py.typed`), async-first, with blocking twins for the API client.
|
|
39
|
+
|
|
40
|
+
## Configure
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export SOLVAPAY_SECRET_KEY=sk_sandbox_... # required
|
|
44
|
+
export SOLVAPAY_PRODUCT_REF=prd_... # the product paid calls bill against
|
|
45
|
+
export SOLVAPAY_API_BASE_URL=... # optional; defaults to https://api.solvapay.com
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Run `npx solvapay init` to sign in and write these to `.env`.
|
|
49
|
+
|
|
50
|
+
## Make a function paid
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from solvapay import PaywallError, create_solvapay
|
|
54
|
+
|
|
55
|
+
solvapay = create_solvapay()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@solvapay.payable()
|
|
59
|
+
async def summarise(text: str, *, customer_ref: str) -> str:
|
|
60
|
+
return text[:100]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
summary = await summarise("...", customer_ref="user_123")
|
|
65
|
+
except PaywallError as error:
|
|
66
|
+
print(error.gate["message"]) # what the customer should see, with a checkout link
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`customer_ref` is your own user id: SolvaPay creates the customer on first use.
|
|
70
|
+
Each call is checked against the customer's plan, and billed when it succeeds.
|
|
71
|
+
|
|
72
|
+
## Paid MCP tools
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from solvapay import create_solvapay
|
|
76
|
+
from solvapay.mcp import ResponseContext, create_solvapay_mcp_server
|
|
77
|
+
|
|
78
|
+
mcp = create_solvapay_mcp_server(
|
|
79
|
+
solvapay=create_solvapay(),
|
|
80
|
+
public_base_url="https://research.example.com",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def quote(symbol: str, ctx: ResponseContext):
|
|
85
|
+
return ctx.respond({"symbol": symbol, "price": 101.5})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
mcp.register_payable("quote", handler=quote, description="Latest price for a symbol.")
|
|
89
|
+
|
|
90
|
+
app = mcp.http_app() # serve with uvicorn; the MCP endpoint is /mcp
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Customers sign in with SolvaPay over OAuth, and every bearer token is verified
|
|
94
|
+
with SolvaPay. A call over the customer's limit returns the paywall: a message
|
|
95
|
+
naming the next step, with a checkout link. See
|
|
96
|
+
[`examples/python-stock-research-mcp`](../examples/python-stock-research-mcp).
|
|
97
|
+
|
|
98
|
+
The server also comes with SolvaPay's built-in tools, as the TypeScript server
|
|
99
|
+
does: `account` shows the customer's plan, balance and usage with a checkout,
|
|
100
|
+
top-up or account link (`view`: `checkout`, `account` or `topup`), and
|
|
101
|
+
`activate_plan` activates a plan by `planRef`. The `/upgrade`,
|
|
102
|
+
`/manage_account`, `/topup` and `/activate_plan` prompts and the
|
|
103
|
+
`docs://solvapay/overview.md` resource come with them. Pass `views=[...]` to
|
|
104
|
+
choose the views, or `views=[]` to leave `account` out.
|
|
105
|
+
|
|
106
|
+
### The checkout widget
|
|
107
|
+
|
|
108
|
+
On hosts that support MCP Apps (ChatGPT, Claude), `account` also opens
|
|
109
|
+
SolvaPay's checkout and account widget in the chat, the same one the TypeScript
|
|
110
|
+
SDK serves. It ships in this package (`solvapay/mcp/mcp-app.html`) and is
|
|
111
|
+
served at `ui://<name>/mcp-app.html` (`resource_uri=` to change it). Text-only
|
|
112
|
+
hosts get the same text as before; `mode: "text"` asks for text on any host.
|
|
113
|
+
|
|
114
|
+
The widget pays, tops up, cancels and reactivates renewals through six tools
|
|
115
|
+
only it calls: `create_payment_intent`, `process_payment`,
|
|
116
|
+
`create_hosted_session`, `set_renewal`, `attach_business_details` and
|
|
117
|
+
`get_history`. They are marked for the app (`_meta.ui.visibility: ["app"]`), so
|
|
118
|
+
MCP Apps hosts keep them from the model. For a host that lists them anyway,
|
|
119
|
+
`hide_tools_by_audience=["ui"]` leaves them out of `tools/list`; ChatGPT still
|
|
120
|
+
gets them, since its widget can only call listed tools.
|
|
121
|
+
|
|
122
|
+
Its Content Security Policy allows Stripe and the SolvaPay API. Add domains with
|
|
123
|
+
`csp={"connectDomains": [...], "resourceDomains": [...], "frameDomains": [...]}`.
|
|
124
|
+
|
|
125
|
+
### Paid by agents
|
|
126
|
+
|
|
127
|
+
`solvapay.agents.mcp.create_agent_mcp_route` serves an MCP server that SolvaPay
|
|
128
|
+
agents pay for per call: each paid tool call is authorized with SolvaPay before
|
|
129
|
+
it runs and completed at the cost the upstream reports. Install
|
|
130
|
+
`solvapay[agents,mcp]`.
|
|
131
|
+
|
|
132
|
+
## Verify webhooks
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from solvapay import SolvaPayError, verify_webhook
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
event = verify_webhook(
|
|
139
|
+
body=raw_body, # bytes or str, exactly as received
|
|
140
|
+
signature=request.headers["sv-signature"],
|
|
141
|
+
secret=os.environ["SOLVAPAY_WEBHOOK_SECRET"],
|
|
142
|
+
)
|
|
143
|
+
except SolvaPayError as error:
|
|
144
|
+
... # error.code: webhook_signature_invalid, webhook_timestamp_out_of_tolerance, ...
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## The API
|
|
148
|
+
|
|
149
|
+
Every SolvaPay API operation, generated from the API spec:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from solvapay import AsyncApiClient
|
|
153
|
+
|
|
154
|
+
async with AsyncApiClient(api_key="sk_sandbox_...") as api:
|
|
155
|
+
customer = await api.get_customer(reference="cus_123")
|
|
156
|
+
balance = await api.get_customer_balance(reference="cus_123")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`ApiClient` is the blocking twin. Errors are `SolvaPayError` with a stable `code`
|
|
160
|
+
and the HTTP `status`; branch on those, not on the message.
|
|
161
|
+
|
|
162
|
+
## Same behaviour as the TypeScript SDK
|
|
163
|
+
|
|
164
|
+
The paywall, its messages, the `account` and `activate_plan` narration, what
|
|
165
|
+
the widget's tools answer and webhook verification are checked against the
|
|
166
|
+
TypeScript SDK on every change by a shared conformance suite
|
|
167
|
+
([`conformance/`](../conformance)). The API client is generated from the same
|
|
168
|
+
spec as the TypeScript types ([`openapi/`](../openapi)).
|
|
169
|
+
|
|
170
|
+
## Versions
|
|
171
|
+
|
|
172
|
+
`solvapay` has its own version line, starting at 0.1.0. Its numbers don't
|
|
173
|
+
follow the TypeScript packages: Python 0.3.0 and `@solvapay/server` 2.12.0 can
|
|
174
|
+
behave the same. What the two share is the conformance suite (the version this
|
|
175
|
+
package passes is pinned in [`conformance.lock`](conformance.lock)) and the API
|
|
176
|
+
spec.
|
|
177
|
+
|
|
178
|
+
Until 1.0.0, a minor release (0.x → 0.y) may change the API. Previews of the
|
|
179
|
+
next release are published from the `dev` branch as `<next>.devN`:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pip install --pre solvapay
|
|
183
|
+
```
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# solvapay
|
|
2
|
+
|
|
3
|
+
SolvaPay for Python: paywalls, usage billing, webhooks and paid MCP tools.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install solvapay # API client, paywall, webhooks
|
|
7
|
+
pip install 'solvapay[mcp]' # paid tools for the official MCP Python SDK
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.10+. Typed (`py.typed`), async-first, with blocking twins for the API client.
|
|
11
|
+
|
|
12
|
+
## Configure
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
export SOLVAPAY_SECRET_KEY=sk_sandbox_... # required
|
|
16
|
+
export SOLVAPAY_PRODUCT_REF=prd_... # the product paid calls bill against
|
|
17
|
+
export SOLVAPAY_API_BASE_URL=... # optional; defaults to https://api.solvapay.com
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run `npx solvapay init` to sign in and write these to `.env`.
|
|
21
|
+
|
|
22
|
+
## Make a function paid
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from solvapay import PaywallError, create_solvapay
|
|
26
|
+
|
|
27
|
+
solvapay = create_solvapay()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@solvapay.payable()
|
|
31
|
+
async def summarise(text: str, *, customer_ref: str) -> str:
|
|
32
|
+
return text[:100]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
summary = await summarise("...", customer_ref="user_123")
|
|
37
|
+
except PaywallError as error:
|
|
38
|
+
print(error.gate["message"]) # what the customer should see, with a checkout link
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`customer_ref` is your own user id: SolvaPay creates the customer on first use.
|
|
42
|
+
Each call is checked against the customer's plan, and billed when it succeeds.
|
|
43
|
+
|
|
44
|
+
## Paid MCP tools
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from solvapay import create_solvapay
|
|
48
|
+
from solvapay.mcp import ResponseContext, create_solvapay_mcp_server
|
|
49
|
+
|
|
50
|
+
mcp = create_solvapay_mcp_server(
|
|
51
|
+
solvapay=create_solvapay(),
|
|
52
|
+
public_base_url="https://research.example.com",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def quote(symbol: str, ctx: ResponseContext):
|
|
57
|
+
return ctx.respond({"symbol": symbol, "price": 101.5})
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
mcp.register_payable("quote", handler=quote, description="Latest price for a symbol.")
|
|
61
|
+
|
|
62
|
+
app = mcp.http_app() # serve with uvicorn; the MCP endpoint is /mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Customers sign in with SolvaPay over OAuth, and every bearer token is verified
|
|
66
|
+
with SolvaPay. A call over the customer's limit returns the paywall: a message
|
|
67
|
+
naming the next step, with a checkout link. See
|
|
68
|
+
[`examples/python-stock-research-mcp`](../examples/python-stock-research-mcp).
|
|
69
|
+
|
|
70
|
+
The server also comes with SolvaPay's built-in tools, as the TypeScript server
|
|
71
|
+
does: `account` shows the customer's plan, balance and usage with a checkout,
|
|
72
|
+
top-up or account link (`view`: `checkout`, `account` or `topup`), and
|
|
73
|
+
`activate_plan` activates a plan by `planRef`. The `/upgrade`,
|
|
74
|
+
`/manage_account`, `/topup` and `/activate_plan` prompts and the
|
|
75
|
+
`docs://solvapay/overview.md` resource come with them. Pass `views=[...]` to
|
|
76
|
+
choose the views, or `views=[]` to leave `account` out.
|
|
77
|
+
|
|
78
|
+
### The checkout widget
|
|
79
|
+
|
|
80
|
+
On hosts that support MCP Apps (ChatGPT, Claude), `account` also opens
|
|
81
|
+
SolvaPay's checkout and account widget in the chat, the same one the TypeScript
|
|
82
|
+
SDK serves. It ships in this package (`solvapay/mcp/mcp-app.html`) and is
|
|
83
|
+
served at `ui://<name>/mcp-app.html` (`resource_uri=` to change it). Text-only
|
|
84
|
+
hosts get the same text as before; `mode: "text"` asks for text on any host.
|
|
85
|
+
|
|
86
|
+
The widget pays, tops up, cancels and reactivates renewals through six tools
|
|
87
|
+
only it calls: `create_payment_intent`, `process_payment`,
|
|
88
|
+
`create_hosted_session`, `set_renewal`, `attach_business_details` and
|
|
89
|
+
`get_history`. They are marked for the app (`_meta.ui.visibility: ["app"]`), so
|
|
90
|
+
MCP Apps hosts keep them from the model. For a host that lists them anyway,
|
|
91
|
+
`hide_tools_by_audience=["ui"]` leaves them out of `tools/list`; ChatGPT still
|
|
92
|
+
gets them, since its widget can only call listed tools.
|
|
93
|
+
|
|
94
|
+
Its Content Security Policy allows Stripe and the SolvaPay API. Add domains with
|
|
95
|
+
`csp={"connectDomains": [...], "resourceDomains": [...], "frameDomains": [...]}`.
|
|
96
|
+
|
|
97
|
+
### Paid by agents
|
|
98
|
+
|
|
99
|
+
`solvapay.agents.mcp.create_agent_mcp_route` serves an MCP server that SolvaPay
|
|
100
|
+
agents pay for per call: each paid tool call is authorized with SolvaPay before
|
|
101
|
+
it runs and completed at the cost the upstream reports. Install
|
|
102
|
+
`solvapay[agents,mcp]`.
|
|
103
|
+
|
|
104
|
+
## Verify webhooks
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from solvapay import SolvaPayError, verify_webhook
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
event = verify_webhook(
|
|
111
|
+
body=raw_body, # bytes or str, exactly as received
|
|
112
|
+
signature=request.headers["sv-signature"],
|
|
113
|
+
secret=os.environ["SOLVAPAY_WEBHOOK_SECRET"],
|
|
114
|
+
)
|
|
115
|
+
except SolvaPayError as error:
|
|
116
|
+
... # error.code: webhook_signature_invalid, webhook_timestamp_out_of_tolerance, ...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## The API
|
|
120
|
+
|
|
121
|
+
Every SolvaPay API operation, generated from the API spec:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from solvapay import AsyncApiClient
|
|
125
|
+
|
|
126
|
+
async with AsyncApiClient(api_key="sk_sandbox_...") as api:
|
|
127
|
+
customer = await api.get_customer(reference="cus_123")
|
|
128
|
+
balance = await api.get_customer_balance(reference="cus_123")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`ApiClient` is the blocking twin. Errors are `SolvaPayError` with a stable `code`
|
|
132
|
+
and the HTTP `status`; branch on those, not on the message.
|
|
133
|
+
|
|
134
|
+
## Same behaviour as the TypeScript SDK
|
|
135
|
+
|
|
136
|
+
The paywall, its messages, the `account` and `activate_plan` narration, what
|
|
137
|
+
the widget's tools answer and webhook verification are checked against the
|
|
138
|
+
TypeScript SDK on every change by a shared conformance suite
|
|
139
|
+
([`conformance/`](../conformance)). The API client is generated from the same
|
|
140
|
+
spec as the TypeScript types ([`openapi/`](../openapi)).
|
|
141
|
+
|
|
142
|
+
## Versions
|
|
143
|
+
|
|
144
|
+
`solvapay` has its own version line, starting at 0.1.0. Its numbers don't
|
|
145
|
+
follow the TypeScript packages: Python 0.3.0 and `@solvapay/server` 2.12.0 can
|
|
146
|
+
behave the same. What the two share is the conformance suite (the version this
|
|
147
|
+
package passes is pinned in [`conformance.lock`](conformance.lock)) and the API
|
|
148
|
+
spec.
|
|
149
|
+
|
|
150
|
+
Until 1.0.0, a minor release (0.x → 0.y) may change the API. Previews of the
|
|
151
|
+
next release are published from the `dev` branch as `<next>.devN`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install --pre solvapay
|
|
155
|
+
```
|