Metadata-Version: 2.4
Name: bank-slip-verifier
Version: 1.0.0
Summary: Zero-dependency client for the OIIO Service Slip Verify API — verify Thai bank transfer slips by image (OCR) or QR code data with pip, uv, poetry or any Python runtime
Author: ByteInDev
License-Expression: MIT
Project-URL: Homepage, https://github.com/ByteInDev/bank-slip-verifier-pip
Project-URL: Slip Verify API, https://slip-c.oiio.download
Keywords: bank-slip,slip,verify,verification,thailand,promptpay,qrcode,ocr,api-client,kbank,scb
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.9; extra == "dev"
Dynamic: license-file

<br>

<div align="center">

# Bank-Slip-Verifier (pip)

**Zero-dependency client for the OIIO Service Slip Verify API** — verify Thai bank transfer slips with pip, uv, poetry or any Python 3.10+ runtime

![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)
![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)
![Zero dependencies](https://img.shields.io/badge/dependencies-0-6DA55F)
![Stdlib only](https://img.shields.io/badge/standard%20library-only-2B6CB0)

**English** - [Thai](README.th.md)

</div>

---

Three verification modes, one client — verify that a "paid" slip image or QR payload really moved money in the bank system:

| Mode | Endpoint | Speed |
| --- | --- | --- |
| `detect_amount(img)` | `POST /api/slip` | Slowest — reads the amount from the image with OCR, then verifies it |
| `verify_amount(img, amount)` | `POST /api/slip/:amount` | Faster — you give the expected amount, OCR is skipped |
| `verify_qr_code(qr_code_data, amount)` | `POST /api/slip/:amount/no_slip` | Fastest — QR payload only, no image |

## Features

| Ability | Details |
| ------- | ------- |
| Three endpoints | OCR amount detection, image + expected amount, and QR-only verification — same typed result for all |
| Static API | `from bank_slip_verifier import Client` — no construction, `Client.verify_amount(...)` just works against the hosted deployment |
| Zero dependencies | pure standard library (`urllib`), no install scripts, no native binaries |
| Typed errors | `SlipApiError` carries the HTTP status, the documented slug (`amount-not-verified`, `slip-not-found`, ...) and the raw envelope, plus `SlipTimeoutError` and `SlipError` |
| Timeout-aware | defaults to a 35 s HTTP timeout — verification can take up to 25 s (OCR) |
| npm parity | same endpoints, payloads, error slugs and messages as the npm SDK (`@byteindev/bank-slip-verifier`) |

## Quick Start

Install with any package manager:

```bash
pip install bank-slip-verifier
# or
uv add bank-slip-verifier
# or
poetry add bank-slip-verifier
```

`Client` is a ready-to-use static wrapper around the hosted deployment — no setup needed:

```python
from bank_slip_verifier import Client

# 1. Verify a QR payload only, no image (fastest):
result = Client.verify_qr_code(qr_code_data="004...", amount=100)
print(result.data.ref)  # bank transaction reference

# 2. Verify a slip image against a known amount (recommended — faster):
data = Client.verify_amount(img="data:image/jpeg;base64,...", amount=100).data
print(data.amount == 100)  # True — the bank confirmed the transfer

# 3. Unknown amount — let OCR read it from the image (slowest):
detected = Client.detect_amount(img="data:image/jpeg;base64,...").data
print(detected.amount)  # amount found by OCR, then verified
```

Every result has the same shape:

```python
SlipResult(
    ok=True,
    message="Slip processed successfully.",
    from_cache=False,  # True when answered from the server cache
    data=SlipData(
        ref="202602032204376094",
        date="2026-03-17T10:00:00.000Z",
        amount=100.0,
        sender_bank="004",
        sender_name="John Doe",
        sender_id="xxx-x-xxxxx-x",
        receiver_bank="014",
        receiver_name="Jane Doe",
        receiver_id="xxx-x-xxxxx-x",
    ),
)
```

Extra fields come back on rich responses (`verified`, `reference_1/2/3`, `sender_bank_details`, `receiver_bank_details`).

### Bring your own deployment (or configure)

```python
from bank_slip_verifier import create_client

client = create_client()  # hosted: https://slip-c.oiio.download
# or
custom = create_client(base_url="https://your-deployment.example.com")
# or configure the static client:
Client.configure(base_url="https://your-deployment.example.com")

result = custom.verify_amount(img="data:image/jpeg;base64,...", amount=100)
```

Base URL resolution order: `base_url` option > `BANK_SLIP_VERIFIER_BASE_URL`
environment variable > hosted deployment:

```bash
BANK_SLIP_VERIFIER_BASE_URL=https://staging.example.com python app.py
```

## Timeout

Verification can take **up to 25 seconds** (OCR endpoint). The API docs require an HTTP
timeout of **30 s or more** — this client defaults to 35 s and raises `SlipTimeoutError`
when a response does not arrive in time. Tighten it only if you know what you are doing:

```python
client = create_client(timeout_ms=60_000)  # or lower, but >= 30_000
```

## Terms of Service

The API rejects every request unless TOS, privacy and EULA are accepted. The client sends
`{"tos": True, "privacy": True, "eula": True}` by default (using the service implies consent).
Override at client level or per call:

```python
create_client(consent={"tos": True, "privacy": False, "eula": True})  # client-level
client.detect_amount(img, consent={"tos": False})  # per-call (wins)
```

## Error handling

All errors extend `SlipError`. The API's failures arrive as `SlipApiError` with the HTTP
status, the documented error slug and the raw envelope:

```python
from bank_slip_verifier import Client, SlipApiError, SlipTimeoutError, SlipError

try:
    result = Client.verify_amount(img=img, amount=100)
except SlipApiError as err:
    print(err.status, err.slug, err.message)
    # 422 'amount-not-verified' '[amount-not-verified] Amount not verified'
    if err.slug == "amount-not-verified":
        # show the customer the expected amount again — the slip does not match
        ...
except SlipTimeoutError:
    # verification took longer than timeout_ms — retry later
    ...
```

| HTTP | slug | Cause |
| --- | --- | --- |
| 400 | `bad-request` | malformed body / missing field |
| 400 | `terms-not-accepted` | TOS/Privacy/EULA not accepted |
| 400 | `invalid-image` | base64 image is invalid |
| 422 | `qr-not-found` | no QR found in the image |
| 422 | `invalid-qr` | QR format invalid |
| 422 | `amount-not-found` | OCR could not read the amount |
| 422 | `amount-not-verified` | amount read, but did not match the bank record |
| 422 | `invalid-slip-data` | slip data incomplete |
| 404 | `slip-not-found` | slip not found in the bank system |

`SlipApiError.slug` is one of these values (`SLIP_ERROR_SLUGS` and `is_slip_error_slug`
are exported for runtime checks).

## Live smoke tests

The package ships live smoke tests against the real deployment (skipped by default):

```bash
LIVE=1 pytest
```

## API

| Export | Description |
| --- | --- |
| `Client` | static wrapper around a shared instance (hosted deployment by default) |
| `create_client(**options)` | new `SlipClient` connected to hosted or custom `base_url` |
| `SlipClient` | the client class; `detect_amount`, `verify_amount`, `verify_qr_code` |
| `DEFAULT_BASE_URL` | `https://slip-c.oiio.download` |
| `ENV_BASE_URL` | `BANK_SLIP_VERIFIER_BASE_URL` — env-var fallback for the base URL |
| `SlipError` / `SlipApiError` / `SlipTimeoutError` | typed errors |
| `SLIP_ERROR_SLUGS`, `is_slip_error_slug` | error slug constants + guard |
| `SlipData` / `SlipResult` | result types |
| `__version__` | package version |

## Credits

Powered by the [OIIO Service Slip Verify API](https://slip-c.oiio.download).

## License

MIT © ByteInDev
