SafeMatch API & SDK

Полная документация REST API и официальный Python SDK для интеграции с платформой SafeMatch. Все методы для работы с заявками, чеками, апелляциями и WebSocket-уведомлениями.

Production: https://api.safematch.io
Sandbox (тестовая среда): https://test.api.safematch.tech

Содержание

  1. Быстрый старт (SDK)
  2. Аутентификация
  3. Серверы
  4. Заявки
  5. Самосопоставление (Self-Matching)
  6. Чеки и подтверждение
  7. Апелляции
  8. Аккаунт
  9. WebSocket-уведомления
  10. Статусы заявок
  11. Коды ответов и ошибки
  12. Справочник методов SDK

1. Быстрый старт (SDK)

Скачать SDK (21 KB) safematch-sdk.zip — Python 3.10+
Содержимое: библиотека 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-SignatureHMAC-SHA256 подпись запроса

Формирование подписи

Подпись формируется по следующему алгоритму:

  1. Составить строку для подписи: HTTP_МЕТОД + ПУТЬ + TIMESTAMP + ТЕЛО_ЗАПРОСА
    • HTTP_МЕТОД — метод запроса заглавными буквами (POST, GET, DELETE)
    • ПУТЬ — путь запроса (например, /orders)
    • TIMESTAMP — значение заголовка X-Timestamp
    • ТЕЛО_ЗАПРОСА — JSON-тело, сериализованное с отсортированными ключами и без пробелов (разделители: , и :). Для запросов без тела эта часть опускается
  2. Вычислить HMAC-SHA256 от полученной строки, используя ваш секретный ключ
  3. Результат передать в заголовке 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.ioProduction Основной (по умолчанию)
"sandbox"test.api.safematch.techSandbox Тестовый сервер
Произвольный URLНапример "http://localhost:8000"
# Переключение серверов
client_prod = SafeMatch(api_key="...", secret_key="...", server="production")
client_test = SafeMatch(api_key="...", secret_key="...", server="sandbox")

WebSocket серверы

СерверURLОписание
Productionwss://ws.safematch.ioОсновной
Sandboxwss://test.ws.safematch.techТестовый сервер

4. Заявки

Сводная таблица

МетодПутьSDKОписание
POST/orderscreate_order()Создание заявки (IN или OUT)
DELETE/orders/{order_id}delete_order()Удаление заявки
POST/orders/{id}/cancel-searchcancel_search()Отмена поиска замены
GET/orders/{order_id}get_order()Данные заявки
GET/ordersget_orders()Список заявок

POST /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
    }
  ]
}
ПолеТипОбязательностьОписание
sidestringда"IN" (покупка) или "OUT" (продажа)
min_pricenumberдаМинимальная цена (курс)
max_pricenumberдаМаксимальная цена (курс)
fiatstringдаФиатная валюта (например, "RUB")
tokenstringдаКриптовалюта (например, "USDT")
fiat_qtynumberдаСумма в фиатной валюте
max_paymentsnumberтолько для OUTМаксимальное количество встречных платежей
banksarrayтолько для OUTСписок банковских реквизитов
client_dataobjectнетДанные клиента: name_ru, name_en, passport_series_number
allow_self_matchboolнетРазрешить самосопоставление (по умолчанию false). См. раздел Самосопоставление

Объект banks[]:

ПолеТипОписание
bank_namestringНазвание банка. Допустимые значения: 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_detailsstringРеквизиты получателя (номер карты или телефон)
recipient_namestringИмя получателя
detail_typestringТип реквизитов: "CARD" или "PHONE_NUMBER"
prioritynumberПриоритет (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-заявке
Торговля запрещенаТорговля заблокирована для данного аккаунта. Обратитесь к администратору
Превышен лимит апелляцийСлишком много апелляций в истории. Создание заявок временно заблокировано
Превышен лимит отменСлишком высокий процент отмен. Создание заявок заблокировано
Некорректная ценаУказано неверное значение цены
Python SDK
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,
        ),
    ],
))

DELETE /orders/{order_id} Удаление заявки

Удаляет вашу заявку. Удаление возможно только для заявок в статусе «Ожидание сопоставления».

Параметры:

ПараметрТипГдеОписание
order_idstringпутьID заявки

Успешный ответ:

{
  "id": "a1b2c3d4-...",
  "result": "success"
}

Возможные ошибки:

ОшибкаПричина
Заявка не найденаЗаявка с указанным ID не существует
Нет доступаВы не являетесь создателем этой заявки
Нельзя удалить заявку в апелляцииЗаявка находится в процессе рассмотрения апелляции
Неверный статус заявкиЗаявку можно удалить только в статусе «Ожидание сопоставления»
Python SDK
client.delete_order("order-uuid")  # Только в статусе waiting

