Документация · API v3

Руководство по API v3

API находит получателя на Fragment, готовит счёт и подтверждает оплату в сети TON. Платёж подписывает ваш кошелёк у вас на сервере, поэтому сид-фраза никуда не уходит.

Адрес API
https://api.fragment-api.net
Формат
JSON по HTTPS
Вход
Подпись кошелька, без регистрации
Кошельки
v4r2 и W5 (v5r1)

Как устроен v3

В v3 платёж подписываете вы. Сервер готовит его, проверяет подпись и следит за оплатой, но ключа от вашего кошелька у него нет.

  1. ВходВы подписываете кошельком одноразовую строку от сервера (формат TON Connect ton_proof) и получаете ключ доступа. Ни регистрации, ни API-ключа.
  2. ЗаказAPI находит получателя на Fragment, берёт счёт и возвращает запрос на оплату: какие сообщения отправить, на какие адреса и до какого времени.
  3. Проверка и подпись у васВаш код сверяет запрос с тем, что вы заказывали, и подписывает его своим ключом. SDK делает это сам.
  4. Отправка и подтверждениеAPI проверяет подписанное сообщение, отправляет его в сеть TON и ждёт, пока оплата появится в блокчейне.

Проще всего начать с официального SDK для Node.js или Python: он проходит все четыре шага и проверяет каждый платёж перед подписью. Если пишете свой клиент, весь протокол описан в разделе «Без SDK».

Что нужно заранее

Для любой покупки

  • Кошелёк TON версии v4r2 или W5 (v5r1) и его сид-фраза из 24 слов.
  • TON на кошельке: на заказ и на сетевые комиссии. Новый кошелёк тоже подойдёт, первый платёж его развернёт.

Со своим аккаунтом Fragment

  • Аккаунт Fragment с KYC, к нему привязаны кошелёк TON и Telegram.
  • Cookie аккаунта. Удобнее всего выгрузить их расширением Cookie-Editor на fragment.com: Export, формат Header String.
  • Оплату в USDT отправляйте с кошелька, привязанного к этому аккаунту, и держите на нём немного TON на газ.

Без KYC

  • Больше ничего не нужно: заказ купит наш верифицированный аккаунт.
  • Оплата только в TON.

Храните сид-фразу и cookie вне кода: в переменных окружения или в менеджере секретов. Сид-фраза нужна только для подписи на вашей стороне, SDK никуда её не отправляет.

Быстрый старт с SDK

Установите SDK и сделайте первую покупку. Пример покупает 50 звёзд со своего аккаунта Fragment.

$npm install fragment-api
import { FragmentAPIv3 } from "fragment-api";

const api = new FragmentAPIv3({
  mnemonic: process.env.TON_SEED,                 // 24 слова, нужны только для подписи здесь
  walletType: "v5r1",                             // ваш кошелёк: "v4r2" или "v5r1" (W5)
  fragmentCookies: process.env.FRAGMENT_COOKIES,  // ваш аккаунт Fragment, без KYC не нужен
  trust: { maxTonPerOrder: 50 },                  // не подписывать заказ дороже 50 TON
});

console.log("Платим с кошелька", api.address);   // проверьте, что это ваш адрес

const result = await api.buy({
  product: "stars",
  username: "durov",
  amount: 50,
  idempotencyKey: "myshop:1001",                  // повтор с этим ключом не купит второй раз
});

console.log(result.success, result.orders[0].ref_id, result.transaction_hash);

Проверьте, что api.address совпадает с адресом вашего кошелька. Если ошибиться с версией кошелька, получится другой адрес, и SDK будет подписывать платежи с пустого кошелька.

При первом вызове buy входит, потом создаёт заказ, проверяет запрос на оплату, подписывает и отправляет его и ждёт подтверждения в блокчейне. Ответ при успехе:

Ответ
{
  "success": true,
  "message": "Payment completed",
  "transaction_hash": "…",
  "orders": [{
    "id": "…", "ref_id": "Ref#…", "status": "success",
    "product": "stars", "amount": 50, "username": "durov",
    "cost": 0.75, "currency": "TON", "txid": "…"
  }]
}

