Metadata-Version: 2.4
Name: spaps
Version: 0.5.0
Summary: Sweet Potato Authentication & Payment Service Python client
Project-URL: Homepage, https://api.sweetpotato.dev
Project-URL: Repository, https://github.com/sweet-potato/spaps
Author-email: buildooor <buildooor@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Sweet Potato Team
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
License-File: LICENSE
Keywords: authentication,payments,spaps,sweet-potato
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.9
Requires-Dist: httpx<0.29.0,>=0.27.0
Requires-Dist: pydantic<3.0.0,>=2.7.0
Provides-Extra: dev
Requires-Dist: build<2.0.0,>=1.2.1; extra == 'dev'
Requires-Dist: httpx<0.29.0,>=0.27.0; extra == 'dev'
Requires-Dist: mypy<2.0.0,>=1.10.0; extra == 'dev'
Requires-Dist: pydantic<3.0.0,>=2.7.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: respx<0.23.0,>=0.22.0; extra == 'dev'
Requires-Dist: ruff<1.0.0,>=0.5.5; extra == 'dev'
Requires-Dist: tomli<3.0.0,>=2.0.1; extra == 'dev'
Description-Content-Type: text/markdown

# Sweet Potato Python Client

Python SDK for the Sweet Potato Authentication & Payment Service (SPAPS). It packages the common auth, session, payment, secure messaging, issue reporting, webhook, and operational client flows used by SPAPS-backed apps and services.

```bash
pip install spaps
```

## TL;DR

**The Problem**: calling SPAPS directly from Python usually means reimplementing token storage, retry behavior, error parsing, and endpoint wrappers across every service.

**The Solution**: the `spaps` Python distribution gives you sync and async clients, typed response models, retry/logging hooks, and standalone helpers for the parts of the SPAPS API that need narrower integration points.

### Why Use the Python Client?

| Feature | What it gives you |
| --- | --- |
| Sync + async ergonomics | `SpapsClient` and `AsyncSpapsClient` cover the common auth and session workflows |
| Typed payloads | Pydantic models for auth, sessions, payments, entitlements, webhooks, and more |
| Retry and logging hooks | Configurable HTTP retries plus request/response logging helpers |
| Standalone helpers | Device flow, webhooks, permission checks, issue reporting, support telemetry, and specialty clients |

## Metadata

- `package_name`: `spaps`
- `latest_version`: `0.4.1`
- `minimum_runtime`: `Python >=3.9`
- `api_base_url`: `https://api.sweetpotato.dev`

## Quick Example

```python
from spaps_client import SpapsClient

client = SpapsClient(
    base_url="http://localhost:3301",
    api_key="test_key_local_dev_only",
)

tokens = client.auth.sign_in_with_password(
    email="user@example.com",
    password="Secret123!",
)
print(tokens.user.email)

current = client.sessions.get_current_session()
print(current.session_id)

products = client.payments.list_products(category="subscription", active=True, limit=5)
print(products.total)

client.close()
```

## Async Example

```python
import asyncio

from spaps_client import AsyncSpapsClient


async def main() -> None:
    client = AsyncSpapsClient(
        base_url="http://localhost:3301",
        api_key="test_key_local_dev_only",
    )
    try:
        await client.auth.sign_in_with_password(
            email="user@example.com",
            password="Secret123!",
        )
        sessions = await client.sessions.list_sessions()
        print(sessions.total)
    finally:
        await client.aclose()


asyncio.run(main())
```

## Design Principles

1. Keep the high-level client familiar to teams already using the TypeScript SDK.
2. Return typed models instead of raw dicts wherever the response shape is stable.
3. Preserve escape hatches for custom token storage, HTTP transport, retries, and logging.
4. Keep specialty integrations available as standalone helpers instead of forcing everything through one client.

## API Surface

### High-Level Clients

| Client | Best for |
| --- | --- |
| `SpapsClient` | Sync auth, sessions, payments, usage, whitelist, secure messages, issue reporting, metrics, and support telemetry |
| `AsyncSpapsClient` | Async auth, sessions, payments, usage, whitelist, secure messages, issue reporting, metrics, entitlements, dayrate, and users |

### Standalone Helpers