POST /orders/{order_id}/cancel-search Отмена поиска замены

Если один из IN-ордеров в вашей OUT-заявке был отменён, система автоматически ищет замену. Этот метод позволяет отменить поиск замены и продолжить с текущими IN-заявками.

Параметры:

ПараметрТипГдеОписание
order_idstringпутьID OUT-заявки

Успешный ответ:

{
  "id": "a1b2c3d4-...",
  "result": "success"
}

Возможные ошибки:

ОшибкаПричина
Заявка не найденаЗаявка с указанным ID не существует
Только для OUT-заявокОтмена поиска доступна только для заявок на продажу
Нет доступаВы не являетесь создателем этой заявки
Неверный статус заявкиЗаявка должна быть в статусе «Поиск замены»
Python SDK
client.cancel_search("out-order-uuid")

GET /orders/{order_id} Данные заявки

Возвращает подробную информацию о заявке. Доступно только участникам сделки (создатель или контрагент).

Параметры:

ПараметрТипГдеОписание
order_idstringпуть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
}
ПолеОписание
idID заявки
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, устанавливается системой)
Python SDK
order = client.get_order("order-uuid")
print(f"Статус: {order.status}, крипто: {order.crypto_amount}")

GET /orders Список заявок

Возвращает список ваших заявок с пагинацией, сортировкой и фильтрами.

Параметры (query):

ПараметрТипПо умолчаниюОписание
pagenumber1Номер страницы
page_sizenumber10Размер страницы (от 1 до 100)
sort_bystringcreated_atПоле сортировки: created_at, min_price, max_price, fiat_qty, crypto_amount
sort_orderstringdescПорядок сортировки: asc или desc
sidestringФильтр по стороне: IN или OUT
statusstringФильтр по статусу заявки

Успешный ответ:

{
  "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_orderOUT переходит в waiting_new_order (ищет замену)
Счётчик ignored_fiat_qtyУвеличивается при диспутеНе увеличивается
Важно: Self-match диспут — это упрощённый механизм отмены. Поскольку обе стороны принадлежат одной площадке, полноценное разбирательство не требуется.

6. Чеки и подтверждение

POST /orders/{order_id}/receipt Загрузка чека

Загружает PDF-файл чека об оплате для IN-заявки (покупка). После загрузки заявка переходит в статус «Ожидание подтверждения» — продавец должен подтвердить получение средств.

Формат запроса: multipart/form-data

Параметры:

ПараметрТипГдеОписание
order_idstringпутьID заявки
payment_methodnumberqueryПриоритет выбранного банка (из списка реквизитов продавца)
receipt_filefileтелоPDF-файл чека

Успешный ответ:

{
  "id": "a1b2c3d4-...",
  "result": "success"
}

Возможные ошибки:

ОшибкаПричина
Заявка не найденаЗаявка с указанным ID не существует
Нет доступаВы не являетесь создателем этой заявки
Неверный статус заявкиЗагрузка чека возможна только в статусах «Ожидание оплаты», «Ожидание ручной проверки», «Ожидание нового чека»
Указанный банк не найденУказан несуществующий приоритет банка
Пустой файлЗагружен пустой файл чека
Python SDK
# Из файла
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)

POST /orders/{order_id}/confirm-payment Подтверждение получения оплаты

Создатель OUT-заявки (продавец) подтверждает, что получил фиатный перевод от покупателя. После подтверждения заявка покупателя завершается, криптовалюта зачисляется покупателю.

Параметры:

ПараметрТипГдеОписание
order_idstringпутьID IN-заявки, по которой подтверждается получение оплаты

Успешный ответ:

{
  "id": "a1b2c3d4-...",
  "result": "success"
}

Возможные ошибки:

ОшибкаПричина
Заявка не найденаIN-заявка с указанным ID не существует
Это не IN-заявкаУказанная заявка не является заявкой на покупку
Заявка не сопоставленаIN-заявка ещё не была сопоставлена с вашей OUT-заявкой
Нет доступаПодтверждение доступно только создателю OUT-заявки
Неверный статус заявкиIN-заявка должна быть в статусе «Ожидание подтверждения»
Python SDK
client.confirm_payment("in-order-id")

POST /orders/{order_id}/dispute Оспаривание чека (диспут)

Создатель OUT-заявки (продавец) не подтверждает получение оплаты по загруженному чеку. Создаётся диспут для разбирательства.

