### Install and Run Development Environment Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Commands to install the development environment and run tests. Use '-m "not live"' for offline tests. ```bash uv sync ``` ```bash uv run pytest ``` ```bash uv run pytest -m "not live" ``` ```bash uv run pytest -m live ``` -------------------------------- ### Install Tolov Package Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Install the Tolov package using pip. Optional extras for Django and FastAPI are available. ```bash pip install tolov ``` ```bash pip install tolov[django] # Django + DRF pip install tolov[fastapi] # FastAPI + SQLAlchemy ``` -------------------------------- ### FastAPI Integration Setup Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Describes the FastAPI integration, focusing on SQLAlchemy for migrations and dependency injection for configuration. ```python # SQLAlchemy-based; run_migrations(engine) creates the transaction table. Config is passed to the handler constructor (not a global settings dict); handlers are wired into routes via Depends(get_db). ``` -------------------------------- ### Database Setup for Orders Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Defines the SQLAlchemy model for orders and sets up the database engine and session. Includes running migrations and creating tables. ```python from datetime import datetime, timezone from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime from sqlalchemy.orm import sessionmaker, declarative_base from tolov.integrations.fastapi.models import run_migrations engine = create_engine("sqlite:///./payments.db") Base = declarative_base() class Order(Base): __tablename__ = "orders" id = Column(Integer, primary_key=True, index=True) product_name = Column(String, index=True) amount = Column(Float) status = Column(String, default="pending") created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) # Create payment transaction tables run_migrations(engine) # Create your tables Base.metadata.create_all(bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) ``` -------------------------------- ### Install and Refresh Dependencies with uv Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Use this command to install or refresh project dependencies based on the `uv.lock` file. It ensures the environment is consistent with the locked versions. ```bash uv sync ``` -------------------------------- ### Django Integration Setup Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Details the Django integration, including the PaymentTransaction model, app registration, and webhook view structure. ```python # PaymentTransaction model (db_table="payments", unique_together=(gateway, transaction_id), integer state codes matching the Payme protocol: -2,-1,0,1,2). The app is registered as tolov.integrations.django in INSTALLED_APPS; config lives in a single settings.TOLOV dict keyed by provider (PAYME, CLICK, …). Webhook flow mirrors the gateway client/internal split: internal_webhooks/.py holds the protocol logic (CBVs subclassing Django View); webhooks.py and views.py expose the BaseWebhookView classes that users subclass to override lifecycle hooks (successfully_payment, cancelled_payment, get_check_data). Account lookups use import_string against ACCOUNT_MODEL. ``` -------------------------------- ### Get App Info for Reporting Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve application-level information for reporting, such as wallet balance and OTP settings. ```python mc.reports.app_info() # wallet balance, OTP settings, ... ``` -------------------------------- ### Get Card Info by Token Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve information about a card using its associated token. ```python mc.cards.info_by_token("") ``` -------------------------------- ### Create Octo Payment Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Use OctoGateway to create a payment. This example demonstrates a one-stage payment with auto-capture. Ensure the notify_url is correctly configured for webhooks. ```python from tolov import OctoGateway octo = OctoGateway( octo_shop_id=123, octo_secret="your-secret", notify_url="https://example.com/octo/webhook", is_test_mode=True, ) # Create payment (one-stage, auto-capture) url = octo.create_payment( id="order_1", amount=50_000, return_url="https://example.com/done", currency="UZS", language="uz", ttl=15, # payment page TTL in minutes ) # Redirect user to url ``` -------------------------------- ### Get Payout Info Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve information about a specific payout using its UUID. ```python mc.payouts.info("") ``` -------------------------------- ### Get Hold Info Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve information about a specific hold using its ID. ```python mc.holds.info(hold_id) ``` -------------------------------- ### Invoices - Get Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieves invoice details by its UUID. ```APIDOC ## GET /invoices/{uuid} ### Description Retrieves invoice details by its UUID. ### Method GET ### Endpoint /invoices/{uuid} ### Parameters #### Path Parameters - **uuid** (string) - Required - The UUID of the invoice. ``` -------------------------------- ### Lint Code with flake8 Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Runs `flake8` on the `tolov` directory to check for style guide violations and potential errors. The maximum line length is set to 121. Note that `flake8 4.0.1` has known issues with Python 3.13. ```bash uv run flake8 tolov ``` -------------------------------- ### Get Recipient Details for Reporting Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve details for a specific recipient account using their merchant account UUID. ```python mc.reports.recipient_details("") ``` -------------------------------- ### Get Payment Registry Report Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve a payment registry report within a specified date range. Supports limiting the number of results. Dates should be in 'YYYY-mm-dd HH:MM:SS' format (GMT+5). ```python mc.reports.payment_registry("2026-06-01 00:00:00", "2026-06-26 23:59:59", limit=100) ``` -------------------------------- ### Get Payout History Report Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieve a payout history report within a specified date range. Dates should be in 'YYYY-mm-dd HH:MM:SS' format (GMT+5). ```python mc.reports.payout_history("2026-06-01 00:00:00", "2026-06-26 23:59:59") ``` -------------------------------- ### Sync vs Async Usage Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Illustrates how to import and use gateways in both synchronous and asynchronous contexts. HTTP-calling methods automatically adapt to async. ```APIDOC ## Sync vs Async ### Synchronous Usage ```python from tolov import PaymeGateway, ClickGateway, OctoGateway, UzumGateway, MulticardGateway # Example instantiation (sync) payme = PaymeGateway(payme_id='YOUR_ID', payme_key='YOUR_KEY') ``` ### Asynchronous Usage ```python from tolov.aio import PaymeGateway, ClickGateway, OctoGateway, UzumGateway, MulticardGateway # Example instantiation (async) async def setup_async_gateway(): payme = PaymeGateway(payme_id='YOUR_ID', payme_key='YOUR_KEY') # Use await for async operations if needed # ... # Note: Methods that make HTTP calls become `async` automatically. # Methods that only build URLs (like `create_payment` for Payme, Click, Uzum, Paynet) remain synchronous even on async gateways. ``` ``` -------------------------------- ### Create Payment Links Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Demonstrates how to create payment links for different providers like Payme, Click, Uzum, Octo, and Paynet. ```APIDOC ## Create Payment Links ### Description This section shows how to generate payment URLs for various payment gateways. ### Usage Instantiate the relevant gateway class with your credentials and call the `create_payment` method. ### Examples **Payme** ```python from tolov import PaymeGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY", is_test_mode=True) payme_url = payme.create_payment( id="order_1", amount=150_000, # in som return_url="https://example.com/done", account_field_name="order_id", # Payme-specific (default: "order_id") ) ``` **Click** ```python from tolov import ClickGateway click = ClickGateway( service_id="SID", merchant_id="MID", merchant_user_id="MUID", secret_key="SECRET", ) click_url = click.create_payment( id="order_1", amount=150_000, return_url="https://example.com/done", ) ``` **Uzum** ```python from tolov import UzumGateway uzum = UzumGateway(service_id="498624684") uzum_url = uzum.create_payment( id="order_1", amount=100_000, # in som, converted to tiyin automatically return_url="https://example.com/done", ) ``` **Octo** ```python from tolov import OctoGateway octo = OctoGateway( octo_shop_id=123, octo_secret="your-secret", notify_url="https://example.com/octo/webhook", ) octo_url = octo.create_payment( id="order_1", amount=50_000, return_url="https://example.com/done", ) ``` **Paynet** ```python from tolov.gateways.paynet.client import PaynetGateway paynet = PaynetGateway(merchant_id=12345) paynet_url = paynet.create_payment( id="order_1", amount=15_000_000, # in tiyin ) # Without amount (configured on Paynet side): paynet_url = paynet.create_payment(id="order_1") ``` ``` -------------------------------- ### Async Usage Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Demonstrates how to use the asynchronous versions of the payment gateway clients. ```APIDOC ## Async Usage ### Description This section covers the asynchronous API for payment operations using `tolov.aio`. ### Usage Import gateway classes from `tolov.aio` and use `await` for HTTP-based operations. ### Examples **General Async Usage** ```python from tolov.aio import PaymeGateway, ClickGateway, OctoGateway, UzumGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY") # Sync methods (no HTTP) work as-is url = payme.create_payment(id="order_1", amount=150_000, return_url="...") # Async methods (HTTP calls) use await status = await payme.check_payment(transaction_id="receipt_abc") result = await payme.cancel_payment(transaction_id="receipt_abc") ``` **Octo - Fully Async** ```python from tolov.aio import OctoGateway octo = OctoGateway(octo_shop_id=123, octo_secret="secret", notify_url="...") url = await octo.create_payment(id="order_1", amount=50_000, return_url="...") status = await octo.check_payment(transaction_id="shop_tx_123") refund = await octo.cancel_payment(transaction_id="octo-uuid", amount=50_000) ``` ``` -------------------------------- ### Create Paynet Payment Link Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate PaynetGateway and create a payment link. Amounts are in tiyin. Can be created without amount if configured on Paynet side. ```python from tolov.gateways.paynet.client import PaynetGateway paynet = PaynetGateway(merchant_id=12345) paynet_url = paynet.create_payment( id="order_1", amount=15_000_000, # in tiyin ) # Without amount (configured on Paynet side): paynet_url = paynet.create_payment(id="order_1") ``` -------------------------------- ### Publish Project to PyPI with uv Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Use this command to upload the built project packages to the Python Package Index (PyPI). Requires the `UV_PUBLISH_TOKEN` environment variable to be set. ```bash uv publish ``` -------------------------------- ### Async Payment Gateway Usage Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Use the `tolov.aio` module for asynchronous operations. Sync methods work as-is, while HTTP calls require `await`. ```python from tolov.aio import PaymeGateway, ClickGateway, OctoGateway, UzumGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY") # Sync methods (no HTTP) work as-is url = payme.create_payment(id="order_1", amount=150_000, return_url="...") # Async methods (HTTP calls) use await status = await payme.check_payment(transaction_id="receipt_abc") result = await payme.cancel_payment(transaction_id="receipt_abc") # Octo — fully async octo = OctoGateway(octo_shop_id=123, octo_secret="secret", notify_url="...") url = await octo.create_payment(id="order_1", amount=50_000, return_url="...") status = await octo.check_payment(transaction_id="shop_tx_123") refund = await octo.cancel_payment(transaction_id="octo-uuid", amount=50_000) ``` -------------------------------- ### Configure ToloV Payment Gateways in Django Settings Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Add the ToloV integration app to INSTALLED_APPS and configure each payment gateway (Payme, Click, Uzum, Paynet, Multicard) in the TOLOV dictionary. Ensure sensitive keys and model details are correctly set. ```python # settings.py INSTALLED_APPS = [ # ... "tolov.integrations.django", ] TOLOV = { "PAYME": { "PAYME_ID": "your_payme_id", "PAYME_KEY": "your_payme_key", "ACCOUNT_MODEL": "orders.models.Order", "ACCOUNT_FIELD": "id", "AMOUNT_FIELD": "amount", "ONE_TIME_PAYMENT": True, }, "CLICK": { "SERVICE_ID": "your_service_id", "MERCHANT_ID": "your_merchant_id", "MERCHANT_USER_ID": "your_merchant_user_id", "SECRET_KEY": "your_secret_key", "ACCOUNT_MODEL": "orders.models.Order", "ACCOUNT_FIELD": "id", "COMMISSION_PERCENT": 0.0, "ONE_TIME_PAYMENT": True, }, "UZUM": { "SERVICE_ID": "your_service_id", "USERNAME": "your_username", "PASSWORD": "your_password", "ACCOUNT_MODEL": "orders.models.Order", "ACCOUNT_FIELD": "order_id", "AMOUNT_FIELD": "amount", "ONE_TIME_PAYMENT": True, }, "PAYNET": { "SERVICE_ID": "your_service_id", "USERNAME": "your_username", "PASSWORD": "your_password", "ACCOUNT_MODEL": "orders.models.Order", "ACCOUNT_FIELD": "id", "AMOUNT_FIELD": "amount", "ONE_TIME_PAYMENT": True, }, "MULTICARD": { "APPLICATION_ID": "your_application_id", "SECRET": "your_secret", # also signs the success callback "STORE_ID": 123, # "CALLBACK_SECRET": "...", # optional; defaults to SECRET "ACCOUNT_MODEL": "orders.models.Order", "ACCOUNT_FIELD": "id", }, } ``` -------------------------------- ### Build Project Artifacts with uv Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md This command builds the source distribution (sdist) and wheel package for the project. The output is placed in the `dist/` directory. ```bash uv build ``` -------------------------------- ### Initialize Async Multicard Gateway Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate the asynchronous MulticardGateway for non-blocking payment operations. Use `await` for HTTP calls. ```python from tolov.aio import MulticardGateway mc = MulticardGateway(application_id="...", secret="...", store_id=123) ``` -------------------------------- ### Create Async Payment Invoice Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Create an invoice for a payment asynchronously. This returns a checkout URL. Amounts are specified in the smallest currency unit. ```python url = await mc.create_payment(id="order_1", amount=150_000, callback_url="...") ``` -------------------------------- ### Create Click Payment Link Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate ClickGateway and create a payment link with service, merchant, and secret keys. ```python from tolov import ClickGateway click = ClickGateway( service_id="SID", merchant_id="MID", merchant_user_id="MUID", secret_key="SECRET", ) click_url = click.create_payment( id="order_1", amount=150_000, return_url="https://example.com/done", ) ``` -------------------------------- ### Create Uzum Payment Link Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate UzumGateway and create a payment link. Amounts are automatically converted to tiyin. ```python from tolov import UzumGateway uzum = UzumGateway(service_id="498624684") uzum_url = uzum.create_payment( id="order_1", amount=100_000, # in som, converted to tiyin automatically return_url="https://example.com/done", ) ``` -------------------------------- ### Async Gateway Implementation Pattern Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Illustrates how async gateway classes subclass sync versions, overriding only HTTP call methods and using async clients. ```python # Async classes subclass the sync gateway and override only the methods that make HTTP calls, marking them async. Pure-compute methods (e.g. generate_pay_link, create_payment for Payme) are inherited untouched. The key enabling pattern: sync request methods are split into a _build_*_request(...) -> (data, headers) helper plus the actual call, so the async override reuses _build_* and only swaps self.http_client.post(...) for await. _setup_clients is overridden to inject AsyncHttpClient. Import async via from tolov.aio import PaymeGateway. ``` -------------------------------- ### Initiate Payment via External App Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Initiate a payment through an external application system (e.g., Payme, Click, Uzum). Returns a checkout URL or deeplink. ```python # Pay via an external app (payme/click/uzum/...) — returns checkout_url/deeplink app = mc.payments.app_pay(payment_system="payme", amount=500_000, invoice_id="order_2") ``` -------------------------------- ### Implement Paynet Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BasePaynetWebhookView to process Paynet payment notifications. Includes methods for successful and cancelled payments, and an optional get_check_data for user details. ```python class PaynetWebhookView(BasePaynetWebhookView): def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() def cancelled_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "cancelled" order.save() def get_check_data(self, params, account): # optional return { "fields": { "first_name": account.user.first_name, "balance": str(account.amount), } } ``` -------------------------------- ### Create Octo Payment Link Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate OctoGateway and create a payment link. Requires shop ID, secret, and notify URL. ```python from tolov import OctoGateway octo = OctoGateway( octo_shop_id=123, octo_secret="your-secret", notify_url="https://example.com/octo/webhook", ) octo_url = octo.create_payment( id="order_1", amount=50_000, return_url="https://example.com/done", ) ``` -------------------------------- ### Initialize Multicard Gateway Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate the MulticardGateway for synchronous payment operations. Ensure you provide your application ID, secret, store ID, and set the test mode appropriately. ```python from tolov import MulticardGateway mc = MulticardGateway( application_id="your_application_id", secret="your_secret", store_id=123, is_test_mode=True, ) ``` -------------------------------- ### Create Payme Payment Link Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Instantiate PaymeGateway and create a payment link. The `account_field_name` is Payme-specific. ```python from tolov import PaymeGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY", is_test_mode=True) payme_url = payme.create_payment( id="order_1", amount=150_000, # in som return_url="https://example.com/done", account_field_name="order_id", # Payme-specific (default: "order_id") ) ``` -------------------------------- ### Automated Release Script Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md This command executes the release script, which typically involves cleaning the project, building artifacts, and publishing them. It's equivalent to the `uv publish` command but may include additional steps. ```bash make upload ``` -------------------------------- ### Core Module Components Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Details the key modules within the `tolov/core/` directory, including base classes, HTTP clients, exceptions, and utilities. ```python # base.py — BasePaymentGateway (ABC: create_payment/check_payment/cancel_payment), BaseWebhookHandler, BasePaymentProcessor (Basic-Auth check). # http.py — HttpClient / AsyncHttpClient, thin httpx wrappers over persistent (connection-pooling) clients. Both share one _handle_response because httpx's Response API is identical sync/async; both translate httpx errors into the exception hierarchy. # exceptions.py — a tree under PaymentException (auth / transaction / account / amount / method / system). exception_whitelist lists the ones that must propagate unchanged. # utils.py — format_amount (som → tiyin, ×100), generate_hmac_signature, generate_basic_auth, and handle_exceptions (see below). # constants.py — PaymentGateway and TransactionState enums. ``` -------------------------------- ### Initiate Card Binding Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Initiate the card binding process by redirecting the user to a form URL. The resulting `card_token` can be obtained from a callback or by polling `check_binding`. ```python res = mc.cards.bind( redirect_url="https://example.com/cards/ok", redirect_decline_url="https://example.com/cards/fail", callback_url="https://example.com/cards/callback", phone="998901234567", ) session_id, form_url = res["session_id"], res["form_url"] # redirect user to form_url ``` -------------------------------- ### Gateway Constructors Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Defines the available payment gateways and their required initialization parameters. All gateways support an optional `is_test_mode` parameter. ```APIDOC ## Gateway Constructors ### PaymeGateway **Required Parameters:** `payme_id`, `payme_key` ### ClickGateway **Required Parameters:** `service_id`, `merchant_id` ### UzumGateway **Required Parameters:** `service_id` ### PaynetGateway **Required Parameters:** `merchant_id` ### OctoGateway **Required Parameters:** `octo_shop_id`, `octo_secret` ### MulticardGateway **Required Parameters:** `application_id`, `secret`, `store_id` **Optional Parameters:** `is_test_mode` (boolean) - Enables sandbox environments. ``` -------------------------------- ### Import Synchronous and Asynchronous Gateways Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Import gateway classes for both synchronous and asynchronous operations. The same class names are used for both. ```python # Sync from tolov import PaymeGateway, ClickGateway, OctoGateway, UzumGateway, MulticardGateway ``` ```python # Async (same class names, same methods) from tolov.aio import PaymeGateway, ClickGateway, OctoGateway, UzumGateway, MulticardGateway ``` -------------------------------- ### Async Payme Card and Receipt Management Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Asynchronous variants for Payme card and receipt management follow the same interface as their synchronous counterparts, using `await` for HTTP calls. ```python from tolov.aio import PaymeGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY") card = await payme.cards.create(card_number="8600...", expire_date="03/25") receipt = await payme.receipts.create(amount=500_000, account={"order_id": "123"}) ``` -------------------------------- ### Run Full Test Suite with pytest Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Executes the entire test suite using pytest. Live integration tests will be skipped if the sandbox environment is unreachable. The `auto` mode for asyncio is used, and `respx` is employed for HTTP mocking. ```bash uv run pytest ``` -------------------------------- ### Format Code with Black Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Applies the Black code formatter to the `tolov` directory. The formatter version is pinned to `black==22.3.0`. ```bash uv run black tolov ``` -------------------------------- ### Implement Uzum Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BaseUzumWebhookView for Uzum payment callbacks. Supports handling payment success/cancellation and optionally retrieving check data. ```python class UzumWebhookView(BaseUzumWebhookView): def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() def cancelled_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "cancelled" order.save() def get_check_data(self, params, account): # optional return {"fio": {"value": "Ivanov Ivan"}} ``` -------------------------------- ### Unified Interface Methods Source: https://github.com/ganiyevuz/tolov/blob/master/README.md All implemented gateways provide a unified interface with `create_payment`, `check_payment`, and `cancel_payment` methods. ```APIDOC ## Unified Interface Every gateway implements the following methods: ### `create_payment(id, amount, ...)` **Description:** Initiates a payment and returns a payment URL. **Parameters:** - `id` (str) - Unique identifier for the payment. - `amount` (int/float) - The amount to be paid. - `...` - Additional gateway-specific parameters may be accepted. **Returns:** `str` - The URL for completing the payment. ### `check_payment(transaction_id)` **Description:** Checks the status and retrieves details of a payment transaction. **Parameters:** - `transaction_id` (str) - The unique identifier for the transaction. **Returns:** `dict` - A dictionary containing the payment status and other details. ### `cancel_payment(transaction_id)` **Description:** Cancels a payment transaction. **Parameters:** - `transaction_id` (str) - The unique identifier for the transaction. **Returns:** `dict` - A dictionary indicating the result of the cancellation. ``` -------------------------------- ### Run Live Integration Tests with pytest Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Executes only the live integration tests against the sandbox environment. This is useful for verifying direct interactions with payment provider sandboxes. ```bash uv run pytest -m live ``` -------------------------------- ### Implement Payme Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BasePaymeWebhookView to handle successful and cancelled payments. Optionally implement get_check_data for additional transaction details. ```python from tolov.integrations.django.views import \ BasePaymeWebhookView, BaseClickWebhookView, BaseUzumWebhookView, BasePaynetWebhookView, BaseOctoWebhookView, BaseMulticardWebhookView, ) from .models import Order class PaymeWebhookView(BasePaymeWebhookView): def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() def cancelled_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "cancelled" order.save() def get_check_data(self, params, account): # optional return { "detail": { "receipt_type": 0, "items": [{ "title": account.product_name, "price": int(account.amount * 100), "count": 1, "code": "00001", "units": 1, "vat_percent": 0, "package_code": "123456", }], } } ``` -------------------------------- ### Payouts - Create Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Creates a payout to credit a card. `kyc_data` is required for amounts over 10 million som. Amounts are in tiyin. ```APIDOC ## POST /payouts/create ### Description Creates a payout to credit a card. `kyc_data` is required for amounts over 10 million som. Amounts are in tiyin. ### Method POST ### Endpoint /payouts/create ### Parameters #### Request Body - **amount** (integer) - Required - The amount in tiyin. - **invoice_id** (string) - Required - The unique identifier for the payout. - **token** (string) - Optional - The card token. - **pan** (string) - Optional - The card PAN. - **confirmable** (boolean) - Optional - If true, requires OTP confirmation. ### Response #### Success Response (200) - **uuid** (string) - The UUID of the payout. ``` -------------------------------- ### Create Payment Invoice Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Create an invoice for a payment using the Multicard gateway. This returns a checkout URL for the user. Amounts are specified in the smallest currency unit (e.g., som for som). ```python # Create an invoice — returns checkout_url (amount in som) url = mc.create_payment( id="order_1", amount=150_000, return_url="https://example.com/done", callback_url="https://example.com/payments/webhook/multicard", ) ``` -------------------------------- ### Create Payment (Invoice) Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Creates an invoice for a payment, returning a checkout URL. Amounts are in som. ```APIDOC ## POST /create_payment ### Description Creates an invoice (payment page) and returns its checkout URL. ### Method POST ### Endpoint /create_payment ### Parameters #### Query Parameters - **id** (string) - Required - Unique identifier for the order. - **amount** (integer) - Required - The payment amount in som. - **return_url** (string) - Required - The URL to redirect to after payment completion. - **callback_url** (string) - Required - The URL to receive payment status updates. ### Request Example ```python mc.create_payment( id="order_1", amount=150_000, return_url="https://example.com/done", callback_url="https://example.com/payments/webhook/multicard", ) ``` ### Response #### Success Response (200) - **checkout_url** (string) - The URL for the payment page. ``` -------------------------------- ### Implement Octo Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BaseOctoWebhookView for Octo payment gateway integrations. Handles successful and cancelled payment callbacks. ```python class OctoWebhookView(BaseOctoWebhookView): def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() def cancelled_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "cancelled" order.save() ``` -------------------------------- ### Create Payout Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Create a payout to credit a card using its PAN or saved token. KYC data is required for amounts over 10 million som. ```python payout = mc.payouts.create(amount=10_000, invoice_id="po_1", token="") ``` -------------------------------- ### Payme Card and Receipt Management Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Manage Payme cards (create, verify, check, remove) and receipts (create, pay, check, cancel, send). Amounts for receipts are in tiyin. ```python from tolov import PaymeGateway payme = PaymeGateway(payme_id="ID", payme_key="KEY") # Cards card = payme.cards.create(card_number="8600...", expire_date="03/25") payme.cards.get_verify_code(token=card["result"]["card"]["token"]) payme.cards.verify(token=card["result"]["card"]["token"], code="123456") payme.cards.check(token="...") payme.cards.remove(token="...") # Receipts receipt = payme.receipts.create( amount=500_000, # in tiyin account={"order_id": "123"}, description="Payment for order #123", ) payme.receipts.pay(receipt_id="...", token="card_token") payme.receipts.check(receipt_id="...") payme.receipts.cancel(receipt_id="...", reason="Customer request") payme.receipts.send(receipt_id="...", phone="998901234567") ``` -------------------------------- ### Tolov Project Directory Structure Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Overview of the main directories and their purposes within the Tolov project. ```plaintext tolov/core/ shared abstractions used by every gateway tolov/gateways/ one sub-package per provider (the sync implementation) tolov/aio/ async variants of the gateways tolov/integrations/{django,fastapi}/ webhook handlers + persistence tolov/factory.py create_gateway("payme", **kwargs) string dispatch ``` -------------------------------- ### Check Pinfl for Card Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Verify Pinfl for Uzcard/Humo cards using the card PAN and Pinfl number. ```python mc.cards.check_pinfl(pan="8600...", pinfl="12345678901234") # Uzcard/Humo only ``` -------------------------------- ### Create Payment by Token Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Charge a saved card token. Supports optional split payments and OFD fiscal data. Amounts are in the smallest currency unit. ```python # Charge a saved card token (optional split + OFD fiscal data) payment = mc.payments.create_by_token( card_token="", amount=500_000, invoice_id="order_1", split=[{"type": "card", "amount": 100_000, "details": "partner share", "recipient": ""}], ) ``` -------------------------------- ### Invoices - Create Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Creates an invoice with a specified amount and invoice ID. Amounts are in tiyin. ```APIDOC ## POST /invoices/create ### Description Creates an invoice with a specified amount and invoice ID. Amounts are in tiyin. ### Method POST ### Endpoint /invoices/create ### Parameters #### Request Body - **amount** (integer) - Required - The amount in tiyin. - **invoice_id** (string) - Required - The unique identifier for the invoice. - **callback_url** (string) - Required - The URL to receive payment status updates. ``` -------------------------------- ### Implement Click Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BaseClickWebhookView to manage payment status updates. This handler is for the Click payment gateway. ```python class ClickWebhookView(BaseClickWebhookView): def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() def cancelled_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "cancelled" order.save() ``` -------------------------------- ### Gateway Module Structure Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Defines the standard structure for each provider's gateway package, including client, internal logic, and constants. ```python # client.py — the public, user-facing class (PaymeGateway, ClickGateway, …). Thin, heavily-docstringed wrapper. # internal.py — the actual business logic. The header comments say this is intended to be compiled to .so (pyarmor obfuscation); that's the reason for the client/internal split. # constants.py — endpoint URLs (test vs prod), provider error codes. # Provider extras: Payme adds cards.py + receipts.py; Click adds merchant.py. ``` -------------------------------- ### Cards - Bind Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Initiates the card binding process, returning a session ID and form URL. ```APIDOC ## POST /cards/bind ### Description Initiates the card binding process, returning a session ID and form URL. The user should be redirected to the `form_url`. ### Method POST ### Endpoint /cards/bind ### Parameters #### Request Body - **redirect_url** (string) - Required - URL to redirect to upon successful binding. - **redirect_decline_url** (string) - Required - URL to redirect to upon declined binding. - **callback_url** (string) - Required - URL to receive binding callback notifications. - **phone** (string) - Optional - User's phone number. ### Response #### Success Response (200) - **session_id** (string) - The session ID for the binding process. - **form_url** (string) - The URL of the form the user needs to complete. ``` -------------------------------- ### Reports - App Info Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieves application-level information such as wallet balance and OTP settings. ```APIDOC ## GET /reports/app_info ### Description Retrieves application-level information such as wallet balance and OTP settings. ### Method GET ### Endpoint /reports/app_info ``` -------------------------------- ### Sort Imports with isort Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Sorts imports within the `tolov` directory using `isort` with the Black profile. This helps maintain consistent import ordering. ```bash uv run isort tolov ``` -------------------------------- ### Configure Payment Webhook URLs Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Define Django URL patterns to route incoming webhook requests to the appropriate payment gateway handler views. ```python # urls.py from django.urls import path from .views import ( PaymeWebhookView, ClickWebhookView, UzumWebhookView, PaynetWebhookView, OctoWebhookView, ) urlpatterns = [ path("payments/webhook/payme/", PaymeWebhookView.as_view()), path("payments/webhook/click/", ClickWebhookView.as_view()), path("payments/webhook/uzum//", UzumWebhookView.as_view()), path("payments/webhook/paynet/", PaynetWebhookView.as_view()), path("payments/webhook/octo/", OctoWebhookView.as_view()), path("payments/webhook/multicard/", MulticardWebhookView.as_view()), ] ``` -------------------------------- ### Create Hold Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Create a hold to block funds on a card. Funds can be captured later or the hold can be canceled. Expiry is in minutes, up to 30 days. ```python hold = mc.holds.create(card_token="", amount=500_000, invoice_id="order_1", expiry=60) hold_id = hold["id"] ``` -------------------------------- ### Make Payment Using Card Token Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Once a card token is verified, you can use it to process payments. Provide the card token, amount, and a unique transaction parameter for tracking. ```python # Pay using token click.card_token_payment( card_token="token_abc", amount=100_000, transaction_parameter="unique_tx_id", ) ``` -------------------------------- ### Implement Multicard Webhook Handler Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Extend BaseMulticardWebhookView for Multicard payment success notifications. The signature is verified before this callback is triggered. ```python class MulticardWebhookView(BaseMulticardWebhookView): # Multicard's success callback fires only on a successful payment; # the signature is verified for you before this runs. def successfully_payment(self, params, transaction): order = Order.objects.get(id=transaction.account_id) order.status = "paid" order.save() ``` -------------------------------- ### Confirm Token Payment with OTP Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Confirm a token payment if an OTP hash is returned. Requires the payment UUID and the OTP code. ```python # If payment["otp_hash"] is not null, an SMS code is required: mc.payments.confirm(payment["uuid"], otp="123456") ``` -------------------------------- ### Confirm Payout with OTP Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Confirm a payout if it was created with `confirmable=True`. Requires the payout UUID and the OTP code. ```python # If created with confirmable=True, confirm with an OTP: mc.payouts.confirm(payout["uuid"], otp="123456") ``` -------------------------------- ### Handle Exceptions Decorator Usage Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Demonstrates the application of the `@handle_exceptions` decorator to public gateway methods for robust error management. ```python # Wraps public gateway methods. It detects coroutine functions and returns the matching sync/async wrapper. Whitelisted PaymentException subclasses re-raise as-is; any other exception is logged and re-raised as InternalServiceError. Apply it to new public-facing gateway methods. ``` -------------------------------- ### Verify Card Token with SMS Code Source: https://github.com/ganiyevuz/tolov/blob/master/README.md After requesting a card token, verify it using the SMS code sent to the cardholder. This step is crucial before proceeding with token-based payments. ```python # Verify with SMS code click.card_token_verify(card_token="token_abc", sms_code="12345") ``` -------------------------------- ### Type Check Code with mypy Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Performs static type checking on the `tolov` directory using `mypy`. This helps catch type-related errors before runtime. ```bash uv run mypy tolov ``` -------------------------------- ### Request Card Token with Tolov ClickGateway Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Use ClickGateway to request a card token for future transactions. Ensure you have the correct service ID, merchant ID, merchant user ID, and secret key. ```python from tolov import ClickGateway click = ClickGateway( service_id="SID", merchant_id="MID", merchant_user_id="MUID", secret_key="SECRET", ) # Request card token result = click.card_token_request( card_number="5614681005030279", expire_date="0330", ) ``` -------------------------------- ### Payments - Create by Token Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Charges a saved card token. Supports optional split payments and OFD fiscal data. Amounts are in tiyin. ```APIDOC ## POST /payments/create_by_token ### Description Charges a saved card token. Supports optional split payments and OFD fiscal data. Amounts are in tiyin. ### Method POST ### Endpoint /payments/create_by_token ### Parameters #### Request Body - **card_token** (string) - Required - The token of the saved card. - **amount** (integer) - Required - The amount to charge in tiyin. - **invoice_id** (string) - Required - The unique identifier for the invoice. - **split** (array) - Optional - Details for split payments. Each object can have `type`, `amount`, `details`, and `recipient`. ### Request Example ```python mc.payments.create_by_token( card_token="", amount=500_000, invoice_id="order_1", split=[ { "type": "card", "amount": 100_000, "details": "partner share", "recipient": "" } ], ) ``` ### Response #### Success Response (200) - **uuid** (string) - The UUID of the created payment. - **otp_hash** (string) - If present, an SMS code is required for confirmation. ``` -------------------------------- ### Payments - App Pay Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Initiates payment via an external application (e.g., Payme, Click, Uzum). Returns a checkout URL or deeplink. ```APIDOC ## POST /payments/app_pay ### Description Initiates payment via an external application (e.g., Payme, Click, Uzum). Returns a checkout URL or deeplink. ### Method POST ### Endpoint /payments/app_pay ### Parameters #### Request Body - **payment_system** (string) - Required - The payment system to use (e.g., "payme"). - **amount** (integer) - Required - The amount in tiyin. - **invoice_id** (string) - Required - The unique identifier for the invoice. ### Response #### Success Response (200) - **checkout_url** (string) - The URL or deeplink for the external payment application. ``` -------------------------------- ### Cards - Info by Token Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Retrieves information about a card using its token. ```APIDOC ## GET /cards/info_by_token ### Description Retrieves information about a card using its token. ### Method GET ### Endpoint /cards/info_by_token ### Parameters #### Query Parameters - **card_token** (string) - Required - The token of the card. ``` -------------------------------- ### Run Specific Test File with pytest Source: https://github.com/ganiyevuz/tolov/blob/master/CLAUDE.md Executes tests from a single specified file, providing a quiet output. This is useful for focused debugging or testing changes within a particular module. ```bash uv run pytest tests/test_multicard_sdk.py -q ``` -------------------------------- ### Card Tokenization Operations Source: https://github.com/ganiyevuz/tolov/blob/master/README.md This section covers the operations related to card tokenization using the Click gateway. It includes requesting a card token, verifying it with an SMS code, and making a payment using a token. ```APIDOC ## Card Tokenization Operations ### Request Card Token This operation requests a token for a given card number and expiration date. ```python # Request card token result = click.card_token_request( card_number="5614681005030279", expire_date="0330", ) ``` ### Verify Card Token This operation verifies a previously requested card token using an SMS code. ```python # Verify with SMS code click.card_token_verify(card_token="token_abc", sms_code="12345") ``` ### Pay Using Card Token This operation processes a payment using a verified card token. ```python # Pay using token click.card_token_payment( card_token="token_abc", amount=100_000, transaction_parameter="unique_tx_id", ) ``` ``` -------------------------------- ### Holds - Create Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Creates a hold to block funds on a card. Funds can be captured or released later. Expiry is in minutes (max 30 days). Amounts are in tiyin. ```APIDOC ## POST /holds/create ### Description Creates a hold to block funds on a card. Funds can be captured or released later. Expiry is in minutes (max 30 days). Amounts are in tiyin. ### Method POST ### Endpoint /holds/create ### Parameters #### Request Body - **card_token** (string) - Required - The token of the card. - **amount** (integer) - Required - The amount to hold in tiyin. - **invoice_id** (string) - Required - The unique identifier for the invoice. - **expiry** (integer) - Required - The expiry time in minutes (max 30 days). ### Response #### Success Response (200) - **id** (string) - The ID of the created hold. ``` -------------------------------- ### Payouts - Confirm Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Confirms a payout that was created with `confirmable=True`. Requires OTP. ```APIDOC ## POST /payouts/confirm ### Description Confirms a payout that was created with `confirmable=True`. Requires OTP. ### Method POST ### Endpoint /payouts/confirm ### Parameters #### Query Parameters - **uuid** (string) - Required - The UUID of the payout. - **otp** (string) - Required - The OTP code received via SMS. ``` -------------------------------- ### Payments - Partial Refund Source: https://github.com/ganiyevuz/tolov/blob/master/README.md Initiates a partial refund for a payment. Amounts are in tiyin. ```APIDOC ## POST /payments/partial_refund ### Description Initiates a partial refund for a payment. Amounts are in tiyin. ### Method POST ### Endpoint /payments/partial_refund ### Parameters #### Query Parameters - **uuid** (string) - Required - The UUID of the payment to refund. - **refund_amount** (integer) - Required - The amount to refund in tiyin. - **ofd** (array) - Optional - Fiscal data for the refund. ```