SafeMatch API & SDK
Полная документация REST API и официальный Python SDK для интеграции с платформой SafeMatch. Все методы для работы с заявками, чеками, апелляциями и WebSocket-уведомлениями.
https://api.safematch.iohttps://test.api.safematch.techСодержание
1. Быстрый старт (SDK)
safematch/, интерактивный агент demo.py, READMEЗависимости:
pip install httpx websockets
from safematch import SafeMatch, OrderCreate
# Подключение к основному серверу
client = SafeMatch(api_key="YOUR_API_KEY", secret_key="YOUR_SECRET_KEY")
# Подключение к тестовому серверу (sandbox)
client = SafeMatch(api_key="YOUR_API_KEY", secret_key="YOUR_SECRET_KEY", server="sandbox")
# Создание IN-ордера
order = client.create_order(OrderCreate(
side="IN",
fiat_qty=10000,
min_price=90,
max_price=95,
))
print(f"Ордер создан: {order.id}, статус: {order.status}")
2. Аутентификация
Все запросы к API (кроме /health) требуют аутентификации. Для аутентификации используются три обязательных заголовка:
| Заголовок | Описание |
|---|---|
X-API-Key | Ваш API-ключ, выданный при регистрации |
X-Timestamp | Текущий UNIX-timestamp (секунды) |
X-Signature | HMAC-SHA256 подпись запроса |
Формирование подписи
Подпись формируется по следующему алгоритму:
- Составить строку для подписи:
HTTP_МЕТОД+ПУТЬ+TIMESTAMP+ТЕЛО_ЗАПРОСАHTTP_МЕТОД— метод запроса заглавными буквами (POST,GET,DELETE)ПУТЬ— путь запроса (например,/orders)TIMESTAMP— значение заголовкаX-TimestampТЕЛО_ЗАПРОСА— JSON-тело, сериализованное с отсортированными ключами и без пробелов (разделители:,и:). Для запросов без тела эта часть опускается
- Вычислить HMAC-SHA256 от полученной строки, используя ваш секретный ключ
- Результат передать в заголовке
X-Signatureв hex-формате
Пример (Python):
import hmac
import hashlib
import json
import time
api_key = "ваш-api-key"
secret_key = "ваш-secret-key"
method = "POST"
path = "/orders"
timestamp = str(int(time.time()))
body = {"side": "IN", "min_price": 90.0, "max_price": 95.0,
"fiat": "RUB", "token": "USDT", "fiat_qty": 10000}
body_str = json.dumps(body, sort_keys=True, separators=(",", ":"))
message = method + path + timestamp + body_str
signature = hmac.new(secret_key.encode(), message.encode(), hashlib.sha256).hexdigest()
headers = {
"X-API-Key": api_key,
"X-Timestamp": timestamp,
"X-Signature": signature,
"Content-Type": "application/json"
}
Для загрузки файлов (например, чек в формате PDF) подпись формируется аналогично, но тело запроса в подпись не включается — используется только МЕТОД + ПУТЬ + TIMESTAMP.
Python SDK — аутентификация выполняется автоматически. Достаточно передатьapi_keyиsecret_keyпри создании клиентаSafeMatch(). Подпись формируется для каждого запроса внутри библиотеки.
3. Серверы
REST API серверы
SDK поддерживает переключение между серверами через параметр server:
| Значение | URL | Описание |
|---|---|---|
"production" | api.safematch.io | Production Основной (по умолчанию) |
"sandbox" | test.api.safematch.tech | Sandbox Тестовый сервер |
| Произвольный URL | — | Например "http://localhost:8000" |
# Переключение серверов
client_prod = SafeMatch(api_key="...", secret_key="...", server="production")
client_test = SafeMatch(api_key="...", secret_key="...", server="sandbox")
WebSocket серверы
| Сервер | URL | Описание |
|---|---|---|
| Production | wss://ws.safematch.io | Основной |
| Sandbox | wss://test.ws.safematch.tech | Тестовый сервер |
4. Заявки
Сводная таблица
| Метод | Путь | SDK | Описание |
|---|---|---|---|
POST | /orders | create_order() | Создание заявки (IN или OUT) |
DELETE | /orders/{order_id} | delete_order() | Удаление заявки |
POST | /orders/{id}/cancel-search | cancel_search() | Отмена поиска замены |
GET | /orders/{order_id} | get_order() | Данные заявки |
GET | /orders | get_orders() | Список заявок |
Создаёт новую заявку на покупку (IN) или продажу (OUT) криптовалюты.
Тело запроса (IN — покупка криптовалюты):
{
"side": "IN",
"min_price": 90.0,
"max_price": 95.0,
"fiat": "RUB",
"token": "USDT",
"fiat_qty": 10000
}
Тело запроса (OUT — продажа криптовалюты):
{
"side": "OUT",
"min_price": 90.0,
"max_price": 95.0,
"fiat": "RUB",
"token": "USDT",
"fiat_qty": 50000,
"max_payments": 3,
"banks": [
{
"bank_name": "Сбербанк",
"recipient_details": "2200 0000 0000 0000",
"recipient_name": "Иван Иванов",
"detail_type": "CARD",
"priority": 1
}
]
}
| Поле | Тип | Обязательность | Описание |
|---|---|---|---|
side | string | да | "IN" (покупка) или "OUT" (продажа) |
min_price | number | да | Минимальная цена (курс) |
max_price | number | да | Максимальная цена (курс) |
fiat | string | да | Фиатная валюта (например, "RUB") |
token | string | да | Криптовалюта (например, "USDT") |
fiat_qty | number | да | Сумма в фиатной валюте |
max_payments | number | только для OUT | Максимальное количество встречных платежей |
banks | array | только для OUT | Список банковских реквизитов |
client_data | object | нет | Данные клиента: name_ru, name_en, passport_series_number |
allow_self_match | bool | нет | Разрешить самосопоставление (по умолчанию false). См. раздел Самосопоставление |
Объект banks[]:
| Поле | Тип | Описание |
|---|---|---|
bank_name | string | Название банка. Допустимые значения: Sberbank, Tinkoff, Alfabank, Raiffeisen, VTB, OTP, Ozon, PSB, Yandex, Uralsib, MTS Money, MTS Wallet, MTS Bank, Gazprombank, Sovcombank, AB Russia, Pochta Bank, Dom RF, Rosselkhozbank, MKB, UBRiR, WB Bank, YooMoney, Zenit, DVB, TKB, Ak Bars, INGO Bank, Rocket |
recipient_details | string | Реквизиты получателя (номер карты или телефон) |
recipient_name | string | Имя получателя |
detail_type | string | Тип реквизитов: "CARD" или "PHONE_NUMBER" |
priority | number | Приоритет (1 — наивысший) |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"side": "IN",
"min_price": 90.0,
"max_price": 95.0,
"fiat": "RUB",
"token": "USDT",
"fiat_qty": 10000,
"max_payments": null,
"banks": null,
"status": "waiting",
"created_at": "2026-03-09T12:00:00"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Недостаточно средств для заморозки | На счёте недостаточно криптовалюты для создания OUT-заявки |
| Недостаточно средств для комиссии | На счёте недостаточно средств для покрытия комиссии по IN-заявке |
| Торговля запрещена | Торговля заблокирована для данного аккаунта. Обратитесь к администратору |
| Превышен лимит апелляций | Слишком много апелляций в истории. Создание заявок временно заблокировано |
| Превышен лимит отмен | Слишком высокий процент отмен. Создание заявок заблокировано |
| Некорректная цена | Указано неверное значение цены |
from safematch import OrderCreate, ClientData, BankDetails
# IN-ордер (покупка крипты)
order = client.create_order(OrderCreate(
side="IN",
fiat_qty=10000,
min_price=90,
max_price=95,
fiat="RUB",
token="USDT",
client_data=ClientData(name_ru="Иван Иванов"),
))
# OUT-ордер (продажа крипты)
order = client.create_order(OrderCreate(
side="OUT",
fiat_qty=50000,
min_price=90,
max_price=95,
max_payments=3,
banks=[
BankDetails(
bank_name="Sberbank",
recipient_details="2200000000000000",
recipient_name="Иван Иванов",
detail_type="CARD",
priority=1,
),
],
))
Удаляет вашу заявку. Удаление возможно только для заявок в статусе «Ожидание сопоставления».
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID заявки |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"result": "success"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Заявка не найдена | Заявка с указанным ID не существует |
| Нет доступа | Вы не являетесь создателем этой заявки |
| Нельзя удалить заявку в апелляции | Заявка находится в процессе рассмотрения апелляции |
| Неверный статус заявки | Заявку можно удалить только в статусе «Ожидание сопоставления» |
client.delete_order("order-uuid") # Только в статусе waiting
Если один из IN-ордеров в вашей OUT-заявке был отменён, система автоматически ищет замену. Этот метод позволяет отменить поиск замены и продолжить с текущими IN-заявками.
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID OUT-заявки |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"result": "success"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Заявка не найдена | Заявка с указанным ID не существует |
| Только для OUT-заявок | Отмена поиска доступна только для заявок на продажу |
| Нет доступа | Вы не являетесь создателем этой заявки |
| Неверный статус заявки | Заявка должна быть в статусе «Поиск замены» |
client.cancel_search("out-order-uuid")
Возвращает подробную информацию о заявке. Доступно только участникам сделки (создатель или контрагент).
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID заявки |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"side": "IN",
"user_side": "IN",
"min_price": 90.0,
"max_price": 95.0,
"fiat_qty": 10000,
"fiat": "RUB",
"token": "USDT",
"max_payments": null,
"banks": [...],
"created_at": 1741510800,
"matched_at": 1741510860,
"status": "waiting_payment",
"creator_id": 1,
"contr_id": 2,
"rate": 92.5,
"in_orders_data": null,
"out_order_id": "out-uuid-...",
"receipt_data": null,
"payment_method": null,
"transfer_time": null,
"crypto_amount": 108.1,
"freezeed_crypto": null,
"fee_collected": 0.0,
"expected_fiat_qty": null,
"filled_fiat_qty": null
}
| Поле | Описание |
|---|---|
id | ID заявки |
side | Сторона заявки: IN (покупка) или OUT (продажа) |
user_side | Ваша роль в этой сделке: IN или OUT |
min_price, max_price | Диапазон цены (курса) |
fiat_qty | Сумма в фиатной валюте |
fiat, token | Валютная пара |
banks | Список банковских реквизитов |
created_at | Время создания (UNIX-timestamp) |
matched_at | Время сопоставления (UNIX-timestamp) |
status | Текущий статус заявки |
rate | Итоговый курс сделки |
crypto_amount | Сумма в криптовалюте |
receipt_data | Данные о загруженном чеке |
transfer_time | Время перевода в секундах (от сопоставления до загрузки чека) |
appeal_data | Данные об апелляции (если есть) |
fee_collected | Удержанная комиссия |
expected_fiat_qty | Ожидаемая сумма в фиате (для OUT) |
filled_fiat_qty | Полученная сумма в фиате (для OUT) |
allow_self_match | Разрешено ли самосопоставление для данной заявки |
is_self_matched | Была ли заявка сопоставлена сама с собой (read-only, устанавливается системой) |
order = client.get_order("order-uuid")
print(f"Статус: {order.status}, крипто: {order.crypto_amount}")
Возвращает список ваших заявок с пагинацией, сортировкой и фильтрами.
Параметры (query):
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
page | number | 1 | Номер страницы |
page_size | number | 10 | Размер страницы (от 1 до 100) |
sort_by | string | created_at | Поле сортировки: created_at, min_price, max_price, fiat_qty, crypto_amount |
sort_order | string | desc | Порядок сортировки: asc или desc |
side | string | — | Фильтр по стороне: IN или OUT |
status | string | — | Фильтр по статусу заявки |
Успешный ответ:
{
"orders": [
{
"id": "a1b2c3d4-...",
"side": "IN",
"status": "waiting",
"fiat_qty": 10000,
"fiat": "RUB",
"token": "USDT",
"created_at": 1741510800,
...
}
],
"total_count": 42,
"page": 1,
"page_size": 10,
"total_pages": 5
}
Python SDK
result = client.get_orders(page=1, page_size=20, side="IN", status="waiting")
for o in result["orders"]:
print(f"{o['id']} — {o['status']} — {o['fiat_qty']} {o['fiat']}")
5. Самосопоставление (Self-Matching)
SafeMatch позволяет площадке сопоставлять собственные IN и OUT заявки между собой. Это полезно, когда площадка выступает и покупателем, и продавцом одновременно.
Как включить
Для самосопоставления обе заявки (IN и OUT) должны быть созданы с полем allow_self_match: true:
# OUT-заявка с разрешённым самосопоставлением
out_order = client.create_order(OrderCreate(
side="OUT",
fiat_qty=50000,
min_price=90,
max_price=95,
allow_self_match=True,
banks=[...],
))
# IN-заявка с разрешённым самосопоставлением
in_order = client.create_order(OrderCreate(
side="IN",
fiat_qty=10000,
min_price=90,
max_price=95,
allow_self_match=True,
))
Если хотя бы у одной из заявок allow_self_match = false (по умолчанию), система не будет сопоставлять их друг с другом.
Определение самосопоставления
После сопоставления в данных заявки появляется поле is_self_matched: true (read-only, устанавливается системой автоматически):
{
"id": "a1b2c3d4-...",
"allow_self_match": true,
"is_self_matched": true,
"status": "waiting_payment",
...
}
Особенности поведения
| Действие | Обычная заявка | Self-matched заявка |
|---|---|---|
Диспут (POST /orders/{id}/dispute) | Создаёт апелляцию для разбирательства | Автоматически отменяет IN-заявку. Возвращает appeal_id: "self-match-auto" |
Апелляция (POST /appeals) | Создаёт апелляцию | Заблокирована (HTTP 400) |
| OUT после диспута | OUT переходит в waiting_new_order | OUT переходит в waiting_new_order (ищет замену) |
Счётчик ignored_fiat_qty | Увеличивается при диспуте | Не увеличивается |
Важно: Self-match диспут — это упрощённый механизм отмены. Поскольку обе стороны принадлежат одной площадке, полноценное разбирательство не требуется.
6. Чеки и подтверждение
Загружает PDF-файл чека об оплате для IN-заявки (покупка). После загрузки заявка переходит в статус «Ожидание подтверждения» — продавец должен подтвердить получение средств.
Формат запроса: multipart/form-data
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID заявки |
payment_method | number | query | Приоритет выбранного банка (из списка реквизитов продавца) |
receipt_file | file | тело | PDF-файл чека |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"result": "success"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Заявка не найдена | Заявка с указанным ID не существует |
| Нет доступа | Вы не являетесь создателем этой заявки |
| Неверный статус заявки | Загрузка чека возможна только в статусах «Ожидание оплаты», «Ожидание ручной проверки», «Ожидание нового чека» |
| Указанный банк не найден | Указан несуществующий приоритет банка |
| Пустой файл | Загружен пустой файл чека |
# Из файла
client.upload_receipt("in-order-id", "receipt.pdf", payment_method=1)
# Из file-like объекта
with open("receipt.pdf", "rb") as f:
client.upload_receipt("in-order-id", f, payment_method=1)
Создатель OUT-заявки (продавец) подтверждает, что получил фиатный перевод от покупателя. После подтверждения заявка покупателя завершается, криптовалюта зачисляется покупателю.
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID IN-заявки, по которой подтверждается получение оплаты |
Успешный ответ:
{
"id": "a1b2c3d4-...",
"result": "success"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Заявка не найдена | IN-заявка с указанным ID не существует |
| Это не IN-заявка | Указанная заявка не является заявкой на покупку |
| Заявка не сопоставлена | IN-заявка ещё не была сопоставлена с вашей OUT-заявкой |
| Нет доступа | Подтверждение доступно только создателю OUT-заявки |
| Неверный статус заявки | IN-заявка должна быть в статусе «Ожидание подтверждения» |
client.confirm_payment("in-order-id")
Создатель OUT-заявки (продавец) не подтверждает получение оплаты по загруженному чеку. Создаётся диспут для разбирательства.
Алиас: /orders/{order_id}/dispute-receipt также поддерживается для обратной совместимости.
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
order_id | string | путь | ID IN-заявки, по которой оспаривается чек |
Тело запроса:
{
"reason": "fake_receipt",
"description": "Оплата не поступила на указанный счёт"
}
| Поле | Тип | Обязательность | Описание |
|---|---|---|---|
reason | string | да | Причина: "fake_receipt", "invalid_requisites" или "other" |
description | string | нет | Дополнительное пояснение |
Успешный ответ (обычная заявка):
{
"appeal_id": "appeal-uuid-...",
"order_id": "out-order-uuid-...",
"reason": "fake_receipt",
"status": "waiting_confirmation",
"created_at": "2026-03-09T12:00:00"
}
Успешный ответ (self-matched заявка):
{
"appeal_id": "self-match-auto",
"order_id": "in-order-uuid-...",
"status": "cancelled",
"created_at": "2026-03-09T12:00:00"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Недопустимая причина | Указана причина, отсутствующая в списке допустимых |
| Заявка не найдена | IN-заявка с указанным ID не существует |
| Это не IN-заявка | Указанная заявка не является заявкой на покупку |
| Нет доступа | Оспаривание доступно только создателю OUT-заявки |
| Неверный статус заявки | IN-заявка должна быть в статусе «Ожидание подтверждения» |
| Заявка уже в апелляции | По данной заявке уже создана апелляция |
from safematch import DisputeCreate
appeal = client.dispute_receipt("in-order-id", DisputeCreate(
reason="fake_receipt",
description="Описание проблемы",
))
Возвращает PDF-файл чека по его идентификатору. Доступно только участникам сделки.
Параметры:
| Параметр | Тип | Где | Описание |
|---|---|---|---|
receipt_id | string | путь | ID чека (без расширения .pdf) |
ID чека можно получить из поля receipt_data в данных заявки.
Ответ: PDF-файл (application/pdf).
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Чек не найден | Чек с указанным ID не существует |
| Нет доступа | Вы не являетесь участником сделки, к которой относится чек |
pdf_bytes = client.download_receipt("receipt-id")
with open("saved.pdf", "wb") as f:
f.write(pdf_bytes)
7. Апелляции
Создаёт апелляцию по завершённой заявке. Доступно для обоих участников сделки (создатель IN и создатель OUT). Для self-matched заявок апелляции заблокированы (HTTP 400).
Тело запроса:
{
"order_id": "a1b2c3d4-...",
"reason": "appeal",
"description": "Описание проблемы"
}
| Поле | Тип | Обязательность | Описание |
|---|---|---|---|
order_id | string | да | ID заявки, по которой создаётся апелляция |
reason | string | да | Причина: appeal |
description | string | нет | Подробное описание проблемы |
Успешный ответ:
{
"appeal_id": "appeal-uuid-...",
"order_id": "a1b2c3d4-...",
"reason": "appeal",
"status": "completed",
"created_at": "2026-03-09T12:00:00"
}
Возможные ошибки:
| Ошибка | Причина |
|---|---|
| Заявка не найдена | Заявка с указанным ID не существует |
| Заявка уже в апелляции | По данной заявке уже создана активная апелляция |
| Нет доступа | Вы не являетесь участником этой сделки |
| Заявка не завершена | Апелляции доступны только для завершённых заявок |
| Self-matched заявка | Апелляции недоступны для self-matched заявок |
| Недопустимая причина | Указана причина, не входящая в список допустимых для данного типа заявки |
from safematch import AppealCreate
appeal = client.create_appeal(AppealCreate(
order_id="order-uuid",
description="Описание проблемы",
))
print(f"Апелляция: {appeal.appeal_id}")
Причина всегда appeal (задаётся автоматически). Указывать не нужно — только order_id и опционально description.
8. Аккаунт
Возвращает данные пользователя, привязанного к API-ключу: ID, балансы, комиссии. Приватные данные (пароль, секретный ключ и т.д.) не возвращаются.
Успешный ответ:
{
"user_id": 35,
"username": "MyPlatform",
"working_balance": 1500.00,
"frozen_balance": 200.00,
"referral_balance": 12.50,
"in_fee_rate": 1.0,
"out_fee_rate": 1.0,
"role": "Proc_Admin"
}
Python SDK
me = client.get_me()
print(f"User: {me['username']} (ID {me['user_id']})")
print(f"Рабочий: {me['working_balance']} USDT")
print(f"Замороженный: {me['frozen_balance']} USDT")
print(f"Реферальный: {me['referral_balance']} USDT")
print(f"Комиссия IN: {me['in_fee_rate']}%, OUT: {me['out_fee_rate']}%")
9. WebSocket-уведомления
SafeMatch отправляет уведомления о событиях по заявкам в реальном времени через WebSocket. Это позволяет моментально реагировать на изменения без постоянного опроса REST API.
URL подключения
wss://ws.safematch.io/ws?api_key={API_KEY}&signature={SIGNATURE}&session_id={SESSION_ID}
Параметры аутентификации
| Параметр | Описание |
|---|---|
api_key | Ваш API-ключ |
signature | HMAC-SHA256 подпись (аналогично REST API) |
session_id | Уникальный идентификатор сессии (UUID v4, генерируется на стороне клиента) |
Подключение
После успешного подключения сервер отправляет сообщение:
{
"type": "connection_established",
"user_id": 123,
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"realm": "main",
"timestamp": 1710604800
}
Клиентские сообщения
Для поддержания соединения отправляйте ping:
// Отправка
{"type": "ping"}
// Ответ сервера
{"type": "pong", "timestamp": 1710604800}
Типы уведомлений
| Тип | Описание | Когда приходит |
|---|---|---|
order_created | Заявка создана | После успешного создания заявки |
order_matched | Заявка сопоставлена | Найдена встречная сторона, выданы реквизиты |
order_updated | Данные заявки обновлены | Смена статуса, загрузка чека, подтверждение |
order_completed | Заявка завершена | Сделка успешно завершена |
order_cancelled | Заявка отменена | Отмена по таймауту, отклонение или другая причина |
order_supplemented | К OUT добавлен новый IN | Найдена замена после отмены одного из IN |
insufficient_balance | Недостаточно баланса | Не хватает средств для заморозки при поиске замены. Пополните баланс. |
appeal_created | Создана апелляция | По ордеру создана апелляция (вам или контрагентом) |
Формат уведомления
Все уведомления приходят в едином формате:
{
"type": "notification",
"notification_type": "order_matched",
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"side": "IN",
"status": "waiting_payment",
"fiat_qty": 10000.0,
"fiat": "RUB",
"token": "USDT",
"banks": [{"bank_name": "Sberbank", "detail_type": "card", "details": "****1234"}],
"creator_id": 123,
"contr_id": 456,
"min_price": 90.0,
"max_price": 100.0,
"crypto_amount": 100.0,
"created_at": 1710604000,
"last_update_time": 1710604800
},
"timestamp": 1710604800
}
Лимиты и keepalive
- Максимум 2 соединения на пользователя одновременно
- Клиент должен отправлять
pingкаждые 30 секунд - Соединение закрывается через 60 секунд без ping
from safematch import SafeMatchWS
ws = SafeMatchWS(api_key="YOUR_KEY", secret_key="YOUR_SECRET")
# Sandbox
ws = SafeMatchWS(api_key="YOUR_KEY", secret_key="YOUR_SECRET", server="sandbox")
@ws.on("order_matched")
def on_matched(data):
print(f"Ордер сматчен: {data['order_id']}")
print(f"Банк: {data.get('banks', [{}])[0].get('bank_name')}")
@ws.on("order_completed")
def on_completed(data):
print(f"Ордер завершён: {data['order_id']}")
@ws.on("order_cancelled")
def on_cancelled(data):
print(f"Ордер отменён: {data['order_id']}")
# Блокирующее подключение (Ctrl+C для остановки)
ws.connect()
# Или в фоновом потоке
thread = ws.connect_in_background()
Async-режим
import asyncio
from safematch import SafeMatchWS
async def main():
ws = SafeMatchWS(api_key="...", secret_key="...", server="sandbox")
@ws.on("order_matched")
def handler(data):
print(data)
await ws.connect_async()
asyncio.run(main())
10. Статусы заявок
| Статус | Пользовательское название | Описание |
|---|---|---|
waiting | Ожидание сопоставления | Заявка размещена и ожидает встречную заявку |
waiting_payment | Ожидание оплаты | IN-заявка сопоставлена, покупатель должен выполнить перевод и загрузить чек |
waiting_payments | Ожидание поступлений | OUT-заявка сопоставлена, продавец ожидает переводы от покупателей |
waiting_confirmation | Ожидание подтверждения | Чек загружен, продавец должен подтвердить получение средств |
waiting_manual_approve | Ожидание ручной проверки | Чек требует ручной проверки |
waiting_new_receipt | Ожидание нового чека | Необходимо загрузить новый чек |
waiting_new_order | Поиск замены | Система ищет новую встречную IN-заявку взамен отменённой |
waiting_moderator | Ожидание модератора | Заявка ожидает решения модератора |
cancelling | Отмена | Промежуточный статус во время отмены (восстановление при сбое) |
completing | Завершение | Промежуточный статус во время перевода средств |
completed | Завершена | Заявка успешно завершена, средства зачислены |
cancelled_wrong_requisites | Отменена | Заявка отменена из-за несоответствия реквизитов |
in_appeal | В апелляции | Заявка находится в процессе рассмотрения апелляции |
11. Коды ответов и ошибки
HTTP-коды ответов
| Код | Описание |
|---|---|
200 | Запрос выполнен успешно |
400 | Ошибка в запросе (неверные данные, неподходящий статус заявки) |
401 | Ошибка аутентификации (неверный API-ключ, подпись или timestamp) |
403 | Доступ запрещён (нет прав на данное действие) |
404 | Ресурс не найден (заявка, чек) |
409 | Конфликт (параллельная операция) |
429 | Превышен лимит запросов |
500 | Внутренняя ошибка сервера |
503 | Сервер на обслуживании |
Исключения SDK
| Исключение | HTTP | Когда |
|---|---|---|
AuthError | 401 | Неверный ключ, подпись или timestamp |
ForbiddenError | 403 | Нет доступа к ордеру, лимит апелляций |
NotFoundError | 404 | Ордер / чек не найден |
ValidationError | 400 | Некорректные параметры |
InsufficientBalanceError | 400 | Недостаточно средств |
ConflictError | 409 | Параллельная операция |
RateLimitError | 429 | Превышен лимит запросов |
MaintenanceError | 503 | Сервер на обслуживании |
Пример обработки ошибок (SDK)
from safematch import SafeMatch
from safematch.exceptions import (
SafeMatchError,
AuthError,
NotFoundError,
ValidationError,
InsufficientBalanceError,
RateLimitError,
MaintenanceError,
)
client = SafeMatch(api_key="...", secret_key="...")
try:
order = client.get_order("non-existent-id")
except NotFoundError:
print("Ордер не найден")
except AuthError:
print("Ошибка авторизации — проверьте ключи")
except InsufficientBalanceError:
print("Недостаточно средств")
except RateLimitError:
print("Слишком много запросов — подождите")
except MaintenanceError:
print("Сервер на обслуживании")
except SafeMatchError as e:
print(f"Ошибка {e.status_code}: {e.detail}")
12. Справочник методов SDK
| API эндпоинт | SDK метод | Описание |
|---|---|---|
POST /orders | client.create_order() | Создать ордер |
DELETE /orders/{id} | client.delete_order() | Удалить ордер |
GET /orders/{id} | client.get_order() | Данные ордера |
GET /orders | client.get_orders() | Список ордеров |
POST /orders/{id}/receipt | client.upload_receipt() | Загрузить чек (PDF) |
POST /orders/{id}/confirm-payment | client.confirm_payment() | Подтвердить оплату |
POST /orders/{id}/dispute | client.dispute_receipt() | Оспорить чек (диспут) |
POST /orders/{id}/cancel-search | client.cancel_search() | Отменить поиск замены |
POST /appeals | client.create_appeal() | Создать апелляцию |
GET /receipt/{id} | client.download_receipt() | Скачать чек |
GET /me | client.get_me() | Профиль и балансы |
GET /health | client.health() | Проверка сервера |
WebSocket /ws | SafeMatchWS.connect() | Уведомления |