ТЗ: экспортный файл курсов (ЭФК) для TopChange.io
Документ для разработчика обменного сервиса. Описывает формат файла с курсами, который ваш сервис публикует по постоянному URL, чтобы попасть в рейтинг и калькулятор TopChange.io. Формат — XML ЭФК BestChange: если ваш сервис уже листится на BestChange и отдаёт ЭФК, подключение бесшовное, отдельный файл под нас не нужен.
Готовый образец: /rates-feed.example.xml
(проверен нашим импортёром — принимается как есть).
1. Как это работает
- Вы публикуете по постоянному URL XML-файл ЭФК с актуальными курсами по направлениям обмена.
- TopChange периодически (по расписанию, интервал — минуты) запрашивает файл методом
GET, асинхронно, без участия вашего бэкенда в момент сделки. - Полученные курсы валидируются и раскладываются по направлениям; список направлений и резервов обновляется автоматически.
- Если файл недоступен или невалиден — используется последнее успешно полученное состояние (degraded-режим), импорт повторяется на следующем цикле. Сбой одного запроса не ломает данные.
- Направления (а для наличных — пары «направление + город»), исчезнувшие из файла, помечаются неактивными до повторного появления.
- После 3 неудач подряд ответственному приходит уведомление — проверьте доступность и формат файла.
Вам нужно только отдавать актуальный валидный XML по URL. Частоту обновления на своей стороне выбираете сами; рекомендуется не реже, чем раз в 1–5 минут для волатильных пар.
2. Требования к эндпоинту
| Параметр | Требование |
|---|---|
| Метод | GET |
| Протокол | HTTPS (рекомендуется), допустим HTTP |
| Тип ответа | application/xml (или text/xml), кодировка UTF-8 |
| Код ответа | 200 OK при успехе |
| Аутентификация | не требуется (URL должен быть публично доступен) |
| Стабильность URL | постоянный; смена URL согласовывается отдельно |
| Таймаут | ответ должен укладываться в ~10 секунд |
| Кэш | отдавайте актуальные данные; не кэшируйте надолго на CDN |
| Размер | без жёсткого лимита; типовой файл — десятки–сотни <item> |
Только XML. Формат приёма один — XML ЭФК. JSON и другие форматы отклоняются (
invalid_feed), офферы при этом не трогаются.
3. Структура файла
Корневой элемент — <rates>. Каждое направление обмена — отдельный <item>.
Курс задаётся парой in/out, а не одним числом: in единиц валюты from
меняются на out единиц валюты to, поэтому курс = out / in.
<?xml version="1.0" encoding="UTF-8"?>
<rates>
<item>
<from>USDTTRC20</from><to>SBERRUB</to>
<in>1</in><out>96.20</out>
<amount>4500000</amount>
<frommin>1000</frommin><frommax>500000</frommax>
</item>
<item>
<from>BTC</from><to>CASHRUB</to>
<in>1</in><out>9450000</out>
<amount>5000000</amount>
<city>SPB</city>
</item>
</rates>
3.1 Поля <item>
| Тег | Обяз. | Что значит | Как используется |
|---|---|---|---|
from |
да | Код отдаваемого актива (что отдаёт клиент), см. §4 | Направление обмена |
to |
да | Код получаемого актива (что получает клиент), см. §4 | Направление обмена |
in |
да | Сколько единиц from за out единиц to. > 0. Обычно 1 |
Знаменатель курса |
out |
да | Сколько единиц to за in единиц from |
Числитель курса; курс = out/in |
amount |
да | Резерв в единицах валюты to (сколько готовы выдать) |
reserve оффера |
frommin |
нет | Минимальная сумма сделки в единицах from |
min оффера |
frommax |
нет | Максимальная сумма сделки в единицах from |
max оффера |
city |
нет* | Только для наличных (to=CASHRUB): код города BestChange (MSK, SPB…), см. §4.3 |
Город выдачи наличных |
param |
нет | Признак предложения: manual, juridical, verifying… Может повторяться |
Принимается, но пока не используется |
fromfee / tofee |
нет | Комиссия по стороне: «10» (фикс) или «0.5%» |
Принимается, но пока не используется |
* Для строки с to=CASHRUB тег <city> фактически обязателен: без него направление наличных
не привязывается к городу и не появляется на страницах «Обмен по городам».
Совместимость. Незнакомые нам теги внутри
<item>(в т.ч.delay,tomin,tomaxи др.) файл не ломают — мы их просто игнорируем. Так что полноценный ЭФК BestChange подходит целиком.
4. Коды активов и городов
Коды — из справочника BestChange. У нас коды активов совпадают с их кодами 1:1 (это и делает подключение бесшовным).
- Валюты: https://bestchange.biz/ru/wiki/currency-directory
- Города: https://bestchange.biz/ru/wiki/city-directory
Машиночитаемый справочник: https://topchange.io/feed-directory.json — актуальные коды активов, активные города и все направления, которые мы принимаем прямо сейчас (генерится из базы, не протухает). Удобно свериться программно: направление из фида учитывается, только если его пара
from→toесть в этом списке.
4.1 Криптоактивы, которые мы сейчас принимаем
USDTTRC20 (Tether TRC-20), USDTBEP20, USDCTRC20, BTC, ETH, LTC, BCH,
XMR, XRP, TRX, BNB, SOL, DAI, DOGE, GRAM (Toncoin — у BestChange код валюты GRAM).
4.2 Рублёвые рельсы (сторона to при продаже)
| Код | Значение |
|---|---|
SBERRUB |
Сбербанк |
TCSBRUB |
Т-Банк (Тинькофф) |
ACRUB |
Альфа-Банк |
TBRUB |
ВТБ |
GPBRUB |
Газпромбанк |
OZONRUB |
Озон Банк |
RFBRUB |
Райффайзен |
MIRCRUB |
Mir Pay |
YAMRUB |
ЮMoney |
CARDRUB |
Любая карта ₽ |
SBPRUB |
СБП по номеру телефона |
CASHRUB |
Наличные (обязательно с <city>, см. §4.3) |
Локальные способы (наши, вне справочника BestChange): RUB-SBP-PO-SSYLKE,
RUB-SBP-ZA-RUBEZH, RUB-SIM-KARTA, RUB-CHAEVYE.
4.3 Города для наличных (to=CASHRUB)
Наличные — это CASHRUB + обязательный <city> с кодом города BestChange. Сейчас активны:
| Код | Город |
|---|---|
MSK |
Москва |
SPB |
Санкт-Петербург |
EKB |
Екатеринбург |
NNOV |
Нижний Новгород |
KZN |
Казань |
NSK |
Новосибирск |
Строка CASHRUB с городом вне этого списка пропускается (город появится позже — присылать можно
заранее). Города берите строго по справочнику BestChange (напр. Нижний Новгород — NNOV, а не NN).
4.4 Направления
- Продажа: крипта → рублёвый рельс (
USDTTRC20 → SBERRUB). - Покупка: рублёвый рельс → крипта (
CARDRUB → USDTTRC20).in— рубли,out— крипта (курс мелкий). - Наличные: крипта →
CASHRUB+<city>. - Крипто-кросс: крипта → крипта (
BTC → USDTTRC20).
Строка с парой, которой у нас пока нет в каталоге, пропускается (не ошибка) и видна в счётчике «пропущено» в админке. Направление добавится — строка начнёт учитываться автоматически.
5. Правила валидности
Есть два уровня, и они ведут себя по-разному — это важно:
a) Битая строка → тихо пропускается. <item> без обязательного поля (from, to, in, out,
amount) или с in ≤ 0 отбрасывается, остальной файл принимается.
b) Нарушение правила курса → отклоняется ВЕСЬ файл. Если строка формально полная, но нарушает:
outдаёт неположительный курс (out ≤ 0);frommin > frommax;- отрицательные
amount/frommin/frommax;
то валидацию не проходит весь файл (invalid_feed), и мы остаёмся на последнем валидном
состоянии. Держите каждую строку корректной — одна плохая строка обнуляет весь импорт.
Прочее:
- Числа — точка как десятичный разделитель, без разделителей разрядов и без строковых обёрток
(
96.20, а не96,20и не"96.20"). - Пара
from→to(для наличных — с учётом города) в пределах файла уникальна. <rates>должен содержать хотя бы один валидный<item>.
6. Поведение импорта
- Периодичность: мы сами опрашиваем URL по расписанию; «пушить» данные не нужно.
- Атомарность по направлению: каждое валидное направление обновляется независимо (upsert).
Наличные различаются по городу —
USDTTRC20→CASHRUB (MSK)и(SPB)— это разные предложения. - Исчезнувшие: если пара (с учётом города) была в прошлом файле, но отсутствует в текущем — она помечается неактивной и скрывается из выдачи; при повторном появлении — снова активна.
- Свежесть: давно не обновлявшийся курс может помечаться устаревшим. Держите файл актуальным.
- Degraded: сетевая ошибка, не-
200, не-XML или нарушение правил (§5b) — изменения не применяются, берём последнее валидное состояние, повтор на следующем цикле.
7. Чек-лист перед сдачей интеграции
- URL отдаёт
200и валидный XML ЭФК по §3,Content-Type: application/xml; charset=utf-8. - Все
from/to— коды из справочника BestChange (§4); для наличныхto=CASHRUBесть<city>. - У каждого
<item>заполненыfrom,to,in,out,amount. - Курс =
out/inстрого положительный; где заданы лимиты —frommin ≤ frommax; числа с точкой. -
<rates>непустой; пары (с учётом города) уникальны. - Файл обновляется с нужной частотой; ответ укладывается в ~10 секунд; URL публично доступен.
- Проверили на образце:
/rates-feed.example.xml.
8. Миграция со старого JSON-формата
Если раньше вы отдавали нам JSON — теперь принимается только XML ЭФК, и коды изменились (перешли на справочник BestChange). Соответствие старых кодов новым:
| Было (JSON) | Стало (ЭФК) |
|---|---|
USDT-TRC20 |
USDTTRC20 |
USDT-BEP20 |
USDTBEP20 |
TON |
GRAM |
RUB-SBERBANK |
SBERRUB |
RUB-T-BANK-TINKOFF |
TCSBRUB |
RUB-KARTA |
CARDRUB |
RUB-SBP |
SBPRUB |
RUB-CASH-MSK (и др.) |
CASHRUB + <city>MSK</city> |
Структура тоже другая: вместо {"rate": 96.2} — пара <in>1</in><out>96.2</out> (курс = out/in),
а резерв переехал в <amount> в валюте to. Ориентир — образец
/rates-feed.example.xml.