Metadata-Version: 2.4
Name: enfuce-nextgen-sdk
Version: 0.0.9
Summary: Enfuce nextgen client SDK (Python). One sub-package per API under enfuce_nextgen.
Author: Enfuce
License-Expression: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: urllib3<3.0.0,>=2.1.0
Requires-Dist: python_dateutil>=2.8.2
Requires-Dist: pydantic>=2.11
Requires-Dist: typing-extensions>=4.7.1

# enfuce-nextgen-sdk

Enfuce nextgen client SDK for Python. Each API is an isolated sub-package under
`enfuce_nextgen`, so identically-named models across APIs never collide.

## Installation

```bash
pip install enfuce-nextgen-sdk
```

## Usage

Every API module ships a fluent `<Module>Client` (e.g. `CardClient`, `ExchangeRateClient`) that
builds the `ApiClient`, installs OAuth, applies the host, and exposes each API. Every request is
authenticated with an OAuth2 `client_credentials` bearer token:

```python
from enfuce_nextgen.oauth import client_credentials
from enfuce_nextgen.config import TenantEnvironment, issuer_base_url, token_url
from enfuce_nextgen.card import CardClient

# Identify the host by tenant + environment; the config helper derives every URL from it.
te = TenantEnvironment("<tenant>", "eu.live.prod")

# One token manager (cached client-credentials grant) — reuse it across every module.
clientCredentialsManager = client_credentials(
    token_url(te),
    "<client-id>", "<client-secret>", scopes=["issuer/cardholder.read"],
)

client = (
    CardClient.builder()
    .base_url(issuer_base_url(te))
    .oauth(clientCredentialsManager)                                # token on every request + 401 retry
    .configure(lambda c: setattr(c, "proxy", "http://proxy:8080")) # optional: TLS, retries, pool …
    .build()
)

card = client.get_card_api().get_card(card_id, x_audit_user="you@example.com")
```