Настройки SDK

Параметры конструктора. Имена в Node.js и Python отличаются только стилем написания.

Node.jsPythonПо умолчаниюЧто это
mnemonicmnemonic24 слова. Нужны только для подписи на вашей стороне
walletTypewallet_type"v4r2"Версия кошелька: "v4r2" или "v5r1" (W5)
fragmentCookiesfragment_cookiesCookie аккаунта Fragment, нужны для заказов с KYC
trust.maxTonPerOrdermax_ton_per_orderОбязательно. Заказ дороже этой суммы в TON не будет подписан
trust.maxUsdtPerOrdermax_usdt_per_orderТо же в USDT. Обязательно для оплаты в USDT
trust.maxFeePercentmax_fee_percent5Потолок комиссии сервиса в процентах от заказа
trust.feeWallets, middleWallets, fragmentAddressesfee_wallets, middle_wallets, fragment_addressesВаши адреса в дополнение к встроенным в SDK
trust.usdtWalletusdt_walletВаш кошелёк USDT, если он нестандартный
trust.trustServerConfigtrust_server_configfalseС true SDK принимает кошельки, которые назовёт сервер. Только для тестов
externalTtlSecondsexternal_ttl_seconds120Сколько секунд действует подписанный платёж, не больше 300
baseUrlbase_urlhttps://api.fragment-api.netАдрес API
timeoutNoneHTTP-таймаут. None ждёт ответа, пока платёж подтверждается

В Python параметры trust передаются словарём: trust={"max_ton_per_order": 50}.

Поля заказа

Node.jsPythonЧто это
productproduct"stars", "premium" или "ton"
usernameusernameЮзернейм в Telegram: durov, @durov или https://t.me/durov
amountamountЗвёзды (от 50), TON (от 1) или месяцы Premium (3, 6, 12)
kyckycПо умолчанию да: покупка с вашего аккаунта Fragment. Нет: через сервис, оплата в TON
paymentMethodpayment_method"ton" (по умолчанию) или "usdt_ton", только с KYC
showSendershow_senderПоказывать вас отправителем, по умолчанию да
idempotencyKeyidempotency_keyВаш идентификатор заказа, например "myshop:1001". Очень советуем
customOrderInfocustom_order_infoЛюбая заметка для себя

Методы

Node.jsPythonЧто делает
buy(order)buy(product, username, amount, ...)Создаёт и оплачивает один заказ
createOrder(order)create_order(...)Создаёт заказ без оплаты: заказ и запрос на оплату
checkOrder(created)check_order(created)Проверяет заказ, ничего не подписывая
payOrders([...])pay_orders([...])Оплачивает несколько заказов одной транзакцией
prepare([...])prepare([...])Подписывает платёж, но не отправляет его
submit(prepared, created)submit(prepared, created)Отправляет подготовленный платёж и ждёт результата
getOrder(id)get_order(order_id)Заказ и его запрос на оплату
listOrders(limit, offset)list_orders(limit, offset)Ваши заказы, новые сначала
userInfo(username)user_info(username)Ищет пользователя Telegram на Fragment
walletInfo()wallet_info()Адрес, состояние, seqno и балансы вашего кошелька
config()config()Сеть, кошельки и комиссии сервиса
revoke()revoke()Удаляет ключ доступа на сервере
addressaddressАдрес вашего кошелька (UQ…)

Примеры

Premium без KYC

Без аккаунта Fragment и без cookie, оплата в TON.

const api = new FragmentAPIv3({ mnemonic: process.env.TON_SEED, walletType: "v5r1", trust: { maxTonPerOrder: 50 } });

await api.buy({ product: "premium", username: "durov", amount: 3, kyc: false });

Оплата в USDT

Со своим аккаунтом Fragment, с привязанного к нему кошелька и с лимитом в USDT. Держите на кошельке немного TON: каждый перевод USDT несёт TON на газ, а неизрасходованное возвращается.

