Metadata-Version: 2.4
Name: truetrial
Version: 1.0.0
Summary: Official Python SDK for the TrueTrial API
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# TrueTrial Python SDK

Official Python client for the TrueTrial API. Manage trial periods, warranties, subscriptions, and compliance-first order lifecycle tracking.

## Requirements

- Python 3.10 or later
- An active TrueTrial account with an API key

## Installation

```bash
pip install truetrial
```

Or install from source:

```bash
pip install -e /path/to/sdks/python
```

## Quick Start

### Synchronous Usage

```python
from truetrial import TrueTrialClient

client = TrueTrialClient(api_key="tt_live_your_key_here")

# List orders
orders = client.orders.list()

# Create an order
order = client.orders.create(
    external_order_id="EXT-12345",
    consumer_email="customer@example.com",
    product_type="physical",
    price_cents=4999,
    currency="USD",
)

# Get a specific order
order = client.orders.get("01JARW5X...")

# Close when done
client.close()
```

Use a context manager to handle cleanup automatically:

```python
with TrueTrialClient(api_key="tt_live_your_key_here") as client:
    orders = client.orders.list(filters={"status": "delivered"})
```

### Asynchronous Usage

```python
import asyncio
from truetrial import AsyncTrueTrialClient

async def main():
    async with AsyncTrueTrialClient(api_key="tt_live_your_key_here") as client:
        orders = await client.orders.list()
        order = await client.orders.get("01JARW5X...")

asyncio.run(main())
```

## API Reference

### Client Initialization

```python
TrueTrialClient(
    api_key="tt_live_...",                          # Required
    base_url="https://truetrial.test/api/v1",       # Optional, defaults shown
)
```

### Orders

```python
client.orders.list(filters={"status": "delivered"})
client.orders.create(
    external_order_id="EXT-123",
    consumer_email="user@example.com",
    product_type="physical",
    price_cents=2999,
)
client.orders.get("order_id")
client.orders.status("order_id")
```

### Shipments

```python
client.shipments.create(
    "order_id",
    carrier="ups",
    tracking_number="1Z999AA10123456784",
)
client.shipments.list("order_id")
```

### Digital Delivery

```python
client.digital_delivery.confirm(
    "order_id",
    delivered_at="2026-01-15T10:30:00Z",
)
```

### Temporal Elements

Manage trials, evaluations, subscriptions, warranties, and guarantees:

```python
client.temporal.get("order_id")
client.temporal.extend("order_id", days=7, reason="Customer request")
client.temporal.adjust("order_id", expires_at="2026-03-01T00:00:00Z")
client.temporal.claim("order_id", reason="Product defective", description="...")
client.temporal.resolve_claim("order_id", resolution="approved")
```

### Cancellations

```python
client.cancellations.create("order_id", reason="Changed mind")
client.cancellations.get("order_id")
```

### Webhooks

```python
client.webhooks.list()
client.webhooks.create(
    url="https://example.com/webhooks/truetrial",
    events=["order.delivered", "trial.expired"],
)
client.webhooks.delete("webhook_id")
```

### System

```python
health = client.system.carrier_health()
```

## Enums

All API enum values are available as Python `StrEnum` classes for type safety:

```python
from truetrial import (
    OrderStatus,
    TemporalType,
    TemporalStatus,
    ShipmentStatus,
    ProductType,
    Carrier,
    DurationUnit,
    DeliverySource,
    WebhookEvent,
)

# Use in comparisons
if order.status == OrderStatus.Delivered:
    print("Order has been delivered")

# Use when creating resources
client.shipments.create(
    "order_id",
    carrier=Carrier.Ups,
    tracking_number="1Z...",
)
```

## Error Handling

All API errors are raised as typed exceptions:

```python
from truetrial import (
    TrueTrialError,
    AuthenticationError,
    ValidationError,
    NotFoundError,
    RateLimitError,
    ServerError,
)

try:
    order = client.orders.get("nonexistent_id")
except NotFoundError as e:
    print(f"Not found: {e}")
    print(f"Status code: {e.status_code}")  # 404
except ValidationError as e:
    print(f"Validation failed: {e}")
    print(f"Field errors: {e.errors}")  # {"field": ["error message"]}
except AuthenticationError:
    print("Check your API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except ServerError:
    print("TrueTrial API is having issues")
except TrueTrialError as e:
    print(f"Unexpected API error: {e} (HTTP {e.status_code})")
```

## Webhook Verification

Verify incoming webhook signatures using HMAC SHA-256:

```python
from truetrial import verify_webhook, parse_webhook
from truetrial.webhook import WebhookVerificationError

# Verify only (returns True or raises WebhookVerificationError)
try:
    verify_webhook(
        payload=request.body,
        signature=request.headers["X-TrueTrial-Signature"],
        secret="whsec_your_webhook_secret",
        tolerance=300,  # Optional: reject webhooks older than 5 minutes
        timestamp=request.headers["X-TrueTrial-Timestamp"],
    )
except WebhookVerificationError as e:
    print(f"Invalid webhook: {e}")

# Verify and parse in one step
try:
    event = parse_webhook(
        payload=request.body,
        signature=request.headers["X-TrueTrial-Signature"],
        secret="whsec_your_webhook_secret",
        tolerance=300,
        timestamp=request.headers["X-TrueTrial-Timestamp"],
    )
    print(f"Received event: {event['event']}")
    print(f"Event data: {event['data']}")
except WebhookVerificationError:
    return "Invalid signature", 403
```

### Flask Example

```python
from flask import Flask, request, jsonify
from truetrial import parse_webhook
from truetrial.webhook import WebhookVerificationError

app = Flask(__name__)

@app.route("/webhooks/truetrial", methods=["POST"])
def handle_webhook():
    try:
        event = parse_webhook(
            payload=request.get_data(),
            signature=request.headers.get("X-TrueTrial-Signature", ""),
            secret="whsec_your_webhook_secret",
            tolerance=300,
            timestamp=request.headers.get("X-TrueTrial-Timestamp", "0"),
        )
    except WebhookVerificationError:
        return jsonify({"error": "Invalid signature"}), 403

    if event["event"] == "order.delivered":
        handle_delivery(event["data"])
    elif event["event"] == "trial.expired":
        handle_trial_expiry(event["data"])

    return jsonify({"received": True}), 200
```

## Development

Install development dependencies:

```bash
pip install -e ".[dev]"
```

Run tests:

```bash
pytest
```

## License

MIT
