Metadata-Version: 2.5
Name: pay-engine
Version: 0.3.0
Summary: All-in-one payment engine for Uzbekistan (Payme, Click, Uzum Bank) with one-liner links, Telegram buttons, and auto-charge.
Project-URL: Homepage, https://github.com/alisher/pay-engine
Project-URL: Bug Tracker, https://github.com/alisher/pay-engine/issues
Author-email: Alisher <930503046m@gmail.com>
License: MIT
License-File: LICENSE
Keywords: aiogram,click,django,fastapi,fintech,payme,payment,telegram,uzbekistan,uzum
Classifier: Development Status :: 4 - Beta
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: Topic :: Office/Business :: Financial :: Point-Of-Sale
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.0; extra == 'django'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100.0; extra == 'fastapi'
Provides-Extra: telegram
Requires-Dist: aiogram>=3.0.0; extra == 'telegram'
Description-Content-Type: text/markdown

# 💳 pay-engine

[![PyPI version](https://img.shields.io/badge/pypi-0.3.0-blue.svg)](https://pypi.org/project/pay-engine/)
[![Python](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/pay-engine/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**pay-engine** — O'zbekistonning asosiy to'lov tizimlarini (**Payme**, **Click**, **Uzum Bank**) birlashtiruvchi, xavfsiz to'lov havolalari yaratish, Telegram bot tugmalari hosil qilish, avtomatik yechish (auto-charge) hamda idempotent webhooklarni boshqarish imkonini beruvchi professional Python kutubxonasi.

---

## 🚀 Imkoniyatlar (Features)

- ⚡ **1 qatorlik to'lov havolalari:** Payme, Click va Uzum Bank rasmiy havolalarini bir lahzada hosil qilish.
- 🤖 **Telegram Botlar:** `aiogram` va boshqa botlar uchun to'lov tugmalari (`payment_buttons`).
- 🛡️ **Haqiqiy Idempotentlik:** Takroriy so'rovlar kelganda (retry), `on_success` qayta chaqirilmaydi. Bitta buyurtma ikki marta bajarilishidan 100% himoyalangan.
- 💾 **Doimiy saqlash (Persistent Storage):** Tranzaksiyalarni ko'p workerli (gunicorn/uvicorn `--workers`) tizimlarda saqlash uchun tayyor `SQLiteStorage` va kengaytiriladigan `BaseStorage`.
- 🐍 **Django & FastAPI integratsiyasi:** Tayyor Class-Based View'lar (`PaymeWebhookView`, `ClickWebhookView`, `UzumWebhookView`) va FastAPI routerlari (`pay_router`).
- 🔄 **Avtomatik yechish (Auto-charge):** Payme Subscribe API (`X-Auth`) va Click Merchant API (`/card_token/payment`) orqali karta tokenidan qayta to'lov yechish.
- 🔒 **Qat'iy xavfsizlik (Fail-closed):** Kalitsiz so'rovlar mutlaqo qabul qilinmaydi. Timing-attack himoyasi (`hmac.compare_digest`).

---

## 📦 O'rnatish (Installation)

```bash
pip install pay-engine
```

FastAPI, Django yoki Telegram bot bilan birga:
```bash
pip install "pay-engine[fastapi,django,telegram]"
```

---

## ⚙️ Sozlash (.env)

```env
# Payme
PAYME_MERCHANT_ID=your_payme_merchant_id
PAYME_SECRET_KEY=your_payme_secret_key
PAYME_TEST_MODE=false

# Click
CLICK_SERVICE_ID=your_click_service_id
CLICK_MERCHANT_ID=your_click_merchant_id
CLICK_SECRET_KEY=your_click_secret_key
CLICK_MERCHANT_USER_ID=your_click_user_id

# Uzum Bank
UZUM_SHOP_ID=your_uzum_shop_id
UZUM_SECRET_KEY=your_uzum_secret_key
```

---

## 📖 Foydalanish (Quickstart)

### 1. To'lov havolasini olish (1 qatorda)

```python
from pay_engine import payme, click, uzum

# Payme havolasi:
payme_url = payme.link(amount=100_000, order_id="ORDER_123")

# Click havolasi:
click_url = click.link(amount=100_000, order_id="ORDER_123")

# Uzum Bank havolasi:
uzum_url = uzum.link(amount=100_000, order_id="ORDER_123")
```

---

### 2. FastAPI da to'lovlarni qabul qilish (Webhook)

```python
from fastapi import FastAPI
from pay_engine.integrations.fastapi import pay_router
from pay_engine import SQLiteStorage

app = FastAPI()

# Doimiy SQLite storage (server restart bo'lsa ham ma'lumotlar saqlanadi):
storage = SQLiteStorage("payments.db")

def check_order(order_id: str, amount: float) -> bool:
    """Buyurtma mavjudligi va summasini bazangizdan tekshiring."""
    # Agar summa mos bo'lmasa yoki buyurtma topilmasa False qaytaring:
    return True

async def on_success(order_id: str, amount: float):
    """Pul to'langanda faqat BIR MARTA chaqiriladi (idempotent)."""
    print(f"✅ To'lov qabul qilindi: Buyurtma #{order_id}, Summa: {amount} so'm")

# /payments/payme, /payments/click, /payments/uzum ulanadi:
app.include_router(pay_router(
    on_success=on_success,
    check_order=check_order,
    storage=storage
))
```

---

### 3. Django Loyihalarida foydalanish

`views.py`:
```python
from pay_engine.integrations.django import PaymeWebhookView, ClickWebhookView
from pay_engine import SQLiteStorage

storage = SQLiteStorage("payments.db")

class MyPaymeView(PaymeWebhookView):
    secret_key = "your_payme_secret"
    storage = storage

    def check_order(self, order_id: str, amount: float) -> bool:
        # Bazadan buyurtmani tekshiring
        return True

    def on_success(self, order_id: str, amount: float):
        # Buyurtmani to'landi deb belgilang
        pass

    def on_cancel(self, order_id: str, amount: float, reason):
        # To'lov bekor qilinganda yoki refund bo'lganda
        pass
```

`urls.py`:
```python
from django.urls import path
from .views import MyPaymeView

urlpatterns = [
    path("payments/payme/", MyPaymeView.as_view(), name="payme_webhook"),
]
```

---

### 4. Telegram Botda to'lov tugmalari (aiogram)

```python
from aiogram import Bot, Dispatcher, types
from pay_engine.integrations.telegram import payment_buttons

bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()

@dp.message(lambda msg: msg.text == "/tolov")
async def pay_command(message: types.Message):
    keyboard = payment_buttons(amount=75_000, order_id=message.from_user.id)
    await message.answer("To'lov usulini tanlang:", reply_markup=keyboard)
```

---

### 5. Avtomatik yechish (Card Token Auto-charge)

```python
from pay_engine import payme, click

# Payme Subscribe API:
res_payme = payme.charge(
    card_token="token_xyz",
    amount=50_000,
    order_id="SUB_101",
    description="Oylik obuna"
)

# Click Merchant API:
res_click = click.charge(
    card_token="token_xyz",
    amount=50_000,
    order_id="SUB_101"
)
```

---

## 🧪 Testlarni ishga tushirish

Barcha 34 ta integratsion va xavfsizlik testlarini ishga tushirish:

```bash
python -m unittest discover -s tests -p "test_*.py"
```

---

## 📄 Litsenziya

MIT License. Ochiq va bepul foydalanish mumkin!
