Перейти к содержанию

Проверка адресов

Один эндпоинт, чтобы прогнать адрес через нашего AML-провайдера прежде, чем отправлять на него деньги.

curl -X POST https://api.pay.nullswap.com/api/v1/aml/check-address \
  -H "x-api-key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xfedcba0987654321fedcba0987654321fedcba09",
    "chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10"
  }'
{
  "amlResultId": "5c6d7e8f-9a0b-41c2-8d34-5e6f7a8b9c0d",
  "merchantId": "9f1d2c3b-4a5e-4f60-8b71-2c3d4e5f6a7b",
  "address": "0xfedcba0987654321fedcba0987654321fedcba09",
  "chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
  "status": "PASSED",
  "score": 12,
  "tags": [],
  "checkedAt": "2026-01-14T09:00:00.000Z",
  "fromCache": false
}
Поле Тип Обяз. Примечания
address string да Адрес для скрининга, в нативном формате сети
chainId uuid да В какой сети его интерпретировать

Важны оба параметра: одна и та же строка в разных сетях — это разные адреса, а EVM-адрес, проверенный по Ethereum и по BNB Chain, — два разных вопроса с двумя, возможно, разными ответами.

Чтение вердикта

status Значение
PASSED Проверен, ничего компрометирующего не нашлось
FAILED Проверен и помечен
SKIPPED Не проверен. У провайдера нет покрытия либо скрининг здесь неприменим

SKIPPED — это не PASSED

Это отсутствие ответа, а не ответ «чисто». Принимать такие адреса или нет — ваша политика, но решение должно быть осознанным. Написав status !== "FAILED", вы молча согласились принимать любой непроверяемый адрес.

Поле Примечания
score Целочисленная оценка риска. Чем выше, тем рискованнее. При SKIPPED бессмысленна
tags Метки провайдера, напр. ["mixer", "sanctions"]. При чистом результате пусто
checkedAt Когда прошёл скрининг — а не когда вы его запросили. При попадании в кеш будет в прошлом
fromCache Переиспользован ли недавний результат вместо новой проверки
amlResultId Указывайте его, если хотите, чтобы вердикт пересмотрели

Не стройте на score собственные пороги. Наши уже зашиты в status, а шкалу провайдера мы неизменной не держим. Решайте по status, а score и tags оставьте для своего аудит-лога.

Чем этот эндпоинт не является

Его вызов не проверяет входящий платёж

Платежи проверяются автоматически и отдельно, а результат попадает в поля платежа amlStatus и amlResultId. Этот маршрут — самостоятельная проверка по требованию, которую запускаете вы. PASSED здесь не меняет ни одной записи инвойса или платежа: им нельзя ни разблокировать депозит на статическом кошельке со статусом WITHHELD, ни превратить DIRTY-баланс в CLEAN.

И это не шлагбаум перед выплатами. Ни POST /api/v1/merchant/withdrawals, ни возврат сюда не обращаются. Нужен скрининг перед отправкой денег — вызывайте его сами и действуйте по ответу.

Где его стоит вызывать

Окупается один сценарий: проверить адрес назначения до того, как вы его приняли.

flowchart TD
    A["Клиент присылает адрес для выплаты"] --> B["POST /aml/check-address"]
    B --> C{"status"}
    C -- "PASSED" --> D["Сохранить в адресную книгу<br/>и продолжить"]
    C -- "FAILED" --> E["Отказать и сообщить клиенту,<br/>что адрес отклонён"]
    C -- "SKIPPED" --> F["Решает ваша политика"]
    D --> G["POST /merchant/withdrawals"]

Два частных случая:

  • Адрес возврата. POST .../refund регистрирует возврат, который вы уже отправили, — забрать его назад нельзя. Проверяйте адрес, пока клиент ещё на странице.
  • Адрес назначения для вывода. Проверьте один раз при добавлении в адресную книгу, а не перед каждой выплатой.

Кеширование и стоимость

Если проверить тот же адрес повторно, пока не истекло окно актуальности у провайдера, ответ придёт с fromCache: true, а checkedAt будет указывать на первую проверку. Это дешевле и быстрее, но и результату при этом может быть несколько часов. Затребовать свежий вердикт через этот эндпоинт нельзя — держите это в голове, когда решение ответственное.

Кешируйте и у себя. Проверять один и тот же адрес при каждой загрузке страницы — пустая работа: достаточно проверить его, когда он у вас появился, и повторить, если по вашим меркам результат устарел.

Ошибки

Статус message Причина
400 InvalidRequestBodyError Нет address или chainId, либо chainId не uuid
403 MerchantIsBlockedError Мерчант заблокирован
404 ChainWithIdNotFoundError Неизвестный chainId
422 EmptyAMLAddressError address есть, но пуст или состоит из одних пробелов
502 AMLServiceError Провайдер скрининга недоступен или не ответил вовремя

502 — это не вердикт

AMLServiceError значит, что на вопрос так и не ответили, — это не тихий SKIPPED. Повторите запрос; если и повтор не удался, обращайтесь с адресом как с непроверенным, а не как с чистым.