const api = new FragmentAPIv3({
  mnemonic: process.env.TON_SEED,
  walletType: "v5r1",
  fragmentCookies: process.env.FRAGMENT_COOKIES,
  trust: { maxTonPerOrder: 50, maxUsdtPerOrder: 100 },
});

await api.buy({ product: "stars", username: "durov", amount: 500, paymentMethod: "usdt_ton" });

Много заказов одной транзакцией

Кошелёк W5 отправляет до 255 сообщений за раз, v4r2 до 4. Заказ с KYC обычно занимает два сообщения (Fragment и комиссия), заказ без KYC одно.

const orders = [];
for (const username of ["alice", "bob", "carol"]) {
  const created = await api.createOrder({ product: "stars", username, amount: 50, idempotencyKey: `myshop:${username}:50` });
  await api.checkOrder(created);   // плохой заказ отклоняется один, а не вместе со всей пачкой
  orders.push(created);
}
const result = await api.payOrders(orders);

Каждый подписанный платёж берёт следующий seqno кошелька, поэтому с одного кошелька платите по одному платежу за раз: одновременные заказы собирайте в пачку или ставьте в очередь.

Если процесс упал

Сохраните подписанный платёж до отправки. Отправить тот же платёж ещё раз всегда безопасно: сеть применит его только один раз.

const created = await api.createOrder({ product: "stars", username: "durov", amount: 50, idempotencyKey: "myshop:1002" });
const prepared = await api.prepare([created]);
await db.save("payment:myshop:1002", prepared);   // ваше хранилище

const result = await api.submit(prepared, [created]);

// после перезапуска, с тем же подготовленным платежом:
// await api.submit(await db.load("payment:myshop:1002"));

Обработка ошибок

Ошибки приходят исключением с полем error_code. Два случая требуют особого внимания: деньги могли уйти, поэтому заказ нужно сверить, а не покупать заново.

try {
  await api.buy({ product: "stars", username: "durov", amount: 50, idempotencyKey: "myshop:1003" });
} catch (e) {
  if (e.error_code === "TRANSFER_AMBIGUOUS" || e.error_code === "SUBMIT_OUTCOME_UNKNOWN") {
    // деньги могли уйти: сверьте заказ позже и не покупайте его снова
  } else {
    console.error(e.error_code, e.message);
  }
}

Без SDK: протокол по шагам

Подключиться можно и напрямую по HTTP. Здесь весь протокол: что отправить, что подписать и что проверить. Ключ кошелька и в этом случае остаётся только у вас.

Общие правила

  • Все запросы идут по HTTPS на https://api.fragment-api.net. Запрос с данными по http:// получит ошибку HTTPS_REQUIRED.
  • Тела запросов и ответов в JSON. В каждом ответе есть success, а при ошибке ещё error_code и message.
  • Ключ доступа передаётся в заголовке Authorization: Bearer <auth_key> и никогда в адресе: адреса попадают в логи.
  • Без ключа работают только GET /v3/auth/challenge, POST /v3/auth и GET /v3/config.

Шаг 1. Вход по подписи кошелька

Запросите одноразовую строку. Она действует 5 минут и принимается один раз, поэтому перехваченную подпись нельзя использовать повторно.

Запрос
curl https://api.fragment-api.net/v3/auth/challenge
Ответ
{ "success": true, "nonce": "<base64url>", "expires_at": 1790000300 }

Подпишите ton_proof ключом кошелька. Это стандартный формат TON Connect, поэтому войти можно и через любой кошелёк с поддержкой TON Connect.

  • domain: api.fragment-api.net. Подпись для другого домена сервер не примет.
  • timestamp: текущее время в секундах, допуск 5 минут в обе стороны.
  • payload: fragment-api/v3:<nonce>:<sha256 от строки cookie в hex>. Без cookie последняя часть пустая: fragment-api/v3:<nonce>:
  • Адрес кошелька: workchain 0, стандартный subwallet. Сервер вычисляет его сам из публичного ключа и версии, поэтому войти можно и с ещё не развёрнутого кошелька.
