# Python SDK

Official Python client with retries, idempotency and webhook verification


The official Python SDK wraps the same REST API documented here. It has **no
third-party dependencies** (standard library only) and adds retries, idempotency
keys, auto-pagination and webhook signature verification.

> **Status:** the `southbill` package is not on PyPI yet. Until the first release
> lands you can install it from the repository, or use the plain REST examples in
> the other articles.

## Install

```bash
pip install southbill
```

Requires Python 3.8 or newer.

## Create a checkout session

```python
from southbill import Southbill

southbill = Southbill()  # reads SOUTHBILL_API_KEY

session = southbill.checkout.sessions.create(
    amount=4900,
    currency="EUR",
    customer_email="ada@acme.com",
    success_url="https://acme.com/thanks",
)

print(session["checkout_url"])
```

All merchant API keys are live keys (`sk_live_...`). Southbill has no test mode.
is `False` for them.

## Resources

| Namespace | Methods |
| --- | --- |
| `checkout.sessions` | `create`, `retrieve`, `list`, `expire` |
| `customers` | `create`, `retrieve`, `update`, `list`, `delete` |
| `invoices` | `create`, `retrieve`, `update`, `list`, `send`, `void`, `mark_paid` |
| `products` | `create`, `retrieve`, `update`, `list` |
| `payments` | `retrieve`, `list` |
| `refunds` | `create` |
| `subscriptions` | `create`, `retrieve`, `list`, `cancel` |
| `events` | `retrieve`, `list`, `replay` |

Payouts, bank details, KYC and API-key management stay merchant-controlled in the
dashboard and are intentionally not part of the API surface.

## Idempotency

Every `POST` sends an `Idempotency-Key` header (random UUID). Pass your own for
safe retries across processes:

```python
southbill.invoices.create(idempotency_key=f"inv-{order_id}", customer="cus_123")
```

## Pagination

```python
for invoice in southbill.invoices.auto_paging_iter(status="open"):
    print(invoice["id"])
```

## Errors and retries

Network errors, `429` and `5xx` are retried twice with exponential backoff
(configurable via `max_retries`). Everything else raises `SouthbillError`:

```python
from southbill import SouthbillError

try:
    southbill.refunds.create(payment="pi_123", amount=500)
except SouthbillError as error:
    print(error.status, error.type, error.param, error.request_id)
```

## Webhooks

Verify the raw request body — never a re-serialized object.

```python
import os
from flask import Flask, request
from southbill import construct_event, SouthbillSignatureError

app = Flask(__name__)

@app.post("/webhooks/southbill")
def webhook():
    try:
        event = construct_event(
            payload=request.get_data(),
            signature=request.headers.get("Southbill-Signature", ""),
            secret=os.environ["SOUTHBILL_WEBHOOK_SECRET"],
        )
    except SouthbillSignatureError:
        return "", 400

    if event["type"] == "invoice.paid":
        pass  # handle it

    return "", 200
```

Signature scheme: `Southbill-Signature: t=<unix seconds>,v1=<hex>` where the hex
digest is `HMAC-SHA256(secret, "<timestamp>.<raw body>")`. Default clock tolerance
is 300 seconds.

## Configuration

```python
Southbill(
    api_key=os.environ["SOUTHBILL_API_KEY"],
    base_url="https://api.southbill.com",
    timeout=30.0,
    max_retries=2,
)
```

