Metadata-Version: 2.4
Name: cosmic-python-sdk
Version: 0.2.0
Summary: Official Python SDK for the Cosmic Gateway payment processing API
Author: Emmanuel Mnanka Samo
License: MIT
Project-URL: Homepage, https://github.com/cosmic-payment/cosmic-gateway
Project-URL: Documentation, https://github.com/cosmic-payment/cosmic-gateway
Project-URL: Source, https://github.com/cosmic-payment/cosmic-gateway
Project-URL: Issues, https://github.com/cosmic-payment/cosmic-gateway/issues
Keywords: payment,gateway,cosmic,api,sdk,payments
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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 :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-mock>=3.0; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"

# Cosmic Python SDK

Official Python client SDK for the [Cosmic Gateway](https://cosmicgateway.com) payment processing API.

## Features

- **Complete payment lifecycle** — charge, capture, refund, and query payments
- **Customer management** — create, retrieve, and list customers
- **Transaction history** — paginated transaction listing with filters
- **Webhook verification** — HMAC-SHA256 signature verification for incoming webhooks
- **Idempotency** — automatic idempotency key generation to safely retry requests
- **Retry with backoff** — automatic retry on 5xx errors and rate limits (429)
- **Request timeout** — configurable timeout per request
- **Environment switching** — built-in sandbox and production endpoints
- **Input validation** — pre-flight validation of amounts, currencies, emails, and more
- **Typed request/response models** — dataclass-based types for all API objects
- **Sensitive data masking** — automatic masking of API keys, PAN, and CVC in logs

## Installation

```bash
pip install cosmic-python-sdk
```

Requires Python 3.8+.

### Development installation

```bash
pip install cosmic-python-sdk[dev]
```

## Quick Start

```python
from cosmic_sdk import CosmicGateway

client = CosmicGateway(
    api_key="sk_test_...",
    environment="sandbox",  # or "production"
)

# Charge a payment
result = client.payments.charge({
    "amount": 1999,        # $19.99 in cents
    "currency": "usd",
    "source": "tok_visa_4242",
    "description": "Test payment",
})

if result["success"]:
    print("Payment ID:", result["transactionId"])
    print("New balance:", result["newBalance"])
else:
    print("Payment failed:", result["status"])
```

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `api_key` | `str` | **required** | Your secret API key |
| `environment` | `str` | `'production'` | API environment: `'sandbox'` or `'production'` |
| `base_url` | `str` | environment default | Custom API base URL (overrides environment) |
| `timeout` | `int` | `30000` | Request timeout in milliseconds |

### Environment URLs

| Environment | URL |
|-------------|-----|
| `sandbox` | `https://api.sandbox.cosmicgateway.com` |
| `production` | `https://api.cosmicgateway.com` |

### Retry Configuration

The SDK retries failed requests with exponential backoff. Configure via `RetryConfig`:

```python
from cosmic_sdk import CosmicGateway
from cosmic_sdk.config import RetryConfig

client = CosmicGateway(
    api_key="sk_test_...",
    environment="sandbox",
)

client.config.retry = RetryConfig(
    max_retries=5,
    base_delay_ms=200,
    max_delay_ms=10000,
)
```

## API Reference

### Payments

#### `client.payments.charge(request)`

Process a payment charge.

```python
result = client.payments.charge({
    "amount": 1999,
    "currency": "usd",
    "source": "tok_abc123",
    "description": "Order #1234",
    "metadata": {"orderId": "ORD-12345"},
    "idempotencyKey": "uuid-v4",  # optional — auto-generated if omitted
})
```

**Success response:**
```python
{
    "success": True,
    "transactionId": "txn_...",
    "referenceId": "ref_...",
    "processor": "stripe",
    "region": "us",
    "amount": 1999,
    "currency": "usd",
    "settlement": {  # present for cross-currency payments
        "from": "usd",
        "to": "kes",
        "originalAmount": 1999,
        "convertedAmount": 289855,
        "rate": 145.0,
    },
    "newBalance": 50000,
    "fraudScore": 0.02,
    "kycVerified": "verified",
}
```

**Failed response:**
```python
{
    "success": False,
    "transactionId": "txn_...",
    "referenceId": None,
    "processor": "stripe",
    "region": "us",
    "status": "failed",
}
```

#### `client.payments.retrieve(payment_id)`

Retrieve payment details by ID.

```python
payment = client.payments.retrieve("pay_abc123")
```

#### `client.payments.list(params=None)`

List payments with optional filtering.

```python
result = client.payments.list({
    "page": 1,
    "limit": 20,
    "status": "SUCCESS",
    "startDate": "2026-01-01T00:00:00Z",
    "endDate": "2026-12-31T23:59:59Z",
})
```

### Refunds

#### `client.refunds.create(request)`

Create a refund for a payment.

```python
result = client.refunds.create({
    "paymentId": "pay_abc123",
    "amount": 500,  # partial refund (optional, defaults to full)
    "reason": "Customer requested refund",
    "metadata": {"ticketId": "TKT-999"},
})
```

#### `client.refunds.retrieve(refund_id)`

```python
refund = client.refunds.retrieve("ref_abc123")
```

#### `client.refunds.list(params=None)`

### Customers

#### `client.customers.create(request)`

```python
customer = client.customers.create({
    "email": "user@example.com",
    "name": "John Doe",
    "phone": "+254700000000",
    "metadata": {"signupSource": "web"},
})
```

#### `client.customers.retrieve(customer_id)`

#### `client.customers.list(params=None)`

### Transactions

#### `client.transactions.list(params=None)`

```python
result = client.transactions.list({
    "page": 1,
    "limit": 20,
})
```

#### `client.transactions.retrieve(transaction_id)`

### Webhook Verification

```python
from cosmic_sdk.webhooks import verify_webhook_signature

payload = request.body  # raw bytes
signature = request.headers.get("X-Cosmic-Signature")  # "sha256=..."
secret = "whsec_..."

event = verify_webhook_signature(payload, signature, secret)
# {"event": "payment.succeeded", "transactionId": "txn_1", ...}
```

The SDK verifies the `sha256=` HMAC signature using **timing-safe comparison** (`hmac.compare_digest`).

Raises `WebhookVerificationError` if the signature is missing, malformed, or doesn't match.

### Health Check

```python
health = client.health()
# {"status": "ok"}
```

### Raw HTTP Methods

The SDK exposes `get()` and `post()` for direct API access if needed:

```python
# Raw GET
resp = client.get("/customers", params={"limit": 10})

# Raw POST
resp = client.post("/customers", json={"email": "user@example.com"})
```

## Error Handling

The SDK raises typed exceptions for all failure modes:

| Exception | HTTP Status | When |
|-----------|-------------|------|
| `ValidationError` | 400 | Invalid input |
| `AuthenticationError` | 401 | Missing or invalid credentials |
| `PaymentFailedError` | 402 | Payment was declined |
| `NotFoundError` | 404 | Resource not found |
| `RateLimitError` | 429 | Too many requests |
| `ServerError` | 500 | Gateway internal error |
| `ServiceUnavailableError` | 503 | Service temporarily down |
| `WebhookVerificationError` | — | Webhook signature invalid |

```python
from cosmic_sdk.errors import PaymentFailedError, ValidationError

try:
    result = client.payments.charge({
        "amount": -1,
        "currency": "usd",
        "source": "tok_abc",
    })
except ValidationError as e:
    print("Bad input:", e)
except PaymentFailedError as e:
    print("Payment declined:", e.transaction_id)
```

## Retry Behavior

- **5xx errors** — retried with exponential backoff (base delay doubles each attempt)
- **429 rate limits** — respects the `Retry-After` header
- **Network errors** — retried as transient failures
- **4xx errors (except 429)** — **not** retried (these are client errors)
- Configurable via `RetryConfig`

## Idempotency

All mutation endpoints (`charge`, `create`, etc.) accept an optional `idempotency_key`. If omitted, a UUID v4 is generated automatically. The gateway uses the `api_key + idempotency_key` pair to detect duplicate requests and return the cached response.

## Sensitive Data Masking

The logger automatically masks sensitive fields (API keys, PAN, CVC, auth tokens) in logs. Set the log level to `DEBUG` to inspect request/response shapes without exposing secrets:

```python
import logging
client._logger = GatewayLogger(level=logging.DEBUG)
```

## Development

```bash
# Clone and install with dev dependencies
pip install -e ".[dev]"

# Run tests
python3 -m pytest tests/

# Run with coverage
python3 -m pytest tests/ --cov=cosmic_sdk

# Build distribution
python3 -m build
```

## License

MIT