Что подписывается
message   = "ton-proof-item-v2/"
            ‖ workchain (int32, big-endian) ‖ хеш адреса (32 байта)
            ‖ длина domain (uint32, little-endian) ‖ domain (utf-8)
            ‖ timestamp (uint64, little-endian)
            ‖ payload (utf-8)

signature = Ed25519(private_key, sha256(0xffff ‖ "ton-connect" ‖ sha256(message)))

То же на Python. Адрес (workchain и 32-байтный хеш) даёт ваша библиотека кошелька, например tonutils или pytoniq.

Python
import hashlib, struct
from nacl.signing import SigningKey   # pip install pynacl

def ton_proof_signature(private_key: bytes, workchain: int, address_hash: bytes,
                        domain: str, timestamp: int, payload: str) -> bytes:
    domain_bytes = domain.encode()
    message = (b"ton-proof-item-v2/"
               + struct.pack(">i", workchain) + address_hash
               + struct.pack("<I", len(domain_bytes)) + domain_bytes
               + struct.pack("<Q", timestamp)
               + payload.encode())
    digest = hashlib.sha256(b"\xff\xff" + b"ton-connect" + hashlib.sha256(message).digest()).digest()
    return SigningKey(private_key[:32]).sign(digest).signature

Отправьте подпись и получите ключ доступа. Поле fragment_cookies нужно только для заказов с KYC, до 8 КБ.

Запрос
curl https://api.fragment-api.net/v3/auth \
  -H "Content-Type: application/json" \
  -d '{
    "public_key": "<64 hex>",
    "wallet_type": "v5r1",
    "fragment_cookies": "stel_ssid=…; stel_token=…",
    "proof": {
      "timestamp": 1790000000,
      "domain": "api.fragment-api.net",
      "payload": "fragment-api/v3:<nonce>:<sha256 hex>",
      "signature": "<base64, 64 байта>"
    }
  }'
Ответ
{
  "success": true,
  "auth_key": "<64 символа>",
  "wallet": { "address": "0:…", "address_friendly": "UQ…", "wallet_type": "v5r1" }
}

Ключ живёт 30 дней с последнего использования, но не дольше 365 дней. Cookie сервер хранит зашифрованными под этим ключом. Чтобы удалить ключ раньше, отправьте DELETE /v3/auth с ним в заголовке.

Ошибки входа: PROOF_DOMAIN (подпись для другого домена), PROOF_PAYLOAD (в payload нет nonce или не совпал хеш cookie), PROOF_INVALID (подпись, время или nonce: запросите новый nonce), INVALID_FRAGMENT_COOKIES.

Шаг 2. Заказ и запрос на оплату

Запрос
curl https://api.fragment-api.net/v3/orders \
  -H "Authorization: Bearer $AUTH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product": "stars",
    "username": "durov",
    "amount": 50,
    "kyc": true,
    "payment_method": "ton",
    "show_sender": true,
    "idempotency_key": "myshop:1001"
  }'

Поля те же, что в SDK: product, username, amount (звёзды и TON не больше 1 000 000), kyc, payment_method, show_sender, idempotency_key, custom_order_info.

Ответ
{
  "success": true,
  "order": {
    "id": "…", "ref_id": "Ref#…", "status": "created", "product": "stars",
    "amount": 50, "username": "durov", "cost": 0.75, "currency": "TON",
    "payment_method": "ton", "valid_until": 1790000900
  },
  "payment": {
    "valid_until": 1790000900,
    "network": "-239",
    "from": "0:…",
    "messages": [
      { "address": "EQBAjaOy…", "amount": "750000000", "payload": "<base64 BoC>", "role": "fragment" },
      { "address": "UQ…", "amount": "15000000", "payload": "<base64 BoC>", "role": "fee" }
    ]
  },
  "recipient_id": "…"
}

