Metadata-Version: 2.4
Name: pinelabs-offer-discovery-sdk
Version: 1.0.0
Summary: Pine Labs Online Offer Discovery SDK — affordability offer discovery client for Pinepg (Python)
Author: Pine Labs Online
License: MIT
Keywords: mpp,p3p,offer,affordability,emi,client,pinelabs,discovery
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: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"

# Pine Labs Online Offer Discovery SDK (Python)

Python SDK for Pine Labs Online's affordability/offer discovery API. It calls
Pinepg's `POST /api/affordability/v2/offer/discovery` endpoint in `UAT` or
`PROD` and returns normalized offer data.

## Installation

```bash
pip install pinelabs-offer-discovery-sdk==1.0.0
```

Import module: `pinelabs_offer_discovery`. Requires Python 3.9 or newer.

## Environments

| `env` | Endpoint |
|---|---|
| `OfferDiscoveryEnvironment.UAT` | `https://pluraluat.v2.pinepg.in/api/affordability/v2/offer/discovery` |
| `OfferDiscoveryEnvironment.PROD` | `https://api.pluralpay.in/api/affordability/v2/offer/discovery` |

Environment defaults:

| Env | Timeout | Retries | Initial retry delay |
|---|---:|---:|---:|
| `UAT` | 60000 ms | 3 | 500 ms |
| `PROD` | 45000 ms | 3 | 500 ms |

## Authentication

- Supply `merchantId`, `clientId`, and `clientSecret`. The
  SDK exchanges the credentials at the selected environment's
  `POST /api/auth/v1/token`, caches the token per client instance, refreshes it
  before expiry, and sends `Authorization: Bearer <accessToken>` to offer
  discovery. You can supply a pre-minted `accessToken` instead. `merchantId`
  is mandatory at client creation and has no SDK fallback or per-request
  override.

The SDK always adds `"tenure": {"tenure_id": "7"}` to the Pine Labs
request. Integrators do not need to provide a tenure value.

## Quick Start

```python
import os

from pinelabs_offer_discovery import (
    Amount,
    OfferDiscoveryClient,
    OfferDiscoveryClientConfig,
    OfferDiscoveryEnvironment,
    OfferDiscoveryRequest,
    ProductAmount,
    ProductDetail,
)

client = OfferDiscoveryClient.create(OfferDiscoveryClientConfig(
    env=OfferDiscoveryEnvironment.PROD,
    merchantId=os.environ["OFFER_MERCHANT_ID"],
    clientId=os.environ["OFFER_CLIENT_ID"],
    clientSecret=os.environ["OFFER_CLIENT_SECRET"],
))

response = client.discover_offers(OfferDiscoveryRequest(
    order_amount=Amount(currency="INR", value=14997000),
    product_details=[
        ProductDetail(
            product_code="SAMSUNG_BRAND-3",
            product_amount=ProductAmount(value=14997000, currency="INR"),
        )
    ],
    future_date="2026-09-09",
))

print(response.offers)
client.close()
```

Keep the client instance long-lived so the cached token is reused. A configured
or per-request `accessToken` takes precedence over client-credentials exchange.
Client secrets must only be used in a trusted server-side process.

## Per-request overrides

`OfferDiscoveryOptions` lets you override `Request-ID` and `accessToken` per
call without mutating the shared client config:

```python
from pinelabs_offer_discovery import OfferDiscoveryOptions

response = client.discover_offers(
    request,
    OfferDiscoveryOptions(
        requestId="ORD-7783073127",
        accessToken="<rotated_widget_token>",
    ),
)
```

The SDK auto-generates a UUID `Request-ID` and a `X-Request-Id` when none is
supplied. Curl-style `Request-ID: ARELLI_Port Myrtice` is supported verbatim.

## Response shape

`OfferDiscoveryResponse` exposes:

- `offers`: a list of normalized `Offer` objects with common fields
  (`offer_id`, `tenure_id`, `monthly_installment`, `total_amount`,
  `product_code`, `bank_name`, …).
- `raw`: the full response payload for any extra/forward-compatible fields.

The parser tolerates several response envelopes the discovery API may return:
top-level array, `{"data": [...]}`, `{"offers": [...]}`, or
`{"data": {"offers": [...]}}`.

## Errors

- `OfferDiscoveryError` — non-2xx API responses. Exposes `.code`,
  `.http_status`, and `.details`.
- `OfferDiscoveryNetworkError` — network/timeout errors. Exposes `.cause`.
- `OfferDiscoveryValidationError` — invalid request input raised before any
  HTTP call.

## Context manager

```python
with OfferDiscoveryClient.create(config) as client:
    response = client.discover_offers(request)
```

The SDK closes any internal `httpx.Client` it created; externally supplied
`http_client` instances are left alone.

## License

MIT
