from __future__ import annotations
from typing import Optional
import httpx
from tbank.core.client import BaseAsyncClient
from tbank.core.client import ensure_idempotency_key as _idem
from tbank.core.client import page_params as _page
from tbank.core.errors import TBankAPIError
from tbank.core.retry import RetryPolicy
from tbank.core.transport import (
AsyncTransport,
CertTypes,
VerifyTypes,
build_async_transports,
)
from tbank.core.urls import (
PROD_URL,
SANDBOX_SECURED_URL,
SANDBOX_URL,
SECURED_URL,
)
from tbank.nominal_accounts._endpoints import ADD_CARD as _ADD_CARD
from tbank.nominal_accounts._endpoints import BANK_DETAILS as _BANK_DETAILS
from tbank.nominal_accounts._endpoints import BENEFICIARY as _BENEFICIARY
from tbank.nominal_accounts._endpoints import NA as _NA
from tbank.nominal_accounts._endpoints import NA_V2 as _NA_V2
from tbank.nominal_accounts._endpoints import PAYMENT as _PAYMENT
from tbank.nominal_accounts.errors import error_from_nominal_response
from tbank.nominal_accounts.models import (
AddCardRequest,
AddCardRequestResponse,
BalanceListResponse,
BankDetailsListResponse,
BankDetailsRequest,
BankDetailsResponse,
BeneficiaryListResponse,
BeneficiaryRequest,
BeneficiaryResponse,
BeneficiaryScoringListResponse,
CreatePaymentRequest,
CreateTransferRequest,
DealListResponse,
DealRequest,
DealResponse,
DealValidity,
DeponentListResponse,
DeponentRequest,
DeponentResponse,
HoldListResponse,
IdentifyIncomingTransactionRequest,
IncomingTransactionListResponse,
PaymentListResponse,
PaymentResponse,
RecipientListResponse,
RecipientRequest,
RecipientResponse,
RetryPaymentResponse,
StepListResponse,
StepRequest,
StepResponse,
TransferListResponse,
TransferResponse,
UpdateRecipientBankDetailsRequest,
)
[документация]
class NominalAccountsClient(BaseAsyncClient):
"""Асинхронный клиент номинальных счетов: бенефициары и их банковские
реквизиты, скоринг, сделки/этапы/депоненты/реципиенты, платежи, балансы,
холды и переводы между виртуальными счетами.
Просмотр карточек сделки, этапа и платежа, проверка сделки и все операции с
переводами идут на secured-хост и требуют **mTLS-сертификата** (`cert`);
остальные методы — на обычном хосте по **Bearer**-токену. Методы создания
ресурсов принимают ключ идемпотентности `idempotency_key` (по умолчанию
генерируется автоматически).
"""
decimal_body = True
def __init__(
self,
token: str,
*,
base_url: Optional[str] = None,
secured_base_url: Optional[str] = None,
sandbox: bool = False,
cert: Optional[CertTypes] = None,
verify: VerifyTypes = True,
retry: Optional[RetryPolicy] = None,
transport: Optional[AsyncTransport] = None,
secured_transport: Optional[AsyncTransport] = None,
) -> None:
transport, secured_transport = build_async_transports(
token,
base_url=base_url or (SANDBOX_URL if sandbox else PROD_URL),
secured_base_url=secured_base_url
or (SANDBOX_SECURED_URL if sandbox else SECURED_URL),
cert=cert,
verify=verify,
retry=retry,
transport=transport,
secured_transport=secured_transport,
)
super().__init__(transport, secured_transport)
def _error_from_response(self, response: httpx.Response) -> TBankAPIError:
return error_from_nominal_response(response)
# --- Бенефициары ---
[документация]
async def list_beneficiaries(
self, *, offset: Optional[int] = None, limit: Optional[int] = None
) -> BeneficiaryListResponse:
"""Список бенефициаров компании."""
return await self._get(
f"{_NA}/beneficiaries", BeneficiaryListResponse, params=_page(offset, limit)
)
[документация]
async def create_beneficiary(
self, request: BeneficiaryRequest, *, idempotency_key: Optional[str] = None
) -> BeneficiaryResponse:
"""Создать бенефициара."""
return await self._send(
"POST",
f"{_NA}/beneficiaries",
_BENEFICIARY,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_beneficiary(self, beneficiary_id: str) -> BeneficiaryResponse:
"""Карточка бенефициара."""
return await self._get(f"{_NA}/beneficiaries/{beneficiary_id}", _BENEFICIARY)
[документация]
async def update_beneficiary(
self, beneficiary_id: str, request: BeneficiaryRequest
) -> BeneficiaryResponse:
"""Обновить бенефициара."""
return await self._send(
"PUT", f"{_NA}/beneficiaries/{beneficiary_id}", _BENEFICIARY, body=request
)
[документация]
async def create_add_card_request(
self,
beneficiary_id: str,
request: AddCardRequest,
*,
idempotency_key: Optional[str] = None,
) -> AddCardRequestResponse:
"""Создать запрос на добавление карты бенефициару."""
return await self._send(
"POST",
f"{_NA}/beneficiaries/{beneficiary_id}/add-card-requests",
_ADD_CARD,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_add_card_request(
self, beneficiary_id: str, add_card_request_id: str
) -> AddCardRequestResponse:
"""Статус запроса на добавление карты."""
return await self._get(
f"{_NA}/beneficiaries/{beneficiary_id}"
f"/add-card-requests/{add_card_request_id}",
_ADD_CARD,
)
[документация]
async def list_bank_details(
self,
beneficiary_id: str,
*,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> BankDetailsListResponse:
"""Список банковских реквизитов бенефициара."""
return await self._get(
f"{_NA}/beneficiaries/{beneficiary_id}/bank-details",
BankDetailsListResponse,
params=_page(offset, limit),
)
[документация]
async def create_bank_details(
self,
beneficiary_id: str,
request: BankDetailsRequest,
*,
idempotency_key: Optional[str] = None,
) -> BankDetailsResponse:
"""Добавить банковские реквизиты бенефициару."""
return await self._send(
"POST",
f"{_NA}/beneficiaries/{beneficiary_id}/bank-details",
_BANK_DETAILS,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_bank_details(
self, beneficiary_id: str, bank_details_id: str
) -> BankDetailsResponse:
"""Карточка банковских реквизитов."""
return await self._get(
f"{_NA}/beneficiaries/{beneficiary_id}/bank-details/{bank_details_id}",
_BANK_DETAILS,
)
[документация]
async def update_bank_details(
self, beneficiary_id: str, bank_details_id: str, request: BankDetailsRequest
) -> BankDetailsResponse:
"""Обновить банковские реквизиты."""
return await self._send(
"PUT",
f"{_NA}/beneficiaries/{beneficiary_id}/bank-details/{bank_details_id}",
_BANK_DETAILS,
body=request,
)
[документация]
async def delete_bank_details(
self, beneficiary_id: str, bank_details_id: str
) -> None:
"""Удалить банковские реквизиты."""
await self._send(
"DELETE",
f"{_NA}/beneficiaries/{beneficiary_id}/bank-details/{bank_details_id}",
)
[документация]
async def set_default_bank_details(
self, beneficiary_id: str, bank_details_id: str
) -> None:
"""Сделать реквизиты основными для бенефициара."""
await self._send(
"POST",
f"{_NA}/beneficiaries/{beneficiary_id}"
f"/bank-details/{bank_details_id}/set-default",
)
[документация]
async def get_beneficiaries_scoring(
self,
*,
beneficiary_id: Optional[str] = None,
passed: Optional[bool] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> BeneficiaryScoringListResponse:
"""Результаты проверки бенефициаров в финансовом мониторинге."""
return await self._get(
f"{_NA_V2}/beneficiaries/scoring",
BeneficiaryScoringListResponse,
params=_page(offset, limit, beneficiaryId=beneficiary_id, passed=passed),
)
# --- Сделки ---
[документация]
async def list_deals(
self, *, offset: Optional[int] = None, limit: Optional[int] = None
) -> DealListResponse:
"""Список сделок."""
return await self._get(
f"{_NA}/deals", DealListResponse, params=_page(offset, limit)
)
[документация]
async def create_deal(
self, request: DealRequest, *, idempotency_key: Optional[str] = None
) -> DealResponse:
"""Создать сделку (черновик)."""
return await self._send(
"POST",
f"{_NA}/deals",
DealResponse,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_deal(self, deal_id: str) -> DealResponse:
"""Карточка сделки (mTLS)."""
return await self._get(f"{_NA}/deals/{deal_id}", DealResponse, secured=True)
[документация]
async def delete_deal(self, deal_id: str) -> None:
"""Удалить сделку-черновик."""
await self._send("DELETE", f"{_NA}/deals/{deal_id}")
[документация]
async def accept_deal(self, deal_id: str) -> None:
"""Принять (согласовать) сделку."""
await self._send("POST", f"{_NA}/deals/{deal_id}/accept")
[документация]
async def cancel_deal(self, deal_id: str) -> None:
"""Отменить сделку."""
await self._send("POST", f"{_NA}/deals/{deal_id}/cancel")
[документация]
async def draft_deal(self, deal_id: str) -> None:
"""Вернуть сделку в черновик."""
await self._send("POST", f"{_NA}/deals/{deal_id}/draft")
[документация]
async def get_deal_validity(self, deal_id: str) -> DealValidity:
"""Проверить возможность проведения платежей по сделке (mTLS)."""
return await self._get(
f"{_NA}/deals/{deal_id}/is-valid", DealValidity, secured=True
)
# --- Этапы сделки ---
[документация]
async def list_steps(
self,
deal_id: str,
*,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> StepListResponse:
"""Список этапов сделки."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps",
StepListResponse,
params=_page(offset, limit),
)
[документация]
async def create_step(
self,
deal_id: str,
request: StepRequest,
*,
idempotency_key: Optional[str] = None,
) -> StepResponse:
"""Создать этап сделки."""
return await self._send(
"POST",
f"{_NA}/deals/{deal_id}/steps",
StepResponse,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_step(self, deal_id: str, step_id: str) -> StepResponse:
"""Карточка этапа сделки (mTLS)."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps/{step_id}", StepResponse, secured=True
)
[документация]
async def update_step(
self, deal_id: str, step_id: str, request: StepRequest
) -> StepResponse:
"""Обновить этап сделки."""
return await self._send(
"PUT", f"{_NA}/deals/{deal_id}/steps/{step_id}", StepResponse, body=request
)
[документация]
async def delete_step(self, deal_id: str, step_id: str) -> None:
"""Удалить этап сделки."""
await self._send("DELETE", f"{_NA}/deals/{deal_id}/steps/{step_id}")
[документация]
async def complete_step(self, deal_id: str, step_id: str) -> None:
"""Завершить этап сделки (провести выплаты)."""
await self._send("POST", f"{_NA}/deals/{deal_id}/steps/{step_id}/complete")
# --- Депоненты этапа ---
[документация]
async def list_deponents(
self,
deal_id: str,
step_id: str,
*,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> DeponentListResponse:
"""Список депонентов этапа сделки."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps/{step_id}/deponents",
DeponentListResponse,
params=_page(offset, limit),
)
[документация]
async def get_deponent(
self, deal_id: str, step_id: str, beneficiary_id: str
) -> DeponentResponse:
"""Карточка депонента этапа сделки."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps/{step_id}/deponents/{beneficiary_id}",
DeponentResponse,
)
[документация]
async def set_deponent(
self,
deal_id: str,
step_id: str,
beneficiary_id: str,
request: DeponentRequest,
) -> DeponentResponse:
"""Добавить/обновить депонента этапа сделки."""
return await self._send(
"PUT",
f"{_NA}/deals/{deal_id}/steps/{step_id}/deponents/{beneficiary_id}",
DeponentResponse,
body=request,
)
[документация]
async def delete_deponent(
self, deal_id: str, step_id: str, beneficiary_id: str
) -> None:
"""Удалить депонента этапа сделки."""
await self._send(
"DELETE",
f"{_NA}/deals/{deal_id}/steps/{step_id}/deponents/{beneficiary_id}",
)
# --- Реципиенты этапа ---
[документация]
async def list_recipients(
self,
deal_id: str,
step_id: str,
*,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> RecipientListResponse:
"""Список реципиентов этапа сделки."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps/{step_id}/recipients",
RecipientListResponse,
params=_page(offset, limit),
)
[документация]
async def create_recipient(
self,
deal_id: str,
step_id: str,
request: RecipientRequest,
*,
idempotency_key: Optional[str] = None,
) -> RecipientResponse:
"""Добавить реципиента этапа сделки."""
return await self._send(
"POST",
f"{_NA}/deals/{deal_id}/steps/{step_id}/recipients",
RecipientResponse,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_recipient(
self, deal_id: str, step_id: str, recipient_id: str
) -> RecipientResponse:
"""Карточка реципиента этапа сделки."""
return await self._get(
f"{_NA}/deals/{deal_id}/steps/{step_id}/recipients/{recipient_id}",
RecipientResponse,
)
[документация]
async def update_recipient(
self,
deal_id: str,
step_id: str,
recipient_id: str,
request: RecipientRequest,
) -> RecipientResponse:
"""Обновить реципиента этапа сделки."""
return await self._send(
"PUT",
f"{_NA}/deals/{deal_id}/steps/{step_id}/recipients/{recipient_id}",
RecipientResponse,
body=request,
)
[документация]
async def delete_recipient(
self, deal_id: str, step_id: str, recipient_id: str
) -> None:
"""Удалить реципиента этапа сделки."""
await self._send(
"DELETE",
f"{_NA}/deals/{deal_id}/steps/{step_id}/recipients/{recipient_id}",
)
[документация]
async def update_recipient_bank_details(
self,
deal_id: str,
step_id: str,
recipient_id: str,
request: UpdateRecipientBankDetailsRequest,
) -> None:
"""Обновить банковские реквизиты реципиента."""
await self._send(
"POST",
f"{_NA}/deals/{deal_id}/steps/{step_id}"
f"/recipients/{recipient_id}/update-bank-details",
body=request,
)
# --- Платежи ---
[документация]
async def list_payments(
self,
*,
beneficiary_id: Optional[str] = None,
deal_id: Optional[str] = None,
account_number: Optional[str] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> PaymentListResponse:
"""Список платежей с номинального счёта."""
return await self._get(
f"{_NA}/payments",
PaymentListResponse,
params=_page(
offset,
limit,
beneficiaryId=beneficiary_id,
dealId=deal_id,
accountNumber=account_number,
),
)
[документация]
async def create_payment(
self, request: CreatePaymentRequest, *, idempotency_key: Optional[str] = None
) -> PaymentResponse:
"""Создать платёж (обычный или налоговый)."""
return await self._send(
"POST",
f"{_NA}/payments",
_PAYMENT,
body=request,
idempotency_key=_idem(idempotency_key),
)
[документация]
async def get_payment(self, payment_id: str) -> PaymentResponse:
"""Карточка платежа (mTLS)."""
return await self._get(f"{_NA}/payments/{payment_id}", _PAYMENT, secured=True)
[документация]
async def retry_payment(self, payment_id: str) -> RetryPaymentResponse:
"""Повторить неуспешный платёж."""
return await self._send(
"POST", f"{_NA}/payments/{payment_id}/retry", RetryPaymentResponse
)
# --- Неидентифицированные пополнения ---
[документация]
async def list_incoming_transactions(
self,
*,
account_number: Optional[str] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> IncomingTransactionListResponse:
"""Список неидентифицированных пополнений."""
return await self._get(
f"{_NA}/incoming-transactions",
IncomingTransactionListResponse,
params=_page(offset, limit, accountNumber=account_number),
)
[документация]
async def identify_incoming_transaction(
self, operation_id: str, request: IdentifyIncomingTransactionRequest
) -> None:
"""Идентифицировать пополнение (распределить по бенефициарам)."""
await self._send(
"POST",
f"{_NA}/incoming-transactions/{operation_id}/identify",
body=request,
)
# --- Виртуальные счета ---
[документация]
async def list_balances(
self,
*,
account_number: Optional[str] = None,
beneficiary_id: Optional[str] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> BalanceListResponse:
"""Балансы бенефициаров на виртуальных счетах."""
return await self._get(
f"{_NA}/virtual-accounts/balances",
BalanceListResponse,
params=_page(
offset,
limit,
accountNumber=account_number,
beneficiaryId=beneficiary_id,
),
)
[документация]
async def list_holds(
self,
*,
account_number: Optional[str] = None,
beneficiary_id: Optional[str] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> HoldListResponse:
"""Заблокированные средства (холды) на виртуальных счетах."""
return await self._get(
f"{_NA}/virtual-accounts/holds",
HoldListResponse,
params=_page(
offset,
limit,
accountNumber=account_number,
beneficiaryId=beneficiary_id,
),
)
[документация]
async def list_transfers(
self,
account_number: str,
*,
deal_id: Optional[str] = None,
from_beneficiary_id: Optional[str] = None,
to_beneficiary_id: Optional[str] = None,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> TransferListResponse:
"""Список переводов между виртуальными счетами (mTLS)."""
return await self._get(
f"{_NA}/virtual-accounts/transfers",
TransferListResponse,
params=_page(
offset,
limit,
accountNumber=account_number,
dealId=deal_id,
fromBeneficiaryId=from_beneficiary_id,
toBeneficiaryId=to_beneficiary_id,
),
secured=True,
)
[документация]
async def create_transfer(
self, request: CreateTransferRequest, *, idempotency_key: Optional[str] = None
) -> TransferResponse:
"""Создать перевод между виртуальными счетами (mTLS)."""
return await self._send(
"POST",
f"{_NA}/virtual-accounts/transfers",
TransferResponse,
body=request,
idempotency_key=_idem(idempotency_key),
secured=True,
)
[документация]
async def get_transfer(self, transfer_id: str) -> TransferResponse:
"""Карточка перевода между виртуальными счетами (mTLS)."""
return await self._get(
f"{_NA}/virtual-accounts/transfers/{transfer_id}",
TransferResponse,
secured=True,
)