Metadata-Version: 2.4
Name: py2328
Version: 0.1.0
Summary: Unofficial async Python SDK for the 2328.io crypto payment gateway API
Author: t9foon1337
License: MIT
Project-URL: Homepage, https://github.com/t9foon1337/py2328
Project-URL: Repository, https://github.com/t9foon1337/py2328
Project-URL: Issues, https://github.com/t9foon1337/py2328/issues
Keywords: 2328,crypto,payments,usdt,async,httpx
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Dynamic: license-file

# py2328

Unofficial async Python SDK for the [2328.io](https://2328.io) crypto payment
gateway API. Handles request signing, payment creation, payment status
lookups, and webhook signature verification.

> Disclaimer: this is an unofficial client, not affiliated with or endorsed by
> 2328.io. Refer to the official docs at <https://doc.2328.io> for API details.

## Install

```bash
pip install py2328
```

Requires Python 3.10+ and depends only on `httpx`.

## Quickstart

```python
import asyncio
from py2328 import Client

client = Client(
    project_uid="your-project-uid",
    api_key="your-api-key",
)

async def main():
    # Create a payment session.
    payment = await client.create_payment(
        order_id="order-42",
        amount=10.0,
        description="Order #42",
        url_callback="https://example.com/webhooks/2328",
        url_return="https://example.com/thanks",
    )
    uuid = payment["uuid"]
    print("Pay here:", payment["url"])

    # Poll for status.
    while True:
        info = await client.get_payment_info(uuid)
        if info.get("status") in ("paid", "expired", "cancelled"):
            print("Final status:", info["status"])
            break
        await asyncio.sleep(5)

asyncio.run(main())
```

## Webhook verification (FastAPI)

Verification is synchronous and does no I/O, so it is safe to call inside a
request handler.

```python
from fastapi import FastAPI, Request, HTTPException
from py2328 import Client

app = FastAPI()
client = Client(project_uid="your-project-uid", api_key="your-api-key")

@app.post("/webhooks/2328")
async def webhook(request: Request):
    payload = await request.json()
    if not client.verify_webhook_sign(payload):
        raise HTTPException(status_code=400, detail="bad signature")
    # payload["sign"] verified; process the event.
    return {"ok": True}
```

## Signing algorithm

Per <https://doc.2328.io/md/en/authentication>:

1. JSON-encode the body compact: `separators=(",", ":")`, `ensure_ascii=False`.
2. Base64-encode that JSON.
3. `HMAC-SHA256(base64_str, api_key)` as lowercase hex.

Bodyless requests sign the empty string (`base64("") == ""`). Webhook
verification runs the same steps: strip `sign`, sign the remaining body, and
constant-time compare against the received value. The exact byte string that
is signed is also the byte string sent on the wire, so re-serialization never
desynchronizes the signature.

The pure signing functions live in `py2328.signing` and can be imported
without `httpx`.

## License

MIT