| Helper | Purpose |
| --- | --- |
| `EntitlementsClient` / `AsyncEntitlementsClient` | Resource entitlements, purchase history, manual grants/revokes |
| `EmailClient` / `AsyncEmailClient` | Template listing, preview, and send flows |
| `UsersClient` / `AsyncUsersClient` | Batch user and email lookups |
| `DeviceFlowClient` | Device-code login workflows |
| `PermissionChecker` | Client-side role and admin convenience checks |
| `verify_spaps_webhook` | Signature verification for incoming SPAPS webhooks |

## Configuration

Constructor values override package defaults.

```python
from spaps_client import SpapsClient, RetryConfig, default_logging_hooks

client = SpapsClient(
    base_url="https://api.sweetpotato.dev",
    api_key="spaps_sec_example",
    retry_config=RetryConfig(max_attempts=4, backoff_factor=0.2),
    logging_hooks=default_logging_hooks(),
)
```

Common parameters:

| Parameter | Purpose |
| --- | --- |
| `base_url` | Target SPAPS API origin |
| `api_key` | Application or service API key |
| `request_timeout` | Per-request timeout |
| `token_storage` | Custom token persistence backend |
| `http_client` | Injected `httpx.Client` or `httpx.AsyncClient` |
| `retry_config` | Retry/backoff policy |
| `logging_hooks` | Structured request/response logging callbacks |

## Common Flows

### Magic Links and Password Reset

```python
from spaps_client import SpapsClient

client = SpapsClient(base_url="http://localhost:3301", api_key="test_key_local_dev_only")

client.auth.send_magic_link(email="user@example.com")
client.auth.request_password_reset(email="user@example.com")
client.auth.confirm_password_reset(
    token="reset-token-from-email",
    new_password="Sup3rStrong!",
)

client.close()
```

### Permission Checks

```python
from spaps_client import PermissionChecker

checker = PermissionChecker(customAdmins=["founder@example.com"])
role = checker.getRole("user@example.com")

if checker.requiresAdmin({"email": "user@example.com"}):
    raise PermissionError(
        checker.getErrorMessage("admin", role, action="change billing settings")
    )
```

### Webhook Verification

```python
from spaps_client import verify_spaps_webhook

payload = verify_spaps_webhook(
    body=request_body_bytes,
    signature=request_headers["X-SPAPS-Signature"],
    secret="whsec_example",
)
print(payload.type)
```

## Architecture

```text
Your Python app
  |
  +--> SpapsClient / AsyncSpapsClient
  |       |
  |       +--> auth / sessions / payments / usage / secure messages
  |       +--> issue reporting / metrics / support telemetry
  |
  +--> standalone helpers
          |
          +--> entitlements / users / email / device flow / webhooks
                  |
                  v
               SPAPS HTTP API
```

## Validation and Development

From the repository root:

```bash
npm run lint:python-client
npm run typecheck:python-client
npm run test:python-client
```

For local package setup:

```bash
cd packages/python-client
pip install -e '.[dev]'
```

## Troubleshooting

### `ValueError: Access token not found`

Authenticate first with `client.auth...` helpers, or pre-seed tokens with `set_tokens(...)`.

### `401` or `403` responses

Confirm the API key, token scope, and endpoint role requirements all match the target environment.

### Hosted examples fail against localhost

Set `base_url="http://localhost:3301"` and use a local-development key or the relevant test credentials.

### I need more control over HTTP behavior

Inject a custom `httpx` client or pass `RetryConfig` and logging hooks.

### Which client should I start with?

Use `SpapsClient` unless your app is already async end-to-end.

## Limitations

- Not every SPAPS helper is available on both the sync and async top-level clients.
- Brand-new backend endpoints may require a client release before typed wrappers exist here.
- Client-side permission helpers improve ergonomics but do not replace server-side authorization.

## FAQ

### Is this package only for backend services?

No. It is suitable for any Python consumer of the SPAPS API.

### Does it persist tokens automatically?

Yes, through the configured token storage backend. You can swap the storage implementation if needed.

### Can I use it without the high-level client?

Yes. The package exports narrower clients and helpers for more specialized integrations.

### Is there webhook support?

Yes. Use `verify_spaps_webhook` for SPAPS signature verification.

### Does it support async codebases?

Yes. Use `AsyncSpapsClient` and the async helper clients.

## About Contributions

> *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.

## License

MIT
