TON-дағы USDT төлемін қалай сенімді анықтауға болады
TON-да USDT төлемін анықтау: get_transactions арқылы жетонның transfer_notification талдауы, get_jetton_info-дан decimals, соманы салыстыру, идемпотенттілік.
TON-да төлем қабылдауды бір рет болса да құрған әркім сол бір қақпанға тап болған: төлем түсті, ал бэкенд оны «көрмеді». Клиент 50 USDT жіберді, әмиян түсімді көрсетіп тұр, ал инвойс әлі «күтуде» күйінде ілініп тұр. Он минуттан кейін — қолдау қызметіне ашулы тикет. Тағы бір сағаттан соң төлемді екі рет есепке алғаныңыз белгілі болады, өйткені поллер ретрайда қайта іске қосылған. TON-дағы кіріс USDT төлемін (incoming USDT payment) сенімді анықтау деген — «минутына бір рет баланс тексеру» емес, жетонның нақты ішкі хабарламаларын идемпотенттілікті және жалғаннан қорғанысты ескере отырып талдау. Мұны қалай дұрыс істеу керегін қадам-қадам қарастырамыз — TONNode MCP-серверінің жергілікті әрі тегін шақыруға болатын нақты құралдарында.
TON-дағы USDT төлемін анықтау неге әмиян балансы туралы емес
Аңғал тәсіл — әмиян балансын мезгіл-мезгіл оқып, ол өссе, төлем қабылданды деп есептеу. Бұл — жәшіктегі жалпы сомаға ғана қарай алатын кассаға ұқсайды: +10 USDT түсті — бірақ кімнен, қай шот бойынша, ескі тапсырыс үшін бе, әлде жаңасы үшін бе? Ал егер екі клиент бір мезгілде 10-нан төлесе, сіз бір-ақ қимылмен +20 көресіз.
TON-да мұндай тәсіл бірден бірнеше себеппен күйрейді:
- USDT — нативті монета емес, жетон (TEP-74 стандарты). Қаражат негізгі әмиянға тікелей емес, оның жетон-әмиянына түседі — бұл нақты бір жетонның мастер-контрактісіне байланған бөлек контракт. Негізгі әмиянның GRAM-дағы балансы бұл кезде мүлдем өзгермейді.
- Баланс дельтасы кімнің не үшін төлегенін айтпайды. Баланста инвойспен байланыс сақталмайды.
- Жарыс пен агрегация. Екі сұрау аралығында бірнеше төлем түседі — сіз жиынтық дельтаны көресіз, жеке оқиғаларды емес.
- Қосарланған есепке алу. Поллинг ретрайы немесе воркердің қайта іске қосылуы — бір төлем екі рет есептеліп кетеді.
- Decimals. USDT-де олар 6, жетондар үшін әдеттегі 9 емес. Бір бөлгіш қате — клиентке 1000 есе жаңылыс «есептеліп» қалады.
Дұрыс жол — баланспен емес, алушының жетон-әмиянының транзакциялар ағынымен жұмыс істеп, әр аударым оқиғасын бөлек талдау. Оқудың бәрі жергілікті әрі тегін қолжетімді:
{ "mcpServers": { "ton": { "command": "npx", "args": ["-y", "@tonnode/mcp"] } } }
Кіріс USDT төлемі іште қалай көрінеді: жетонның internal_transfer-і (op 0x178d4519)
Біреу сізге USDT аударғанда мынадай тізбек орындалады:
- Жіберушінің жетон-әмияны соманы шегеріп, сіздің жетон-әмияныңызға
internal_transferinternal-хабарламасын жібереді (op0x178d4519). - Сіздің жетон-әмияныңыз қаражатты есепке алады және — тек жіберуші
forward_ton_amount > 0қосқан жағдайда ғана — қосымша сіздің негізгі әмияныңызғаtransfer_notificationішкі хабарламасын жібереді (op0x7362d09c).
Басты айырық осында жасырынып жатыр. transfer_notification — «сізге жетон түсті» деген ыңғайлы хабарлама, бірақ ол негізгі иеленуші-әмиянға және тек forward_ton_amount > 0 болғанда ғана кетеді. Егер жіберуші нөл қойса — жетон-әмиян қаражатты бәрібір есепке алады, бірақ иесіне хабарлама болмайды.
Ал жетон-әмиянның өз тарихында бұл есепке алу әрқашан тіркеледі — forward_ton_amount-қа тәуелсіз, кіріс internal_transfer түрінде. Сондықтан сенімді анықтау алушының жетон-әмиянының тарихын сұрауға және дәл internal_transfer-ді талдауға негізделеді. Оның TEP-74 бойынша құрылымы:
internal_transfer#178d4519
query_id: uint64
amount: (VarUInteger 16) // жетонның raw-бірліктері
from: MsgAddress // ТӨЛЕУШІ-иеленушінің мекенжайы (адам)
response_address: MsgAddress
forward_ton_amount: (VarUInteger 16)
forward_payload: (Either Cell ^Cell) // түсініктеме/memo
Екі маңызды тұс:
fromөрісі — бұл нақты төлеушінің (адам-иеленушінің) мекенжайы, оның жетон-әмиянының мекенжайы емес. Дәл соны жіберуші ретінде логқа жазу керек. Назар аударыңыз: хабарлама деңгейіндегі мекенжай (tx.in_msg.source) мұнда — төлеушінің жетон-әмияны, ал адам-төлеуші хабарлама денесіндегіfromөрісінде тұр.amountөрісіндегі сома — raw-бірліктерде, қайта есептеуге төменде ораламыз.
Осыдан шығатын басты архитектуралық қорытынды
Сенімді анықтау негізгі әмияндағы notification-ды күтуге емес, дәл алушының жетон-әмиянының тарихын сұрауға негізделеді. Оның тарихында есепке алу forward_ton_amount-қа тәуелсіз әрқашан көрінеді. Егер қосымша негізгі әмиянды да тыңдасаңыз, онда transfer_notification-ды ұстайсыз (op 0x7362d09c, жіберуші — sender өрісінде) — бірақ бұл тек forward_ton_amount > 0 жағдайына арналған бонус, шындықтың жалғыз көзі емес.
get_transactions арқылы тарихты оқып, internal_transfer-ді талдаймыз
Алдымен алушының жетон-әмиянының мекенжайы керек. Оны офлайн есептеп шығармаңыз және тикерге сенбеңіз — USDT мастер-контрактісінің өзінен сұраңыз: USDT мастерінде get_wallet_address шақыруы (run_get_method арқылы) сіздің USDT жетон-әмияныңыздың мекенжайын, шынайы мастерге байланған күйінде, детерминистік түрде қайтарады. Содан кейін осы жетон-әмиянның транзакцияларын get_transactions арқылы сұраймыз.
Поллинг циклындағы агентке арналған промпт:
EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs мекенжайындағы
USDT мастерінде run_get_method шақыр: әдіс — get_wallet_address,
аргумент — UQ...myservice иеленуші мекенжайы.
Біздің USDT жетон-әмиянымыздың мекенжайын қайтар. Содан кейін
get_transactions арқылы осы жетон-әмиянның соңғы транзакцияларын
оқы. Әрбір кіріс internal_transfer (op 0x178d4519) үшін query_id,
amount (raw), from өрісін (төлеушінің мекенжайы), forward_payload
ішіндегі мәтіндік түсініктемені, сондай-ақ транзакцияның lt және hash
мәндерін қайтар.
Бір транзакцияны өңдеудің псевдокоды:
for (const tx of txs) {
const body = tx.in_msg?.decoded; // кіріс хабарламаның талданған денесі
if (body?.op !== 0x178d4519) continue; // internal_transfer емес — өткізіп жібереміз
const rawAmount = BigInt(body.amount); // raw-бірліктер, адам оқитын түрде ЕМЕС
const payer = body.from; // төлеуші-иеленушінің мекенжайы
const memo = parseComment(body.forward_payload);
const key = `${tx.lt}:${tx.hash}`; // дедуп үшін идентификатор
// …салыстыру мен есепке алу төменде
}
Сондай-ақ негізгі аккаунттың күйін get_account_state арқылы бір рет тексеріп алған пайдалы (белсенді ме, соңғы транзакция қашан болған) — бұл поллердің «тірі» екенін және артта қалып қоймағанын түсінуге көмектеседі.
Бәрін decimals шешеді: get_jetton_info және raw-соманы қайта есептеу (USDT = 6, 9 емес)
Аударымдағы amount өрісі — жетонның raw-бірліктерінде, үтірсіз бүтін сан. Оны адам оқитын сомаға айналдыру үшін decimals керек. Дәл осы жерде интеграциялардың басым бөлігі бұзылады.
Жетондардың көпшілігінде decimals = 9. TON-дағы USDT-де (Tether) — decimals = 6. Яғни:
1 USDT = 1 000 000 raw (10^6, 10^9 емес)
Егер әдетпен USDT-дің raw-сомасын 10^9-ға бөлсеңіз, клиенттің 50 USDT төлемі сізде 0.05-ке айналады — 1000 есе аз. Кері бағыттағы қате дәл солай оңай 1000 есе көп есептеп жібереді. Бөлгішті хардкод жасамаңыз — decimals-ті get_jetton_info арқылы он-чейн метадеректерден алыңыз:
const info = await getJettonInfo(USDT_MASTER); // name, symbol, decimals, эмиссия
const decimals = info.decimals; // USDT үшін = 6
const human = Number(rawAmount) / 10 ** decimals; // 50000000 → 50.0
decimals-ті кэште тикер бойынша емес, мастер мекенжайы бойынша ұстаңыз: тикерді қолдан жасауға болады, ал decimals-ті нақты контрактпен байланыстыру керек. Ереже қарапайым: decimals әрқашан get_jetton_info-дан, ал сома көрсету сәтіне дейін тек BigInt/raw түрінде.
Шығу тегін, соманы және түсініктемені салыстырамыз — жалған жетондарды кесіп тастаймыз
Енді қауіпсіздік үшін ең маңыздысы. Мыналарсыз есепке алуға болмайтын тексерулер.
1. Мастер-контракт — тек шынайы USDT. Кез келген адам «USDT» атауы мен «USD₮» символы бар жетон шығарып, сізге 1000 «USDT»-ға аударым жібере алады. Егер төлемді тикер бойынша сәйкестендірсеңіз — сізді бес минутта алдап кетеді. Шынайы Tether-ге жалғыз сенімді байланыс — шынайы USDT мастер-контрактісі сіздің иеленушіңіз үшін есептеп шығарған жетон-әмиянмен жұмыс істеу:
Tether USD₮ master: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
Дәл сондықтан жетон-әмиянның мекенжайын осы мастердегі get_wallet_address он-чейн шақыруынан аламыз (жоғарыда қараңыз), ал тарихты тек осы — басқа ешбір емес — контракттан сұраймыз. Оған есепке алынған кез келген internal_transfer сол мастердің заңды бауырлас жетон-әмиянынан келетініне кепілдік бар: жалған «USDT» басқа мастердің әмияндарында өмір сүреді және сіздің мекенжайыңызға жетпейді. Сондықтан бұл архитектурада tx.in_msg.source-ты бір нәрсемен салыстырудың қажеті жоқ — қорғанысты бақылау нүктесін таңдаудың өзі қамтамасыз етеді. Және ескеріңіз: parse_address — бұл тек EQ/UQ/raw форматтарын офлайн түрлендіру, ол жетон-әмиянның мекенжайын есептеп шығармайды; ол үшін дәл get_wallet_address он-чейн шақыруы қажет. Оны салыстыру алдында мекенжайларды нормализациялау үшін қолданған ыңғайлы — бір мекенжайдың әртүрлі көрінісін бөлек-бөлек деп есептеп қалмау үшін.
2. Инвойспен сәйкестендіруге арналған түсініктеме (memo). Мәтіндік түсініктеме forward_payload ішінде стандартты форматта сақталады: 0x00000000 op-префиксі (4 нөлдік байт) + UTF-8 жол. Соның көмегімен кіріс аударымды нақты тапсырыспен байланыстырасыз.
3. Сома мен төлеуші. Қайта есептелген соманы инвойс бойынша күтілетін сомамен салыстырыңыз; қажет болса, тарих пен антифрод үшін from (төлеушінің мекенжайы) мәнін тіркеп қойыңыз.
// 1. Шығу тегіне кепілдік берілген: біз ДӘЛ шынайы USDT мастерінің
// get_wallet_address-і қайтарған жетон-әмиянның тарихын
// оқимыз — демек, мұндағы кез келген аударым шынайы.
// 2. Түсініктеме → инвойс
const invoice = invoices.get(memo);
if (!invoice) continue;
// 3. Сома сәйкес пе?
if (human < invoice.expectedAmount) markUnderpaid(invoice);
// 4. Төлеуші — логтар/антифрод үшін
log({ payer, memo, human, lt: tx.lt, hash: tx.hash });
Идемпотенттілік: төлемді екі рет есепке алмау үшін lt + hash бойынша дедуп
Поллинг әрқашан өз-өзімен қиылысады: кестемен іске қосылады, құлайды, ретрай жасайды, сұрау терезелері бір-бірін қабаттастырады. Дедупсыз ертелі-кеш бір төлемді екі рет есепке аласыз.
TON-да әр транзакция logical time (lt) + hash жұбымен бірегей түрде идентификацияланады. Бұл — сіздің табиғи идемпотенттілік кілтіңіз:
const key = `${tx.lt}:${tx.hash}`;
if (await seen.has(key)) continue; // өңдеп қойғанбыз — шығамыз
await creditInvoice(invoice, human); // есепке алу
await seen.add(key); // сол ДҚ транзакциясында тіркейміз
Кілт ретінде аударымдағы query_id-ді де қолдануға болады, бірақ (lt, hash) жетон-әмиянның кез келген кіріс транзакциясы үшін, оның ішінде forward_ton_amount = 0 жағдайында да жұмыс істейді. Өңделген кілттерді персистентті сақтаңыз және есепке алуды бірегейлікті тексерумен бірге бір ДҚ транзакциясында жасаңыз — сонда параллель воркерлер дубль жасамайды.
Мезгіл-мезгіл реконсиляция жасаған жөн: N минут сайын жетон-әмиянның нақты балансын (get_jetton_balance арқылы — ол мекенжайды он-чейн есептеп, балансты бір шақырумен береді) есепке алынған барлық төлемдердің сомасымен салыстырыңыз. Алшақтық — бір жерде аударым өткізіп жіберілгенінің немесе қосарланғанының белгісі, тіпті бір ғана поллинг циклы бұзылған жағдайда да.
Жүктеме кезіндегі тұрақты throughput: ашық лимиттердің орнына өз кілтіңіз
Төлем поллингі — бұл тұрақты, біркелкі сұраныс ағыны: N әмиян × сұрау жиілігі. Дәл осы жерде ашық инфрақұрылым тар өткелге айналады.
Глобал конфигтегі ашық лайтсерверлер — ортақ әрі лимитті: жүктеме кезінде not ready деп жауап береді немесе ADNL-таймаутқа кетеді. Кілтсіз ашық HTTP-API-лар (toncenter, tonapi.io) секундына шамамен 1 сұрау ұстайды және лимиттен асқанда адал HTTP 429 «Too Many Requests» қайтарады. Байқаңыз: лимиттің нақты коды — дәл 429; TON қауымдастығында жүрген мемдік «228» емес — ол API коды емес. Тарихты бірнеше секунд сайын сұрайтын касса үшін бұл лимиттер өткізіп жіберілген және кешіккен есепке алуларды білдіреді. Өз кілті бар провайдерді қалай таңдау керегі — жеке талдауда.
Төлемдерді анықтауға қажет оқу құралдарының бәрі — get_transactions, run_get_method, get_jetton_balance, get_jetton_info, parse_address, get_account_state — жергілікті әрі тегін қолжетімді: npx -y @tonnode/mcp. @tonnode/mcp пакеті — open source (MIT), TON-ның нативті ADNL-протоколы бойынша HTTP-қабаттарсыз жұмыс істейді. Поллинг продқа шыққанда және кепілдендірілген throughput керек болғанда, өз кілтіңізбен hosted-эндпоинт қосылады:
{
"mcpServers": {
"ton": {
"type": "http",
"url": "https://mcp.tonnode.io/mcp",
"headers": { "Authorization": "Bearer tn_live_…" }
}
}
}
Тарифтер тек минутына сұраныс лимитімен ғана ерекшеленеді — 16 құралдың бәрі әрқайсысында қолжетімді:
- Hobby — мәңгі тегін, минутына 60 сұраныс. Кілт кірген бойда, картасыз беріледі.
- Pro — айына $29, минутына 300 сұраныс.
- Scale — айына $199, минутына 1200 сұраныс.
Төлем поллингін бастау үшін тегін 60 сұраныс/мин 429-сыз тұрақты сұрау ұстауға жетеді. Егер осының үстіне төлемдерді өзі талдайтын ЖИ-агент құрып жатсаңыз — оның қалай ұйымдастырылғаны TON-дағы ЖИ-агенттерге арналған MCP нұсқаулығында талданған.
Тегін Hobby кілтінен бастаңыз — төлем поллингіне 60 req/min, картасыз: tonnode.io/dashboard?plan=hobby. Жүктеме өсіп, бір воркер жеткіліксіз болғанда — Pro мен Scale лимиттерін салыстырыңыз.
TON-дағы USDT-ні сенімді анықтаудың чек-парағы:
- Негізгі әмиянның балансын емес, алушының жетон-әмиянының тарихын
get_transactionsарқылы сұраңыз. - Осы жетон-әмиянның мекенжайын шынайы USD₮ мастерінде (
EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs)get_wallet_address(run_get_method) он-чейн шақыруынан алыңыз — сонда оған келген кез келген аударым міндетті түрде шынайы USDT болады. 0x178d4519op-ы бар кірісinternal_transfer-лерді іздеңіз — дәл соларforward_ton_amount-қа тәуелсіз жетон-әмиянның тарихында әрқашан сақталады.transfer_notification(0x7362d09c) негізгі әмиянға және текforward_ton_amount > 0болғанда ғана кетеді.- Төлеушінің мекенжайын
fromөрісінен, соманыamount-тан (raw) алыңыз. decimals-тіget_jetton_info-дан алыңыз. USDT = 6, 9 емес.- Инвойсты
forward_payloadішіндегі түсініктеме бойынша сәйкестендіріңіз (op0x00000000+ UTF-8). (lt, hash)бойынша дедуп — қатаң түрде бір рет қана есепке алыңыз.- Тұрақты throughput-ты ашық лимиттермен емес, өз кілтіңізбен ұстаңыз.
Агентіңізге TON-ға қолжетімділік беріңіз
16 MCP-құрал: оқу, кастодиалды емес сваптар, кроссчейн және әмияндар. Тегін тариф — 60 сұрау/мин, карта керек емес.