messages в точности в формате sendTransaction TON Connect. У каждого сообщения есть роль:

РольКогдаЧто это
fragmentс KYCСообщение самого Fragment без изменений. При оплате в USDT оно идёт на ваш кошелёк USDT, а внутри лежит перевод USDT на адрес Fragment
feeс KYCКомиссия сервиса: TON или перевод USDT на кошелёк комиссий. Отсутствует, если комиссия не берётся
middleбез KYCЦена заказа и комиссия одним переводом в TON на кошелёк сервиса. Сервис оплатит Fragment сам, когда перевод придёт, и ровно один раз

Повтор с тем же idempotency_key вернёт тот же заказ и тот же запрос на оплату. Если заказ с этим ключом истёк неоплаченным, создастся новый. В ответе есть и recipient_id, идентификатор получателя на Fragment: по нему можно применить свой список блокировок до оплаты.

Шаг 3. Проверка перед подписью

Сервер не хранит вашу сид-фразу, но он говорит, что подписать. Поэтому ничего не подписывайте на слово. SDK проверяет следующее, и свой клиент должен делать то же:

  1. Заказ тот, который вы просили: товар, количество, получатель, с KYC или без, валюта. Сверяйте с вашим запросом, а не с тем, что вернул сервер. Набор сообщений следует из этого: заказу с KYC никогда не нужно сообщение middle.
  2. Сообщение fragment идёт только на адрес Fragment из вашего собственного списка, а не из ответа сервера. При оплате в USDT этот адрес должен быть получателем внутри payload, а само сообщение идёт на ваш кошелёк USDT.
  3. Сообщение с переводом USDT идёт только на ваш собственный кошелёк USDT. Его адрес вы вычисляете сами из мастер-контракта USDT и кода кошелька: иначе сервер мог бы подставить кошелёк другого вашего токена.
  4. Сообщения fee и middle идут только на кошельки сервиса, которые вы закрепили у себя. GET /v3/config их показывает, но источником доверия он быть не должен.
  5. Комиссия не больше вашего потолка (в SDK 5% от заказа) в той же валюте.
  6. Заказ не дороже вашего лимита в TON, а при оплате в USDT и лимита в USDT. Что именно покупает счёт Fragment, проверить нельзя, поэтому лимит на заказ обязателен.
  7. Платёж действует недолго: valid_until = min(payment.valid_until, сейчас + 120 с), режим отправки 3.

Адреса Fragment, закреплённые в SDK:

Адреса Fragment
UQBAjaOyi2wGWlk-EDkSabqqnF-MrrwMadnwqrurKpkla4QB
UQCFJEP4WZ_mpdo0_kMEmsTgvrMHG7K_tWY16pQhKHwoOtFz
UQBeab7D38RIwypegbN7YZgQzwDbb8QfMMwY8ouJc3qPl4CJ

Шаг 4. Подпись

Узнайте состояние кошелька:

Запрос
curl https://api.fragment-api.net/v3/wallet -H "Authorization: Bearer $AUTH_KEY"
Ответ
{
  "success": true,
  "address": "0:…", "address_friendly": "UQ…", "wallet_type": "v5r1",
  "state": "active", "seqno": 42, "balance_nano": "…", "usdt_raw": "…"
}

Соберите внешнее сообщение вашего кошелька:

  • внутри ровно сообщения из payment.messages в том же порядке, а при оплате нескольких заказов их сообщения подряд, заказ за заказом;
  • режим отправки 3 у каждого сообщения;
  • seqno из /v3/wallet, valid_until как в шаге 3;
  • если state не active, добавьте StateInit: первый платёж развернёт кошелёк.

Подпишите его ключом кошелька. Результат, BoC внешнего сообщения в base64, и отправляется на шаге 5. В основной сети подпись без доменного префикса и покрывает хеш ячейки.

В браузере то же можно сделать через TON Connect: передайте messages и validUntil в sendTransaction, а полученный boc отправьте на шаге 5. Проверки из шага 3 ваш код делает до вызова кошелька.

