Metadata-Version: 2.4
Name: tensorrail
Version: 0.1.0
Summary: TensorRail Payments SDK for Python — typed server-side client for payments, refunds, customers, and webhook signature verification.
Project-URL: Homepage, https://www.tensorrail.com
Project-URL: Documentation, https://docs.tensorrail.com
Project-URL: Source, https://github.com/tensorrail/python-sdk
License-Expression: MIT
License-File: LICENSE
Keywords: payment-orchestration,payments,refunds,tensorrail,webhooks
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# tensorrail

TensorRail Payments SDK for Python (3.9+). A typed, server-side client for the
TensorRail API.

- Base URL: `https://api.tensorrail.com`
- Auth: the `api-key` request header (your merchant API key)
- **Amounts are in minor units** (the lowest denomination of the currency),
  e.g. `6540` for $65.40 USD.
- Built-in idempotency keys, retries with backoff, and typed errors.

## Installation

```bash
pip install tensorrail
```

## Quick Start

```python
from tensorrail import TensorRail

client = TensorRail("your-api-key")  # base_url defaults to https://api.tensorrail.com

# Create a payment — amount is in MINOR units (cents): 4999 = $49.99
payment = client.payments.create(
    amount=4999,
    currency="USD",
    payment_method="card",
    payment_method_data={
        "card_number": "4242424242424242",
        "exp_month": 12,
        "exp_year": 2027,
        "cvc": "123",
    },
    idempotency_key="order-abc-123",
)
print(payment.payment_id, payment.status)

# Retrieve a payment
fetched = client.payments.retrieve(payment.payment_id)

# Confirm / capture / cancel
confirmed = client.payments.confirm(payment.payment_id)
captured = client.payments.capture(payment.payment_id, amount_to_capture=4999)
client.payments.cancel(payment.payment_id)
```

## Refunds

```python
# Refund a payment — amount in MINOR units (omit to refund the full amount).
refund = client.refunds.create(
    payment.payment_id,
    amount=1000,  # $10.00
    reason="requested_by_customer",
    idempotency_key="refund-abc-123",
)
print(refund.refund_id, refund.status)

fetched_refund = client.refunds.retrieve(refund.refund_id)
```

## Customers

```python
customer = client.customers.create(name="Ada Lovelace", email="ada@example.com")
fetched_customer = client.customers.retrieve(customer.customer_id)
```

## Context Manager

```python
with TensorRail("your-api-key") as client:
    payment = client.payments.create(amount=999, currency="EUR")  # €9.99
```

## Webhook Signature Verification

TensorRail signs webhook deliveries with the `TensorRail-Signature` header
(HMAC-SHA512). Always verify the signature against the **raw** request body
before trusting an event — do not re-serialize the parsed JSON first.

```python
from flask import Flask, request, abort
from tensorrail import TensorRail, WebhookVerificationError

client = TensorRail("your-api-key")
app = Flask(__name__)

@app.post("/webhooks/tensorrail")
def handle_webhook():
    signature = request.headers.get("TensorRail-Signature", "")
    try:
        event = client.webhooks.construct_event(
            request.get_data(),  # raw bytes
            signature,
            secret="your-webhook-secret",
        )
    except WebhookVerificationError:
        abort(400)
    # event is the verified, parsed payload
    print("verified event:", event)
    return "", 200
```

## Error Handling

```python
from tensorrail import (
    TensorRail,
    TensorRailError,
    AuthenticationError,
    ValidationError,
    RateLimitError,
)

try:
    client.payments.create(amount=-1, currency="USD")
except ValidationError as e:
    print(f"Invalid request: {e} (code={e.code})")
except AuthenticationError as e:
    print(f"Auth failed: {e} (HTTP {e.status_code})")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except TensorRailError as e:
    print(f"API error: {e} (HTTP {e.status_code}, request_id={e.request_id})")
```

## License

MIT
