Metadata-Version: 2.4
Name: nextune-geofencing
Version: 0.1.7
Summary: Geofencing middleware and NextUne IP/whoami helpers for Django.
Author: Nextune Solutions
License: Proprietary
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: Django>=4.1
Requires-Dist: requests>=2.0

# nextune-geofencing

Django app paired with the Express package **@nextuneapps/geofencing**: optional **middleware** that attaches location-related attributes to each request, optional **URL routes**, and **`TypedDict` shapes** for the payload structures your project works with.

## Install

```bash
pip install nextune-geofencing
```

Requires Django 4.1+ and Python 3.10+.

## Types (IDE and static checkers)

Import `TypedDict` definitions from the package so views, services, and tests stay aligned with the JSON you handle:

```python
from typing import cast

from nextune_geofencing import (
    ExchangeRatesBlock,
    ExchangeRatesResponse,
    NextuneGeo,
    NextuneGeofencing,
    NextuneLocationPayload,
)


def summarize_location(body: object) -> dict:
    data = cast(NextuneLocationPayload, body)
    geo = cast(NextuneGeo | None, data.get("geo"))
    raw_gf = data.get("geofencing")
    gf = cast(NextuneGeofencing | None, raw_gf if isinstance(raw_gf, dict) else None)
    return {
        "currency": (gf or {}).get("currency") or (geo or {}).get("currency"),
        "country": (gf or {}).get("country_code") or (geo or {}).get("country"),
        "payment_methods": data.get("payment_methods"),
    }


def rates_table(body: object, base_code: str) -> dict[str, float | int] | None:
    payload = cast(ExchangeRatesResponse, body)
    block = cast(ExchangeRatesBlock | None, payload.get(base_code.upper()))
    return block["rates"] if block else None
```

**After middleware** — use the helpers for typed reads of `request`:

```python
from django.http import HttpRequest, JsonResponse

from nextune_geofencing import get_nextune_geofencing, get_nextune_country_code


def checkout(request: HttpRequest):
    gf = get_nextune_geofencing(request)
    return JsonResponse(
        {
            "country": get_nextune_country_code(request),
            "currency": gf.get("currency") if gf else None,
        }
    )
```

**Error bodies** from this app’s views can be annotated as `GeofencingAPIError` when you parse JSON.

## Django setup

```python
INSTALLED_APPS = [
    # ...
    "nextune_geofencing",
]

MIDDLEWARE = [
    # ...
    "nextune_geofencing.middleware.GeofencingMiddleware",
]
```

## Routes (optional)

```python
from django.urls import include, path

urlpatterns = [
    # ...
    path("v1/geo/", include("nextune_geofencing.urls")),
]
```

Under the mount prefix (example above: `v1/geo/`):

- **`GET …/whoami`** — resolves the **requesting client’s IP**, then returns **location JSON** (`NextuneLocationPayload`: `geo`, `geofencing`, `payment_methods`, etc.). Commonly used for “current visitor” context (country, currency, payments).
- `POST …/ip` — JSON body `{"ip": "<address>"}` → the same location JSON shape (`NextuneLocationPayload`).
- `GET …/exchange-rates/<currency>` — three-letter ISO code (e.g. `KES`) → exchange-rate JSON (`ExchangeRatesResponse`).