Никогда не подписывайте заказ заново, если не знаете, чем кончилась предыдущая отправка. Подписать заново можно только после ответа TRANSFER_NOT_SENT с resign: true и только с тем же seqno: тогда старое и новое сообщения не смогут пройти оба.

Шаг 5. Отправка и результат

Запрос
curl https://api.fragment-api.net/v3/orders/submit \
  -H "Authorization: Bearer $AUTH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "orders": ["<id заказа>"], "boc": "<base64 подписанного сообщения>" }'

Одно сообщение может оплатить несколько заказов: перечислите их в orders в том порядке, в каком их сообщения стоят в транзакции. Кошелёк W5 оплатит так до 127 заказов с KYC. BoC не больше 64 КБ.

До отправки сервер проверяет, что:

  • все заказы созданы этим кошельком, иначе ORDER_WALLET_MISMATCH;
  • сообщение адресовано вашему кошельку и подписано его ключом, а StateInit, если он есть, соответствует адресу;
  • каждое сообщение совпадает с запросом: адрес, сумма, payload и режим 3, ничего лишнего и ничего не пропущено;
  • valid_until ещё не наступил и не позже срока оплаты заказов;
  • заказы ваши и ещё не оплачены.

Потом сервер отправляет сообщение в сеть и ждёт его в блокчейне. Ответ при успехе:

Ответ
{
  "success": true,
  "message": "Payment completed",
  "transaction_hash": "…",
  "orders": [{ "id": "…", "ref_id": "Ref#…", "status": "success", "txid": "…" }]
}

Что делать с другими ответами:

ОтветЧто значитЧто делать
500 TRANSFER_FAILEDСеть отклонила платёж части заказовСмотрите статусы в orders: такие заказы failed
500 TRANSFER_AMBIGUOUSРезультат пока неизвестен, заказы в processingНе подписывайте заново. Проверяйте заказ позже через GET /v3/orders/{id}
503 TRANSFER_NOT_SENT, resign: trueСообщение истекло, не попав в сетьПодпишите заново с тем же seqno и отправьте
503 TRANSFER_NOT_SENTСообщение не ушло в сетьОтправьте тот же BoC ещё раз
503 TON_SERVICE_UNAVAILABLEСеть TON временно недоступна, ничего не списаноОтправьте тот же BoC позже
400 INVALID_SIGNED_MESSAGEПодписано не то, что запрошено. Причина в поле reasonИсправьте сборку сообщения
400 INSUFFICIENT_BALANCEНе хватает TON или USDT, ничего не отправлено. Сколько нужно, видно в need_ton_nano и need_usdt_rawПополните кошелёк и отправьте платёж снова: тот же BoC, если он ещё действует, или подписанный заново с тем же seqno
400 ORDER_EXPIREDСчёт Fragment истёкСоздайте заказ заново, тот же idempotency_key подойдёт

Статус заказа можно узнать в любой момент. Пока заказ в created, ответ содержит и его запрос на оплату.

Запрос
curl https://api.fragment-api.net/v3/orders/<id заказа> -H "Authorization: Bearer $AUTH_KEY"

Справочник методов

Полная схема с полями всех ответов есть в Swagger. Здесь коротко о каждом методе.

GET/v3/auth/challengeбез ключа

Одноразовая строка для подписи входа: nonce и expires_at.

POST/v3/authбез ключа

Вход по подписи ton_proof. Тело: public_key, wallet_type, fragment_cookies (по желанию) и proof. Ответ: auth_key и wallet.

DELETE/v3/authключ

Удаляет ключ, с которым пришёл запрос.

GET/v3/configбез ключа

Параметры сервиса: network, fee_wallet, fee_percent, middle_wallet, no_kyc_fee_percent, fragment_addresses, usdt_jetton_master.

GET/v3/walletключ

Ваш кошелёк: address, address_friendly, wallet_type, state (active, uninit, nonexist или frozen), seqno, balance_nano, usdt_raw.

