Community Python SDK for SolvaPay (agent-native payment rails)
Project description
solvapay-python
Community Python SDK for SolvaPay — agentic payment rails.
pip install solvapay-python · Python 3.10+ · Fully typed (py.typed) · PyPI
One tool. Three runtimes. One paywall.
@payable_tool stamps a function with payment metadata once. Any framework reads it — no per-framework wiring.
# Define once
from solvapay.adapters.mcp import payable_tool
@payable_tool(product="prd_search")
def web_search(*, customer_ref: str, query: str) -> list[str]:
"""Search the web."""
return do_real_search(query)
# Run on MCP (Claude Desktop / Claude Code)
from solvapay.adapters.mcp import register_payable_tool_fastmcp
from fastmcp import FastMCP
mcp = FastMCP("My App")
register_payable_tool_fastmcp(mcp, web_search)
mcp.run()
# Run on LangChain
from solvapay.adapters.langchain import monetize_tool
paid = monetize_tool(web_search, product="prd_search")
# Blocked callers get {"paywall_required": True, "checkout_url": "..."}
# Run raw async
async with AsyncSolvaPay() as sv:
limits = await sv.limits.acheck(customer_ref="cus_123", product_ref="prd_search")
if limits.within_limits:
result = await web_search(customer_ref="cus_123", query="hello")
pip install 'solvapay-python[mcp]' · pip install 'solvapay-python[langchain]'
Quickstart
pip install solvapay-python
export SOLVAPAY_SECRET_KEY=sk_...
from solvapay import SolvaPay
sv = SolvaPay()
# Ensure customer exists
customer_ref = sv.customers.ensure("user_alice")
# Create checkout session
session = sv.checkout.create_session(customer_ref=customer_ref, product_ref="prd_0QKI8NHF")
print(session.checkout_url)
# Check limits and track usage
limits = sv.limits.check(customer_ref=customer_ref, product_ref="prd_0QKI8NHF")
if limits.within_limits:
sv.usage.track(
customer_ref=customer_ref,
product_ref="prd_0QKI8NHF",
meter_name="requests",
units=1.0,
idempotency_key="req_abc123", # idempotent — safe to retry
)
Stable API
| Namespace | Sync | Async |
|---|---|---|
sv.customers |
ensure, get, update, balance |
aensure, aget, aupdate, abalance |
sv.checkout |
create_session |
acreate_session |
sv.limits |
check |
acheck |
sv.purchases |
cancel, reactivate |
acancel, areactivate |
sv.usage |
track |
atrack |
sv.products |
list, get, create, delete, clone |
a prefix on each |
sv.plans |
list, create, update, delete |
a prefix on each |
sv.merchant |
get, get_platform_config |
aget, aget_platform_config |
Idempotency
All mutating ops accept idempotency_key. Use solvapay.idempotency.from_payload to derive deterministic keys from request content:
from solvapay.idempotency import from_payload
key = from_payload("track_usage", customer_ref, product_ref, "requests", units)
sv.usage.track(..., idempotency_key=key) # retry-safe
Retried POSTs must reuse the same key. Key should change only when the logical request changes.
Errors and retries
from solvapay import (
AuthenticationError, PermissionError, NotFoundError,
RateLimitError, InvalidRequestError, APIServerError,
APIConnectionError, APITimeoutError, SolvaPayError,
)
try:
sv.customers.ensure("cus_123")
except AuthenticationError:
... # 401 — bad key
except RateLimitError as e:
... # 429 — retry after e.retry_after seconds
except APIConnectionError:
... # network failure
except APITimeoutError:
... # request timed out (default 30s)
except SolvaPayError as e:
... # catch-all
No built-in retries by design. Layer tenacity manually. solvapay[retry] RetryTransport ships in v0.9.
Webhooks
import os
from solvapay.webhooks import WebhookPipeline
pipeline = WebhookPipeline(
[os.environ["SOLVAPAY_WEBHOOK_SECRET"]],
max_clock_skew_seconds=300,
replay_ttl_seconds=600,
)
envelope = pipeline.process(body=request.body, signature=request.headers["sv-signature"])
Typed events — discriminated union over 13 event types:
from solvapay import WebhookEvent, PurchaseCreated, PaymentSucceeded
from pydantic import TypeAdapter
event = TypeAdapter(WebhookEvent).validate_python(envelope.event)
match event:
case PurchaseCreated():
print(f"New purchase: {event.data['purchaseRef']}")
case PaymentSucceeded():
print(f"Payment: {event.data['amount']}")
Paywall decorator
from solvapay.paywall import require, PaywallRequired
@require(product="prd_0QKI8NHF", client=sv)
def run_query(*, customer_ref: str, query: str) -> str:
return expensive_query(query)
try:
result = run_query(customer_ref="cus_123", query="hello")
except PaywallRequired as e:
print(f"Upgrade at: {e.checkout_url}")
if e.checkout_mint_error:
print(f"Could not auto-mint checkout URL: {e.checkout_mint_error}")
Async version: @require_async. For MCP/LangChain: @payable_tool (see above).
Async
AsyncSolvaPay is the supported async pattern. Always use async with — it guarantees aclose():
from solvapay import AsyncSolvaPay
async with AsyncSolvaPay() as sv:
customer_ref = await sv.customers.aensure("user_alice")
limits = await sv.limits.acheck(customer_ref=customer_ref, product_ref="prd_0QKI8NHF")
if limits.within_limits:
await sv.usage.atrack(
customer_ref=customer_ref,
product_ref="prd_0QKI8NHF",
meter_name="requests",
units=1.0,
)
Migrating from v0.7.x
Flat methods still work but emit DeprecationWarning. Removed in v2.0.
| v0.7.x | v0.8+ |
|---|---|
sv.ensure_customer(ref) |
sv.customers.ensure(ref) |
sv.get_customer(ref) |
sv.customers.get(ref) |
sv.check_limits(...) |
sv.limits.check(...) |
sv.track_usage(...) |
sv.usage.track(...) |
sv.create_checkout_session(...) |
sv.checkout.create_session(...) |
sv.cancel_purchase(ref) |
sv.purchases.cancel(ref) |
sv.reactivate_purchase(ref) |
sv.purchases.reactivate(ref) |
sv.get_customer_balance(ref) |
sv.customers.balance(ref) |
sv.list_products() |
sv.products.list() |
sv.create_plan(prd, ...) |
sv.plans.create(prd, ...) |
sv.get_merchant() |
sv.merchant.get() |
Installation
pip install solvapay-python # core
pip install 'solvapay-python[mcp]' # + FastMCP adapter (FastMCP ≥0.4)
pip install 'solvapay-python[langchain]' # + LangChain adapter (langchain-core ≥0.3)
pip install 'solvapay-python[fastapi]' # + FastAPI webhook router
Environment variables
| Variable | Required | Default |
|---|---|---|
SOLVAPAY_SECRET_KEY |
Yes | — |
SOLVAPAY_API_BASE_URL |
No | https://api.solvapay.com |
SOLVAPAY_WEBHOOK_SECRET |
For webhooks | — |
Examples
| Path | What |
|---|---|
examples/multi-framework-paywall/ |
One @payable_tool → FastMCP + LangChain + raw async |
examples/marketplace/ |
Streamlit AI-agent marketplace — real SolvaPay sandbox + Gemini LLM |
examples/fastmcp-paywall/ |
FastMCP server gated by @paywall.require |
examples/langchain-paywall/ |
LangChain agent with monetize_tool |
Roadmap
| Version | Theme |
|---|---|
| v0.8 ✅ | V1 architecture spine — Transport kernel, OpSpec registry, paywall/webhook packages, @payable_tool, stability manifest, layer DAG CI gate |
| v0.9 | Production polish — API-version pinning, idempotency TTL, RetryTransport, RecordingTransport, contract tests, lint automation, doc site (MkDocs Material), supply-chain hygiene |
| v1.0 | Gated on founder signal — OpenAPI-generated models, full secret rotation, production hardening |
Status
v0.8.0 — V1 architecture spine + AI-agent moat. mypy --strict clean (43 files). 261 tests. 89% line coverage.
Community SDK, not official. Proposal: solvapay/solvapay-sdk#187.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file solvapay_python-0.9.0.tar.gz.
File metadata
- Download URL: solvapay_python-0.9.0.tar.gz
- Upload date:
- Size: 721.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fee40f01896e5a1f1c5fcb908e555af3006cbd0fc6aa4cc910624992bd3a88f
|
|
| MD5 |
2a7f69086c5b39e3a5b73e5b55776a22
|
|
| BLAKE2b-256 |
d0d1bee4c67484958b30c2ff3fbb4af1a8144dd4b8565201b62fd8c1b5260e82
|
Provenance
The following attestation bundles were made for solvapay_python-0.9.0.tar.gz:
Publisher:
publish.yml on dhruv-sanan/solvapay-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
solvapay_python-0.9.0.tar.gz -
Subject digest:
8fee40f01896e5a1f1c5fcb908e555af3006cbd0fc6aa4cc910624992bd3a88f - Sigstore transparency entry: 1614931315
- Sigstore integration time:
-
Permalink:
dhruv-sanan/solvapay-python@abcc50e1d82f0df9d51bfd99ab2961fddc96d59d -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/dhruv-sanan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@abcc50e1d82f0df9d51bfd99ab2961fddc96d59d -
Trigger Event:
push
-
Statement type:
File details
Details for the file solvapay_python-0.9.0-py3-none-any.whl.
File metadata
- Download URL: solvapay_python-0.9.0-py3-none-any.whl
- Upload date:
- Size: 53.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6601f64ead1adcf5f5088ce47493c2c27c7b07c71f43d9fafb63bca79aed05f
|
|
| MD5 |
ebb4021bb43d1f166004465c35e189ec
|
|
| BLAKE2b-256 |
385109275aeacc91ea8f1e2c602e7159c77b890f212b7283af61c88491b39880
|
Provenance
The following attestation bundles were made for solvapay_python-0.9.0-py3-none-any.whl:
Publisher:
publish.yml on dhruv-sanan/solvapay-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
solvapay_python-0.9.0-py3-none-any.whl -
Subject digest:
e6601f64ead1adcf5f5088ce47493c2c27c7b07c71f43d9fafb63bca79aed05f - Sigstore transparency entry: 1614931344
- Sigstore integration time:
-
Permalink:
dhruv-sanan/solvapay-python@abcc50e1d82f0df9d51bfd99ab2961fddc96d59d -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/dhruv-sanan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@abcc50e1d82f0df9d51bfd99ab2961fddc96d59d -
Trigger Event:
push
-
Statement type: