Как устроен v3
В v3 платёж подписываете вы. Сервер готовит его, проверяет подпись и следит за оплатой, но ключа от вашего кошелька у него нет.
- ВходВы подписываете кошельком одноразовую строку от сервера (формат TON Connect
ton_proof) и получаете ключ доступа. Ни регистрации, ни API-ключа. - ЗаказAPI находит получателя на Fragment, берёт счёт и возвращает запрос на оплату: какие сообщения отправить, на какие адреса и до какого времени.
- Проверка и подпись у васВаш код сверяет запрос с тем, что вы заказывали, и подписывает его своим ключом. SDK делает это сам.
- Отправка и подтверждение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.
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);
import os from fragment_api_lib.v3 import FragmentAPIv3 api = FragmentAPIv3( mnemonic=os.environ["TON_SEED"], # 24 слова, нужны только для подписи здесь wallet_type="v5r1", # ваш кошелёк: "v4r2" или "v5r1" (W5) fragment_cookies=os.environ["FRAGMENT_COOKIES"], # ваш аккаунт Fragment, без KYC не нужен trust={"max_ton_per_order": 50}, # не подписывать заказ дороже 50 TON ) print("Платим с кошелька", api.address) # проверьте, что это ваш адрес result = api.buy("stars", "durov", 50, idempotency_key="myshop:1001") print(result["success"], result["orders"][0]["ref_id"], result.get("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.js | Python | По умолчанию | Что это |
|---|---|---|---|
mnemonic | mnemonic | 24 слова. Нужны только для подписи на вашей стороне | |
walletType | wallet_type | "v4r2" | Версия кошелька: "v4r2" или "v5r1" (W5) |
fragmentCookies | fragment_cookies | Cookie аккаунта Fragment, нужны для заказов с KYC | |
trust.maxTonPerOrder | max_ton_per_order | Обязательно. Заказ дороже этой суммы в TON не будет подписан | |
trust.maxUsdtPerOrder | max_usdt_per_order | То же в USDT. Обязательно для оплаты в USDT | |
trust.maxFeePercent | max_fee_percent | 5 | Потолок комиссии сервиса в процентах от заказа |
trust.feeWallets, middleWallets, fragmentAddresses | fee_wallets, middle_wallets, fragment_addresses | Ваши адреса в дополнение к встроенным в SDK | |
trust.usdtWallet | usdt_wallet | Ваш кошелёк USDT, если он нестандартный | |
trust.trustServerConfig | trust_server_config | false | С true SDK принимает кошельки, которые назовёт сервер. Только для тестов |
externalTtlSeconds | external_ttl_seconds | 120 | Сколько секунд действует подписанный платёж, не больше 300 |
baseUrl | base_url | https://api.fragment-api.net | Адрес API |
timeout | None | HTTP-таймаут. None ждёт ответа, пока платёж подтверждается |
В Python параметры trust передаются словарём: trust={"max_ton_per_order": 50}.
Поля заказа
| Node.js | Python | Что это |
|---|---|---|
product | product | "stars", "premium" или "ton" |
username | username | Юзернейм в Telegram: durov, @durov или https://t.me/durov |
amount | amount | Звёзды (от 50), TON (от 1) или месяцы Premium (3, 6, 12) |
kyc | kyc | По умолчанию да: покупка с вашего аккаунта Fragment. Нет: через сервис, оплата в TON |
paymentMethod | payment_method | "ton" (по умолчанию) или "usdt_ton", только с KYC |
showSender | show_sender | Показывать вас отправителем, по умолчанию да |
idempotencyKey | idempotency_key | Ваш идентификатор заказа, например "myshop:1001". Очень советуем |
customOrderInfo | custom_order_info | Любая заметка для себя |
Методы
| Node.js | Python | Что делает |
|---|---|---|
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() | Удаляет ключ доступа на сервере |
address | address | Адрес вашего кошелька (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 });
api = FragmentAPIv3(mnemonic=os.environ["TON_SEED"], wallet_type="v5r1", trust={"max_ton_per_order": 50}) api.buy("premium", "durov", 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" });
api = FragmentAPIv3(
mnemonic=os.environ["TON_SEED"],
wallet_type="v5r1",
fragment_cookies=os.environ["FRAGMENT_COOKIES"],
trust={"max_ton_per_order": 50, "max_usdt_per_order": 100},
)
api.buy("stars", "durov", 500, payment_method="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);
orders = [] for username in ["alice", "bob", "carol"]: created = api.create_order("stars", username, 50, idempotency_key=f"myshop:{username}:50") api.check_order(created) # плохой заказ отклоняется один, а не вместе со всей пачкой orders.append(created) result = api.pay_orders(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"));
created = api.create_order("stars", "durov", 50, idempotency_key="myshop:1002") prepared = api.prepare([created]) db.save("payment:myshop:1002", prepared) # ваше хранилище result = api.submit(prepared, [created]) # после перезапуска, с тем же подготовленным платежом: # api.submit(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); } }
from fragment_api_lib.exceptions import FragmentAPIError try: api.buy("stars", "durov", 50, idempotency_key="myshop:1003") except FragmentAPIError as e: code = getattr(e, "error_code", None) if code in ("TRANSFER_AMBIGUOUS", "SUBMIT_OUTCOME_UNKNOWN"): pass # деньги могли уйти: сверьте заказ позже и не покупайте его снова else: print(code, e)
Без 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.
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 проверяет следующее, и свой клиент должен делать то же:
- Заказ тот, который вы просили: товар, количество, получатель, с KYC или без, валюта. Сверяйте с вашим запросом, а не с тем, что вернул сервер. Набор сообщений следует из этого: заказу с KYC никогда не нужно сообщение
middle. - Сообщение
fragmentидёт только на адрес Fragment из вашего собственного списка, а не из ответа сервера. При оплате в USDT этот адрес должен быть получателем внутри payload, а само сообщение идёт на ваш кошелёк USDT. - Сообщение с переводом USDT идёт только на ваш собственный кошелёк USDT. Его адрес вы вычисляете сами из мастер-контракта USDT и кода кошелька: иначе сервер мог бы подставить кошелёк другого вашего токена.
- Сообщения
feeиmiddleидут только на кошельки сервиса, которые вы закрепили у себя.GET /v3/configих показывает, но источником доверия он быть не должен. - Комиссия не больше вашего потолка (в SDK 5% от заказа) в той же валюте.
- Заказ не дороже вашего лимита в TON, а при оплате в USDT и лимита в USDT. Что именно покупает счёт Fragment, проверить нельзя, поэтому лимит на заказ обязателен.
- Платёж действует недолго:
valid_until = min(payment.valid_until, сейчас + 120 с), режим отправки 3.
Адреса Fragment, закреплённые в SDK:
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. Здесь коротко о каждом методе.
/v3/auth/challengeбез ключаОдноразовая строка для подписи входа: nonce и expires_at.
/v3/authбез ключаВход по подписи ton_proof. Тело: public_key, wallet_type, fragment_cookies (по желанию) и proof. Ответ: auth_key и wallet.
/v3/authключУдаляет ключ, с которым пришёл запрос.
/v3/configбез ключаПараметры сервиса: network, fee_wallet, fee_percent, middle_wallet, no_kyc_fee_percent, fragment_addresses, usdt_jetton_master.
/v3/walletключВаш кошелёк: address, address_friendly, wallet_type, state (active, uninit, nonexist или frozen), seqno, balance_nano, usdt_raw.
/v3/user_info?username=durovключИщет пользователя на Fragment: found, name, recipient_id, photo.
/v3/ordersключСоздаёт заказ и запрос на оплату, см. шаг 2.
/v3/orders?limit=10&offset=0ключВаши заказы, новые сначала. limit от 1 до 100.
/v3/orders/{id}ключЗаказ, а пока он не оплачен, ещё и его запрос на оплату.
/v3/orders/submitключОплата подписанным сообщением, см. шаг 5.
Объект заказа
| Поле | Что это |
|---|---|
id | Идентификатор заказа |
ref_id | Номер заказа на Fragment, вида Ref#… |
status | created, processing, success или failed |
product, amount, username | Что, сколько и кому |
cost, currency | Цена заказа и валюта: TON или USDT |
payment_method, kyc | Способ оплаты и покупка с KYC или без |
valid_until | До какого времени (unix) можно оплатить счёт |
txid | Хеш транзакции оплаты |
error | Причина, если заказ failed |
api_version | 3 для заказов 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_COOKIES | Cookie не подходят | Выгрузите cookie заново |
FRAGMENT_COOKIES_REQUIRED | Заказ с KYC без cookie | Передайте cookie при входе или закажите без KYC |
INVALID_AMOUNT, INVALID_DURATION, INVALID_USERNAME_FORMAT, INVALID_PAYMENT_METHOD | Неверные поля заказа | Исправьте запрос |
ORDER_NOT_POSSIBLE | Fragment не может выполнить этот заказ, например из-за минимума для получателя | Не повторяйте, причина в message |
ORDER_CREATION_FAILED | Временный сбой на стороне Fragment | Повторите позже с тем же idempotency_key |
NO_KYC_UNAVAILABLE | Покупка без KYC сейчас недоступна | Попробуйте позже |
NO_KYC_USDT_UNAVAILABLE | Оплата USDT без KYC | Заказы без KYC оплачиваются в TON |
WALLET_NOT_CONNECTED_TO_FRAGMENT | USDT не с того кошелька | Платите 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_PAYMENT | SDK отказался подписывать платёж | Ничего не отправлено. Проверьте настройки trust: лимит на заказ обязателен |
SUBMIT_OUTCOME_UNKNOWN | API не ответил на отправку | Отправьте тот же подготовленный платёж позже, не подписывайте заново |
Повторы без двойной оплаты
- Создание заказа. Передавайте
idempotency_key. Повтор с тем же ключом вернёт тот же заказ, даже после перезапуска вашего сервера. Ключи привязаны к вашему кошельку и с ключами других клиентов не пересекаются. - Отправка платежа. Тот же BoC можно отправлять сколько угодно раз: сеть применит его один раз, а сервер ответит текущим состоянием заказов.
- Неизвестный исход. После
TRANSFER_AMBIGUOUSили потерянного ответа не подписывайте заказ заново. Проверяйте его статус, пока он не станетsuccessилиfailed. - Подпись заново. Только после
TRANSFER_NOT_SENTсresign: trueи только с тем жеseqno. - Один кошелёк, один платёж за раз. Каждый платёж занимает следующий
seqno. Одновременные заказы собирайте в одну транзакцию или ставьте в очередь.
Кошельки и пакетная оплата
| Кошелёк | Сообщений в транзакции | Заказов с KYC за раз | Subwallet |
|---|---|---|---|
v4r2 | до 4 | 2 | 698983191 |
v5r1 (W5) | до 255 | до 127 | wallet 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, getStarsOrderStatus | createOrder, payOrders, getOrder |
getUserInfo(username) | userInfo(username) |
getBalance(authKey) | walletInfo() |
getOrders(authKey, limit, offset) | listOrders(limit, offset) |
Для v3 советуем завести новый кошелёк: сид-фраза старого уже отправлялась на сервер, а в v3 она не покидает ваш компьютер. Заказы старого кошелька остаются под ним, по запросу мы привяжем их к новому.
Помощь
Вопросы по интеграции и комиссиям задавайте разработчику в Telegram. Все эндпоинты с полями ответов собраны в Swagger, а исходный код SDK открыт.