# BIAPAY Python SDK

Official Python SDK for integrating the [BIAPAY](https://biapay.net) payment gateway into your Python applications.

## Features

- **Session creation** — Authenticate and create payment sessions with one call
- **Iframe helpers** — Generate ready-to-use `<iframe>` HTML or just the URL
- **Localization** — French language support (`&lang=fr`)
- **Callback verification** — Verify HMAC-SHA256 signatures on BIAPAY callbacks
- **Framework-agnostic** — Works with Django, Flask, FastAPI, or plain Python
- **Typed & documented** — Full docstrings, type hints, and clear exceptions

---

## Installation

```bash
pip install biapay
```

**Requirements:** Python 3.8+, `requests` >= 2.25.0

---

## Quick Start

### 1. Register on BIAPAY

Create a merchant account at [https://connect.biapay.net/register](https://connect.biapay.net/register) and obtain your **Client ID** and **Client Secret** from your POS settings.

### 2. Create a payment session

```python
from biapay import BIAPay

client = BIAPay(
    client_id="your-client-id",
    client_secret="your-client-secret"
)

session = client.create_payment_session(
    order_id="ORDER-001",
    amount=5000,            # Amount in smallest currency unit
    currency_code="XAF",
    customer_email="customer@example.com",
    customer_phone="237600000000",
    transaction_id="TXN-001"  # Optional, defaults to order_id
)

print(session.launch_url)  # Load this in your iframe
```

### 3. Embed the payment iframe

```python
# Get a plain URL
url = session.get_iframe_url()

# Get a URL in French
url_fr = session.get_iframe_url(lang="fr")

# Get a full HTML <iframe> tag
html = session.get_iframe_html(width="100%", height="600")

# Get a French iframe
html_fr = session.get_iframe_html(lang="fr", style="border:none;")
```

Output example:
```html
<iframe src="https://payment.biapay.net/web/iframe/init?Authorization=eyJ..." width="100%" height="600" frameborder="0" allowfullscreen="true"></iframe>
```

---

## Callback Handling

After the customer completes (or fails) the payment, BIAPAY redirects to your callback URL (configured in your POS settings on the merchant panel).

### Set up your callback URL

In the BIAPAY merchant panel → POS → Edit → set your **Callback URL**, e.g.:
```
https://yoursite.com/payment/biapay/callback
```

### Handle the callback

```python
from biapay import BIAPay, CallbackPayload
from biapay.exceptions import BIAPaySignatureError

client = BIAPay(
    client_id="your-client-id",
    client_secret="your-client-secret"
)

# --- Django example ---
def payment_callback(request):
    try:
        payload = client.process_callback(request.GET)
    except BIAPaySignatureError:
        return HttpResponse("Forbidden", status=403)

    if payload.is_completed:
        # Payment successful — fulfill the order
        Order.objects.filter(id=payload.order_id).update(status="paid")
        print(f"Order {payload.order_id} paid via BIAPAY Txn {payload.biapay_transaction_id}")
    else:
        # Payment failed
        print(f"Order {payload.order_id} payment failed. Status: {payload.status}")

    return HttpResponse("OK")
```

```python
# --- Flask example ---
from flask import request as flask_request
from biapay.exceptions import BIAPaySignatureError

@app.route("/payment/biapay/callback")
def payment_callback():
    try:
        payload = client.process_callback(flask_request.args)
    except BIAPaySignatureError:
        return "Forbidden", 403

    if payload.is_completed:
        # handle success
        pass

    return "OK"
```

### Manual signature verification

```python
from biapay import CallbackPayload

# Parse the callback manually
payload = CallbackPayload.from_query_params(request.GET)

# Verify the signature
is_valid = client.verify_callback_signature(payload)
if not is_valid:
    raise Exception("Invalid callback signature!")

print(payload.order_id)
print(payload.status)           # "COMPLETED" or "FAILED"
print(payload.amount)
print(payload.biapay_transaction_id)
print(payload.is_completed)     # True / False
```

---

## API Reference

### `BIAPay(client_id, client_secret, base_url, timeout, originated_by)`

Main client class.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `client_id` | str | required | Your BIAPAY POS Client ID |
| `client_secret` | str | required | Your BIAPAY POS Client Secret |
| `base_url` | str | `https://api.biapay.net/gateway` | API base URL |
| `timeout` | int | `30` | HTTP timeout in seconds |
| `originated_by` | str | `"2"` | Channel identifier |

#### Methods

| Method | Returns | Description |
|--------|---------|-------------|
| `create_payment_session(...)` | `PaymentSession` | Create a payment session |
| `verify_callback_signature(callback)` | `bool` | Verify HMAC signature |
| `process_callback(params, verify_signature=True)` | `CallbackPayload` | Parse + verify callback |

---

### `PaymentSession`

| Attribute | Type | Description |
|-----------|------|-------------|
| `access_token` | str | JWT token |
| `url` | str | Base iframe URL |
| `launch_url` | str | Full iframe URL with token |

| Method | Description |
|--------|-------------|
| `get_iframe_url(lang=None)` | Returns the iframe src URL |
| `get_iframe_html(lang=None, **attrs)` | Returns a full `<iframe>` HTML string |

---

### `CallbackPayload`

| Attribute | Type | Description |
|-----------|------|-------------|
| `order_id` | str | Your original order ID |
| `status` | str | `"COMPLETED"` or `"FAILED"` |
| `amount` | str | Transaction amount |
| `currency` | str | Currency code |
| `biapay_transaction_id` | str | BIAPAY transaction reference |
| `transaction_id` | str | Your original transaction ID |
| `biapay_signature` | str | HMAC signature for verification |
| `is_completed` | bool | True if payment completed |
| `is_failed` | bool | True if payment failed |

---

## Exceptions

| Exception | When raised |
|-----------|-------------|
| `BIAPayError` | Base exception — catch-all for BIAPAY errors |
| `BIAPayAuthError` | Invalid `client_id` or `client_secret` |
| `BIAPayConnectionError` | Network or connection failure |
| `BIAPaySignatureError` | Callback HMAC signature mismatch |
| `BIAPayValidationError` | Missing or invalid request parameters |

```python
from biapay.exceptions import BIAPayError, BIAPayAuthError, BIAPayConnectionError

try:
    session = client.create_payment_session(...)
except BIAPayAuthError:
    print("Check your client_id and client_secret")
except BIAPayConnectionError:
    print("Network error — check your connection")
except BIAPayError as e:
    print(f"BIAPAY error: {e.message} (status: {e.status_code})")
```

---

## Sandbox / Dev Environment

```python
client = BIAPay(
    client_id="your-client-id",
    client_secret="your-client-secret",
    base_url="https://gatewaydev.biapay.net"  # Dev/sandbox URL
)
```

---

## License

MIT License. See [LICENSE](LICENSE) for details.