GET/v3/user_info?username=durovключ

Ищет пользователя на Fragment: found, name, recipient_id, photo.

POST/v3/ordersключ

Создаёт заказ и запрос на оплату, см. шаг 2.

GET/v3/orders?limit=10&offset=0ключ

Ваши заказы, новые сначала. limit от 1 до 100.

GET/v3/orders/{id}ключ

Заказ, а пока он не оплачен, ещё и его запрос на оплату.

POST/v3/orders/submitключ

Оплата подписанным сообщением, см. шаг 5.

Объект заказа

ПолеЧто это
idИдентификатор заказа
ref_idНомер заказа на Fragment, вида Ref#…
statuscreated, processing, success или failed
product, amount, usernameЧто, сколько и кому
cost, currencyЦена заказа и валюта: TON или USDT
payment_method, kycСпособ оплаты и покупка с KYC или без
valid_untilДо какого времени (unix) можно оплатить счёт
txidХеш транзакции оплаты
errorПричина, если заказ failed
api_version3 для заказов v3

Статусы заказа

СтатусЧто значит
createdЗаказ создан и ждёт оплаты до valid_until
processingПлатёж отправлен, результат ещё подтверждается. Такой заказ нельзя оплачивать снова
successОплачено, Fragment получил платёж
failedСеть отклонила платёж заказа, причина в поле error

Если результат неясен, заказ остаётся в processing, пока его не сверят с блокчейном. Сервис не помечает такой заказ неудачным и не оплачивает его заново наугад.

Ошибки

Ошибка приходит с HTTP-статусом и телом {"success": false, "error_code": "…", "message": "…"}. Главные коды:

КодЧто случилосьЧто делать
AUTH_KEY_INVALIDКлюч неверный, истёк или удалёнВойдите заново
PROOF_DOMAIN, PROOF_PAYLOAD, PROOF_INVALIDПодпись входа не принятаЗапросите новый nonce и подпишите заново
INVALID_FRAGMENT_COOKIESCookie не подходятВыгрузите cookie заново
FRAGMENT_COOKIES_REQUIREDЗаказ с KYC без cookieПередайте cookie при входе или закажите без KYC
INVALID_AMOUNT, INVALID_DURATION, INVALID_USERNAME_FORMAT, INVALID_PAYMENT_METHODНеверные поля заказаИсправьте запрос
ORDER_NOT_POSSIBLEFragment не может выполнить этот заказ, например из-за минимума для получателяНе повторяйте, причина в message
ORDER_CREATION_FAILEDВременный сбой на стороне FragmentПовторите позже с тем же idempotency_key
NO_KYC_UNAVAILABLEПокупка без KYC сейчас недоступнаПопробуйте позже
NO_KYC_USDT_UNAVAILABLEОплата USDT без KYCЗаказы без KYC оплачиваются в TON
WALLET_NOT_CONNECTED_TO_FRAGMENTUSDT не с того кошелькаПлатите USDT с кошелька, привязанного к аккаунту Fragment
IDEMPOTENCY_KEY_HELD_BY_V2_ORDERКлюч занят неоплаченным заказом v2Оплатите его через v2 или дождитесь, пока он истечёт
ORDER_WALLET_MISMATCHЗаказ создан другим кошелькомОплачивайте тем кошельком, которым создавали
ORDER_ALREADY_PROCESSING, ORDER_ALREADY_PROCESSEDЗаказ уже оплачивается или оплаченПроверьте его статус
ORDER_EXPIREDСчёт истёкСоздайте заказ заново
INSUFFICIENT_BALANCEНе хватает средствПополните кошелёк
INVALID_SIGNED_MESSAGEПодписано не то, что запрошеноСмотрите поле reason
TRANSFER_NOT_SENTПлатёж не ушёл в сетьСм. шаг 5
TRANSFER_FAILEDСеть отклонила платёжСмотрите статусы заказов
TRANSFER_AMBIGUOUSРезультат неясенНе платите заново, проверьте заказ позже
TON_SERVICE_UNAVAILABLE, BALANCE_CHECK_ERRORВременный сбой, ничего не списаноПовторите тот же запрос позже
HTTPS_REQUIREDЗапрос пришёл по http://Используйте https://

