Metadata-Version: 2.5
Name: bluapi
Version: 0.3.0
Summary: Python client for the Blu Bank HTTP API
Project-URL: Homepage, https://github.com/RayanErfan/blu-api
Project-URL: Repository, https://github.com/RayanErfan/blu-api
Project-URL: Documentation, https://github.com/RayanErfan/blu-api/blob/main/python/README.md
Project-URL: Issues, https://github.com/RayanErfan/blu-api/issues
Author: RayanErfan
Maintainer-email: RayanErfan <rayanerfan@users.noreply.github.com>
License: MIT
License-File: LICENSE
Keywords: api,banking,blu,blubank,client,iran
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
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: Topic :: Office/Business :: Financial
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# bluapi

Python client for the [Blu Bank](https://blubank.com) HTTP API.

The bank's own gateway speaks protobuf over HTTP/2 and only answers from an
Iranian IP, so the protocol is handled by the companion
[`blu-api`](https://github.com/RayanErfan/blu-api) server. This package is a
thin, typed client for that server.

```
pip install bluapi
```

## Use

Start the server first (see the `blu-api` README), then:

```python
import bluapi

with bluapi.Client() as blu:
    print(blu.balance()["total"])
    print(blu.owner("6104335551234567"))
```

`Client` defaults to `http://127.0.0.1:8088`, or `$BLU_API_URL` when set. Pass
`api_key=` (or set `$BLU_API_KEY`) if the server was started with `BLU_API_KEY`.

### Session

```python
blu.login("username", "password")   # otpRequired: true on a fresh device
blu.otp("123456")                   # only then
blu.logout()
```

A device the bank has already trusted logs in without a code, and the server
renews the token silently, so in practice you log in once.

### Money

```python
blu.transfer(1000, "6104335551234567")             # card to card
blu.transfer(1000, "IR000560611828001111111101", method=bluapi.PAYA)
```

Amounts are IRR. Source and destination must differ, so the account's own Blu
card cannot be the destination. This moves real money.

### Card payments

```python
blu.card_otp("6219865551234568")
# {'code': '123456', 'otpRequest': '73443', 'validFrom': ..., 'expiresIn': 120, ...}
```

The dynamic second password a card payment asks for: six digits, single use,
valid for about two minutes. Fetch it right before paying.

### Finding a transaction

```python
blu.find_transaction("249042")
# {'receipt': '249042', 'type': 'انتقال به کارت', 'amount': '۱۰٬۰۰۰ ریال', ...}
```

Every receipt carries a کد پیگیری. There is no lookup by it, so the server
walks the history page by page; `pages=` bounds how far back it goes.

### Everything else

```python
blu.status()            blu.profile()          blu.balances()
blu.cards()             blu.all_cards()        blu.transactions()
blu.card_otp(card)      # رمز دوم پویا, valid ~2 minutes
blu.find_transaction("249042")  # by receipt number, walks the history
blu.recent_transfers()  blu.scheduled_transfers()
blu.banks()             blu.contacts()         blu.cheques()
blu.cashback_categories()                      blu.cashback_vendors()
blu.recent_charges()    blu.recent_internet_packages()
blu.favorite_bills()    blu.banners()          blu.onboarding()
blu.junior()            blu.identities()       blu.processed_cards()
blu.transfer_get(id)    blu.transfer_cancel(id)
```

Responses are the server's JSON. Gateway payloads are protobuf on the wire and
arrive as nested dicts keyed by field number.

### Raw routes

```python
blu.raw(
    "account/deposit/list/all",
    "solutions.saman.bluebank.services.AccountListAllRequest",
    "solutions.saman.bluebank.services.AccountListAllResponse",
)
```

## Errors

`ApiError` carries `status` and `payload`. `GatewayError` is a subclass raised
when the bank itself rejected the call, and carries `detail`. Both derive from
`BluApiError`.

```python
try:
    blu.owner("0000000000000000")
except bluapi.GatewayError as exc:
    print(exc.status, exc.detail)
```

## Command line

```
bluapi status
bluapi balance
bluapi call owner 6104335551234567
```

## Notes

- Python 3.8+, standard library only.
- The server is single-account: whichever session is active answers.
- `transfer_get` and `transfer_cancel` only resolve ids from the two-step
  `transfer/init` + `submit` flow, so an id from `transfer()` usually answers
  404 or 500.

## License

MIT.