`.oauth(clientCredentialsManager)` attaches the token at the transport layer, so it works uniformly
for every module — including ones whose spec declares no security scheme (e.g. `ExchangeRateClient`).
See [Client customization](#client-customization) for transport tuning (timeouts, proxy, TLS, pool).

Keeping the token URL separate from the client identity? Pass a `ClientCredentials` — e.g. with the
`config` helper deriving the URL:
```python
from enfuce_nextgen.oauth import ClientCredentials, client_credentials
from enfuce_nextgen.config import TenantEnvironment, token_url

te = TenantEnvironment("<tenant>", "eu.live.prod")
clientCredentialsManager = client_credentials(
    token_url(te),
    ClientCredentials("<client-id>", "<client-secret>", ["issuer/cardholder.read"]),
)
```

### Inspecting scopes

`available_scopes(...)` discovers the scopes the client is entitled to — it requests a token with no
scope narrowing and returns the granted scopes (sorted, de-duplicated). `scopes_of(...)` decodes the
granted scopes from any JWT you already hold. Both return an empty list for an opaque (non-JWT) token
and never raise.

```python
from enfuce_nextgen.oauth import available_scopes, scopes_of
from enfuce_nextgen.config import token_url

# The client's full entitlement (a live, uncached token request):
available = available_scopes(token_url(te), "<client-id>", "<client-secret>")

# Or decode the scopes granted on a token you already hold:
granted = scopes_of(clientCredentialsManager.get_token())
```

## Client customization

`.configure(lambda c: …)` on any `<Module>Client` builder hands you the underlying `Configuration`
for transport settings — proxy, TLS/SSL, retries, connection pool, etc. It composes with `.oauth(...)`.

```python
client = (
    CardClient.builder()
    .base_url(issuer_base_url(te))
    .oauth(clientCredentialsManager)
    .configure(lambda c: setattr(c, "proxy", "http://proxy:8080"))
    .build()
)
```

Prefer the lower level? `enfuce_nextgen.oauth.oauth_configuration` / `oauth_api_client` build an
OAuth-enabled `Configuration` / `ApiClient` you can pass to an API class directly.

### Configuring timeouts

Timeouts are **per request** in the Python client (not a `Configuration` setting) — pass
`_request_timeout` (a total in seconds, or a `(connect, read)` tuple) on any call:

```python
client.get_card_api().get_card(card_id, x_audit_user="you@example.com", _request_timeout=10)
client.get_card_api().get_card(card_id, x_audit_user="you@example.com", _request_timeout=(3, 10))
```

## API modules

Each module builds the same way — pick the base URL for the module's host
(`issuer_base_url`, `processor_base_url`, or `exchange_rate_base_url`) and reuse the shared
`clientCredentialsManager`. The snippets below assume `te` and `clientCredentialsManager` are in
scope.

### `cardholder` — issuer host

```python
from enfuce_nextgen.config import issuer_base_url
from enfuce_nextgen.cardholder import CardholderClient

client = CardholderClient.builder().base_url(issuer_base_url(te)).oauth(clientCredentialsManager).build()

holder = client.get_cardholder_api().get_cardholder_by_id(cardholder_id, x_audit_user="you@example.com")
cards = client.get_cards_by_cardholder_id_api().get_cards_by_cardholder_id(cardholder_id, x_audit_user="you@example.com")
```

### `card` — issuer host

```python
from enfuce_nextgen.config import issuer_base_url
from enfuce_nextgen.card import CardClient

client = CardClient.builder().base_url(issuer_base_url(te)).oauth(clientCredentialsManager).build()

card = client.get_card_api().get_card(card_id, x_audit_user="you@example.com")
client.update_card_api().activate_card(card_id, x_audit_user="you@example.com")
```

### `wallet` — issuer host

```python
from enfuce_nextgen.config import issuer_base_url
from enfuce_nextgen.wallet import WalletClient

client = WalletClient.builder().base_url(issuer_base_url(te)).oauth(clientCredentialsManager).build()

tokens = client.get_tokens_api().get_tokens(card_id, x_audit_user="you@example.com")
```

### `pin` — issuer host

```python
from enfuce_nextgen.config import issuer_base_url
from enfuce_nextgen.pin import PinClient

client = PinClient.builder().base_url(issuer_base_url(te)).oauth(clientCredentialsManager).build()

view = client.pin_operations_using_pki_api().view_pin(view_pin_request_body, x_audit_user="you@example.com")
```

### `exchange_rate` — exchange-rate host

```python
from enfuce_nextgen.config import exchange_rate_base_url
from enfuce_nextgen.exchange_rate import ExchangeRateClient

client = ExchangeRateClient.builder().base_url(exchange_rate_base_url(te)).oauth(clientCredentialsManager).build()

currencies = client.get_ecb_supported_currencies_api().get_ecb_supported_currencies_v1()
rate = client.get_ecb_exchange_rate_api().get_ecb_rate_v1("EUR", "USD")
```

### `threeds` — processor host

```python
from enfuce_nextgen.config import processor_base_url
from enfuce_nextgen.threeds import ThreedsClient

client = ThreedsClient.builder().base_url(processor_base_url(te)).oauth(clientCredentialsManager).build()

client.three_ds_api().handle_authentication_challenge_result(challenge_result_body)
```

### `cards` — processor host

```python
from enfuce_nextgen.config import processor_base_url
from enfuce_nextgen.cards import CardsClient

client = CardsClient.builder().base_url(processor_base_url(te)).oauth(clientCredentialsManager).build()

client.cards_api().reset_pin_counter(card_id, sequence_number)
```

## Webhooks (model-only modules)

Some APIs describe payloads Enfuce sends **to you** — you implement the endpoint, Enfuce POSTs to it.
These specs are shipped **model-only**: the sub-package contains just the payload models (no
`<Module>Client`, no API classes). Use the pydantic models to parse the inbound request body
(`from_json` / `from_dict`):

```python
from enfuce_nextgen.issuer_events import CardEvent

event = CardEvent.from_json(payload)
```

`authorisation_control` is synchronous — Enfuce expects an approve/decline decision in the response,
so you both consume a model and return one:

```python
from enfuce_nextgen.authorisation_control import AuthRequestBody, AuthResponseBody, AuthResponseCode

request = AuthRequestBody.from_json(payload)
decision = AuthResponseBody(auth_response_code=AuthResponseCode.APPROVED)
```

| Module | Payload model(s) |
| --- | --- |
| `enfuce_nextgen.issuer_events` | `CardEvent`, `CardholderEvent`, `TokenEvent` |
| `enfuce_nextgen.authorisation_control` | `AuthRequestBody` → `AuthResponseBody` (synchronous decision) |
| `enfuce_nextgen.threeds_oob` | `InitiateAuthenticationChallengeBody` |
| `enfuce_nextgen.transaction_event` | `TransactionEvent` |

> A runnable FastAPI example of all of the above (both the API modules and the webhook receivers)
> lives in [`examples/backends/python`](../../examples/backends/python).

## File Parsing (model-only module)

Some specs describe a **file** Enfuce produces for you to ingest (not an HTTP API). These are shipped
**model-only** too — the sub-package contains just the models, and you parse the file's JSON into
them. `clearing_file_copy` is a clearing (settlement) file: a `FileData` header plus a list of records.

```python
from enfuce_nextgen.clearing_file_copy import FileData

file = FileData.from_json(clearing_file_copy_json)
for record in file.records or []:
    ...
```

| Module | Model(s) |
| --- | --- |
| `enfuce_nextgen.clearing_file_copy` | `FileData` (clearing file header + records) |

## Requirements

- Python `>=3.9`
- pydantic `>=2`

## License

MIT