Только в SDK:

КодЧто случилосьЧто делать
UNTRUSTED_PAYMENTSDK отказался подписывать платёжНичего не отправлено. Проверьте настройки trust: лимит на заказ обязателен
SUBMIT_OUTCOME_UNKNOWNAPI не ответил на отправкуОтправьте тот же подготовленный платёж позже, не подписывайте заново

Повторы без двойной оплаты

  • Создание заказа. Передавайте idempotency_key. Повтор с тем же ключом вернёт тот же заказ, даже после перезапуска вашего сервера. Ключи привязаны к вашему кошельку и с ключами других клиентов не пересекаются.
  • Отправка платежа. Тот же BoC можно отправлять сколько угодно раз: сеть применит его один раз, а сервер ответит текущим состоянием заказов.
  • Неизвестный исход. После TRANSFER_AMBIGUOUS или потерянного ответа не подписывайте заказ заново. Проверяйте его статус, пока он не станет success или failed.
  • Подпись заново. Только после TRANSFER_NOT_SENT с resign: true и только с тем же seqno.
  • Один кошелёк, один платёж за раз. Каждый платёж занимает следующий seqno. Одновременные заказы собирайте в одну транзакцию или ставьте в очередь.

Кошельки и пакетная оплата

КошелёкСообщений в транзакцииЗаказов с KYC за разSubwallet
v4r2до 42698983191
v5r1 (W5)до 255до 127wallet id 2147483409 в основной сети

Workchain 0, стандартный subwallet. Новый кошелёк разворачивается первым платежом: пока он не active, сообщение должно нести StateInit.

Если заказов много, берите W5: одна транзакция оплатит больше сотни заказов, и сетевых комиссий будет меньше.

Комиссии

  • Со своим аккаунтом Fragment: 2% от заказа отдельным сообщением fee в той же транзакции, что и оплата.
  • Без KYC: 3% от заказа, они входят в единственный перевод на кошелёк сервиса.
  • Сетевые комиссии TON платит ваш кошелёк, как при любом переводе.
  • Текущие проценты показывает GET /v3/config. SDK не подпишет заказ с комиссией выше maxFeePercent.

Переход с v2

v1 и v2 отправляют сид-фразу на сервер. Они устарели и будут отключены. Их ответы уже несут заголовки Deprecation: true и Link на это руководство, а когда назначена дата отключения, ещё и Sunset. После отключения они отвечают 410 с кодом API_VERSION_GONE.

v2 (FragmentAPIClient)v3 (FragmentAPIv3)
new FragmentAPIClient(...) и auth(cookies, seed)new FragmentAPIv3({ mnemonic, walletType, fragmentCookies })
buyStars(username, amount, authKey, ...)buy({ product: "stars", username, amount })
buyStarsWithoutKYC(username, amount, authKey)buy({ product: "stars", username, amount, kyc: false })
buyPremium(username, months, ...)buy({ product: "premium", username, amount: months })
buyTon(username, amount, ...)buy({ product: "ton", username, amount })
createStarsOrder, payStarsOrder, getStarsOrderStatuscreateOrder, payOrders, getOrder
getUserInfo(username)userInfo(username)
getBalance(authKey)walletInfo()
getOrders(authKey, limit, offset)listOrders(limit, offset)

Для v3 советуем завести новый кошелёк: сид-фраза старого уже отправлялась на сервер, а в v3 она не покидает ваш компьютер. Заказы старого кошелька остаются под ним, по запросу мы привяжем их к новому.

Помощь

Вопросы по интеграции и комиссиям задавайте разработчику в Telegram. Все эндпоинты с полями ответов собраны в Swagger, а исходный код SDK открыт.