Алиас: /orders/{order_id}/dispute-receipt также поддерживается для обратной совместимости.

Параметры:

ПараметрТипГдеОписание
order_idstringпутьID IN-заявки, по которой оспаривается чек

Тело запроса:

{
  "reason": "fake_receipt",
  "description": "Оплата не поступила на указанный счёт"
}
ПолеТипОбязательностьОписание
reasonstringдаПричина: "fake_receipt", "invalid_requisites" или "other"
descriptionstringнетДополнительное пояснение

Успешный ответ (обычная заявка):

{
  "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-заявка должна быть в статусе «Ожидание подтверждения»
Заявка уже в апелляцииПо данной заявке уже создана апелляция
Python SDK
from safematch import DisputeCreate

appeal = client.dispute_receipt("in-order-id", DisputeCreate(
    reason="fake_receipt",
    description="Описание проблемы",
))

GET /receipt/{receipt_id} Скачать PDF-чек

Возвращает PDF-файл чека по его идентификатору. Доступно только участникам сделки.

Параметры:

ПараметрТипГдеОписание
receipt_idstringпутьID чека (без расширения .pdf)

ID чека можно получить из поля receipt_data в данных заявки.

Ответ: PDF-файл (application/pdf).

Возможные ошибки:

ОшибкаПричина
Чек не найденЧек с указанным ID не существует
Нет доступаВы не являетесь участником сделки, к которой относится чек
Python SDK
pdf_bytes = client.download_receipt("receipt-id")
with open("saved.pdf", "wb") as f:
    f.write(pdf_bytes)

7. Апелляции

POST /appeals Создание апелляции

Создаёт апелляцию по завершённой заявке. Доступно для обоих участников сделки (создатель IN и создатель OUT). Для self-matched заявок апелляции заблокированы (HTTP 400).

Тело запроса:

{
  "order_id": "a1b2c3d4-...",
  "reason": "appeal",
  "description": "Описание проблемы"
}
ПолеТипОбязательностьОписание
order_idstringдаID заявки, по которой создаётся апелляция
reasonstringдаПричина: appeal
descriptionstringнетПодробное описание проблемы

Успешный ответ:

{
  "appeal_id": "appeal-uuid-...",
  "order_id": "a1b2c3d4-...",
  "reason": "appeal",
  "status": "completed",
  "created_at": "2026-03-09T12:00:00"
}

Возможные ошибки:

ОшибкаПричина
Заявка не найденаЗаявка с указанным ID не существует
Заявка уже в апелляцииПо данной заявке уже создана активная апелляция
Нет доступаВы не являетесь участником этой сделки
Заявка не завершенаАпелляции доступны только для завершённых заявок
Self-matched заявкаАпелляции недоступны для self-matched заявок
Недопустимая причинаУказана причина, не входящая в список допустимых для данного типа заявки
Python SDK
from safematch import AppealCreate

appeal = client.create_appeal(AppealCreate(
    order_id="order-uuid",
    description="Описание проблемы",
))
print(f"Апелляция: {appeal.appeal_id}")

Причина всегда appeal (задаётся автоматически). Указывать не нужно — только order_id и опционально description.


8. Аккаунт

GET /me Профиль и балансы текущего пользователя

Возвращает данные пользователя, привязанного к 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-ключ
signatureHMAC-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

Python SDK
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Когда
AuthError401Неверный ключ, подпись или timestamp
ForbiddenError403Нет доступа к ордеру, лимит апелляций
NotFoundError404Ордер / чек не найден
ValidationError400Некорректные параметры
InsufficientBalanceError400Недостаточно средств
ConflictError409Параллельная операция
RateLimitError429Превышен лимит запросов
MaintenanceError503Сервер на обслуживании

Пример обработки ошибок (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 /ordersclient.create_order()Создать ордер
DELETE /orders/{id}client.delete_order()Удалить ордер
GET /orders/{id}client.get_order()Данные ордера
GET /ordersclient.get_orders()Список ордеров
POST /orders/{id}/receiptclient.upload_receipt()Загрузить чек (PDF)
POST /orders/{id}/confirm-paymentclient.confirm_payment()Подтвердить оплату
POST /orders/{id}/disputeclient.dispute_receipt()Оспорить чек (диспут)
POST /orders/{id}/cancel-searchclient.cancel_search()Отменить поиск замены
POST /appealsclient.create_appeal()Создать апелляцию
GET /receipt/{id}client.download_receipt()Скачать чек
GET /meclient.get_me()Профиль и балансы
GET /healthclient.health()Проверка сервера
WebSocket /wsSafeMatchWS.connect()Уведомления