TONda kiruvchi USDT-toʻlovni qanday ishonchli aniqlash
TONda USDT-toʻlovni aniqlash: get_transactions orqali jetton transfer_notification tahlili, get_jetton_info dan decimals, summani tekshirish va idempotentlik.
TONda toʻlov qabul qilishni bir marta boʻlsa ham qurgan har bir odam bir xil tuzoqqa duch kelgan: toʻlov keldi, backend esa uni «koʻrmadi». Mijoz 50 USDT joʻnatdi, hamyon tushumni koʻrsatib turibdi, invoys esa hamon «kutilmoqda» statusida osilib yotibdi. Oʻn daqiqadan keyin — qoʻllab-quvvatlash xizmatiga gʻazabli tiket. Yana bir soatdan soʻng esa maʼlum boʻladiki, siz toʻlovni ikki marta hisobga olib qoʻygansiz, chunki poller qayta urinishda (retry) yana ishlab ketgan. TONda kiruvchi USDT-toʻlovni (incoming USDT payment) ishonchli aniqlash — bu «daqiqada bir marta balansni tekshirish» emas, balki jettonning aniq ichki xabarlarini idempotentlik va soxtalashtirishdan himoya bilan tahlil qilishdir. Buni qanday toʻgʻri bajarishni bosqichma-bosqich koʻrib chiqamiz — TONNode MCP-serverining real vositalarida, ularni lokalda bepul chaqirish mumkin.
Nega TONda USDT-toʻlovni aniqlash hamyon balansi haqida emas
Sodda yondashuv — vaqti-vaqti bilan hamyon balansini oʻqib turish va agar u oshgan boʻlsa, toʻlovni qabul qilingan deb hisoblash. Bu faqat qutidagi umumiy summaga qaray oladigan kassaga oʻxshaydi: +10 USDT keldi — lekin kimdan, qaysi hisob boʻyicha, eski buyurtma uchunmi yoki yangisi uchunmi? Agar ikkita mijoz bir vaqtda 10 tadan toʻlasa, siz buni yagona +20 boʻlib koʻrasiz, xolos.
TONda bunday yondashuv bir vaqtning oʻzida bir necha sababga koʻra qulaydi:
- USDT — nativ tanga emas, balki jetton (TEP-74 standarti). Mablagʻ asosiy hamyonga toʻgʻridan-toʻgʻri emas, uning jetton-hamyoniga — jettonning aniq master-kontraktiga bogʻlangan alohida kontraktga keladi. Asosiy hamyonning GRAMdagi balansi esa bunda umuman oʻzgarmaydi.
- Balans deltasi kim va nima uchun toʻlaganini aytmaydi. Balans invoysga bogʻlanishni saqlamaydi.
- Poyga va agregatsiya. Ikki soʻrov oraligʻida bir nechta toʻlov keladi — siz alohida hodisalarni emas, umumiy deltani koʻrasiz.
- Ikki marta hisobga olish. Pollingning qayta urinishi yoki workerning qayta ishga tushishi — va bitta toʻlov ikki marta hisoblanadi.
- Decimals. USDTda ular 6 ta, jettonlar uchun odatiy boʻlgan 9 ta emas. Bitta notoʻgʻri boʻluvchi — va mijozning hisobiga 1000 barobar xato summa «tushadi».
Toʻgʻri yoʻl — balans bilan emas, oluvchining jetton-hamyonidagi tranzaksiyalar oqimi bilan ishlash va har bir oʻtkazma hodisasini alohida tahlil qilish. Barcha oʻqish amallari lokalda bepul mavjud:
{ "mcpServers": { "ton": { "command": "npx", "args": ["-y", "@tonnode/mcp"] } } }
Kiruvchi USDT-toʻlov ichkaridan qanday koʻrinadi: jetton internal_transfer (op 0x178d4519)
Kimdir sizga USDT oʻtkazganda quyidagi zanjir ishga tushadi:
- Joʻnatuvchining jetton-hamyoni summani hisobdan chiqaradi va sizning jetton-hamyoningizga
internal_transferinternal-xabarini (op0x178d4519) yuboradi. - Sizning jetton-hamyoningiz mablagʻni hisobga oladi va — faqat joʻnatuvchi
forward_ton_amount > 0ilova qilgan boʻlsa — qoʻshimcha ravishda sizning asosiy hamyoningizgatransfer_notificationichki xabarini (op0x7362d09c) yuboradi.
Mana shu yerda eng muhim ajralish nuqtasi yashiringan. transfer_notification — «sizga jetton keldi» degan qulay bildirishnoma, ammo u asosiy hamyonga — jetton egasining hamyoniga — va faqat forward_ton_amount > 0 boʻlgandagina yuboriladi. Agar joʻnatuvchi nol qoʻysa — jetton-hamyon mablagʻni baribir hisobga oladi, lekin egasiga bildirishnoma yuborilmaydi.
Jetton-hamyonning oʻz tarixida esa tushum har doim qayd etilgan boʻladi — forward_ton_amount dan qatʼi nazar, kiruvchi internal_transfer sifatida. Shuning uchun ishonchli aniqlash oluvchining jetton-hamyoni tarixini soʻrash va aynan internal_transfer ni tahlil qilish asosida quriladi. Uning TEP-74 boʻyicha tuzilishi:
internal_transfer#178d4519
query_id: uint64
amount: (VarUInteger 16) // jettonning raw-birliklari
from: MsgAddress // TOʻLOVCHI-egasining manzili (odam)
response_address: MsgAddress
forward_ton_amount: (VarUInteger 16)
forward_payload: (Either Cell ^Cell) // izoh/memo
Ikkita muhim jihat:
frommaydoni — bu haqiqiy toʻlovchining (odam-egasining) manzili, uning jetton-hamyoni manzili emas. Aynan uni joʻnatuvchi sifatida logga yozish kerak. Eʼtibor bering: bu yerda xabar darajasidagi manzil (tx.in_msg.source) — toʻlovchining jetton-hamyoni, odam-toʻlovchi esa xabar tanasidagifrommaydonida koʻrsatiladi.amountmaydonidagi summa — raw-birliklarda, qayta hisoblashga quyida qaytamiz.
Bundan arxitektura haqidagi asosiy xulosa
Ishonchli aniqlash asosiy hamyonda notification kelishini kutish emas, balki aynan oluvchining jetton-hamyoni tarixini soʻrash asosida quriladi. Uning tarixida tushum har doim koʻrinadi, forward_ton_amount dan qatʼi nazar. Agar siz qoʻshimcha ravishda asosiy hamyonni ham tinglasangiz, u yerda transfer_notification ni (op 0x7362d09c, joʻnatuvchi — sender maydonida) ushlaysiz — ammo bu yagona haqiqat manbayi emas, faqat forward_ton_amount > 0 holati uchun bonus, xolos.
get_transactions orqali tarixni oʻqiymiz va internal_transfer ni tahlil qilamiz
Avval oluvchining jetton-hamyoni manzili kerak. Uni oflayn hisoblab chiqarmang va tikerga ishonmang — USDTning master-kontraktining oʻzidan soʻrang: USDT masterida get_wallet_address chaqiruvi (run_get_method orqali) haqiqiy masterga bogʻlangan sizning USDT-jetton-hamyoningiz manzilini deterministik tarzda qaytaradi. Soʻngra shu jetton-hamyonning tranzaksiyalarini get_transactions orqali soʻraymiz.
Polling siklidagi agentga prompt:
USDT masterida — EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
manzilida — get_wallet_address metodi va argument sifatida
UQ...myservice egasining manzili bilan run_get_method ni chaqir.
Bizning USDT-jetton-hamyonimiz manzilini qaytar. Soʻngra
get_transactions orqali shu jetton-hamyonning soʻnggi tranzaksiyalarini
oʻqi. Har bir kiruvchi internal_transfer (op 0x178d4519) uchun query_id,
amount (raw), from maydoni (toʻlovchi manzili), forward_payload dagi
matnli izoh, shuningdek tranzaksiyaning lt va hash ini qaytar.
Bitta tranzaksiyani qayta ishlash psevdokodi:
for (const tx of txs) {
const body = tx.in_msg?.decoded; // kiruvchi xabarning tahlil qilingan tanasi
if (body?.op !== 0x178d4519) continue; // internal_transfer emas — oʻtkazib yuboramiz
const rawAmount = BigInt(body.amount); // raw-birliklar, odam oʻqiydigan koʻrinishda EMAS
const payer = body.from; // toʻlovchi-egasining manzili
const memo = parseComment(body.forward_payload);
const key = `${tx.lt}:${tx.hash}`; // dedup uchun identifikator
// …tekshirish va hisobga olish quyida
}
Asosiy akkaunt holatini get_account_state orqali bir marta tekshirib koʻrish ham foydali (u faolmi, soʻnggi tranzaksiya qachon boʻlgan) — bu poller «tirik»mi va ortda qolib ketmaganmi, shuni tushunishga yordam beradi.
Hamma narsani decimals hal qiladi: get_jetton_info va raw-summani qayta hisoblash (USDT = 6, 9 emas)
Oʻtkazmadagi amount maydoni — jettonning raw-birliklarida, verguli boʻlmagan butun son. Uni odam oʻqiydigan summaga aylantirish uchun decimals kerak. Aynan shu yerda eng koʻp integratsiya buziladi.
Jettonlarning koʻpchiligida decimals = 9. TONdagi USDT (Tether) uchun esa — decimals = 6. Yaʼni:
1 USDT = 1 000 000 raw (10^6, 10^9 emas)
Agar odat boʻyicha USDTning raw-summasini 10^9 ga boʻlsangiz, mijozning 50 USDTlik toʻlovi sizda 0.05 ga aylanadi — 1000 barobar kam. Teskari yoʻnalishdagi xato ham xuddi shunday osonlik bilan 1000 barobar koʻp summani hisobga olib qoʻyadi. Boʻluvchini hardkod qilmang — decimals ni get_jetton_info orqali on-chain metamaʼlumotlardan oling:
const info = await getJettonInfo(USDT_MASTER); // name, symbol, decimals, emissiya
const decimals = info.decimals; // USDT uchun = 6
const human = Number(rawAmount) / 10 ** decimals; // 50000000 → 50.0
decimals ni keshda tiker boʻyicha emas, master manzili boʻyicha saqlang: tiker soxtalashtiriladi, decimals ni esa aniq kontraktga bogʻlash kerak. Qoida oddiy: decimals har doim get_jetton_info dan olinadi, summa esa ekranga chiqarilgunga qadar faqat BigInt/raw koʻrinishida saqlanadi.
Kelib chiqishi, summa va izohni tekshiramiz — soxta jettonlarni ajratib tashlaymiz
Endi xavfsizlik uchun eng muhim qismi. Quyidagilar — ularsiz hisobga olishning umuman iloji yoʻq boʻlgan tekshiruvlar.
1. Master-kontrakt — faqat haqiqiy USDT. Har kim «USDT» nomi va «USD₮» ramzi bilan jetton chiqarib, sizga 1000 «USDT» lik oʻtkazma yuborishi mumkin. Agar toʻlovni tiker boʻyicha moslashtirsangiz — sizni besh daqiqada aldashadi. Haqiqiy Tetherga yagona ishonchli bogʻlanish — haqiqiy USDT master-kontrakti sizning egalik manzilingiz uchun hisoblab bergan jetton-hamyon bilan ishlash:
Tether USD₮ master: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
Aynan shuning uchun jetton-hamyon manzilini shu masterdagi get_wallet_address on-chain chaqiruvidan olamiz (yuqoriga qarang), tarixni esa faqat shu — boshqa hech qanday — kontraktdan soʻraymiz. Unga tushgan har qanday internal_transfer kafolatli ravishda oʻsha masterning qonuniy jetton-hamyonlaridan biridan keladi: soxta «USDT» boshqa masterning hamyonlarida yashaydi va sizning manzilingizgacha shunchaki yetib kelmaydi. Shuning uchun bu arxitekturada tx.in_msg.source ni biror narsa bilan solishtirish shart emas — himoya kuzatuv nuqtasini toʻgʻri tanlashning oʻzi bilan taʼminlanadi. Va shuni hisobga oling: parse_address — bu faqat EQ/UQ/raw formatlarining oflayn konvertatsiyasi, u jetton-hamyon manzilini hisoblamaydi; buning uchun aynan get_wallet_address on-chain chaqiruvi kerak. Uni solishtirishdan oldin manzillarni normallashtirish uchun qoʻllash qulay — shunda bitta manzilning turli koʻrinishlarini har xil deb hisoblab yubormaysiz.
2. Invoys bilan moslashtirish uchun izoh (memo). Matnli izoh forward_payload da standart formatda saqlanadi: op prefiksi 0x00000000 (4 ta nol bayt) + UTF-8 satr. Aynan u orqali kiruvchi oʻtkazmani muayyan buyurtma bilan bogʻlaysiz.
3. Summa va toʻlovchi. Qayta hisoblangan summani invoys boʻyicha kutilgani bilan solishtiring; kerak boʻlsa, tarix va antifrod uchun from (toʻlovchi manzili) ni qayd qilib qoʻying.
// 1. Kelib chiqishi kafolatlangan: biz haqiqiy USDT masterining
// get_wallet_address chaqiruvi qaytargan AYNAN oʻsha jetton-hamyon
// tarixini oʻqiymiz — demak, bu yerdagi har qanday oʻtkazma haqiqiy.
// 2. Izoh → invoys
const invoice = invoices.get(memo);
if (!invoice) continue;
// 3. Summa mos keladimi?
if (human < invoice.expectedAmount) markUnderpaid(invoice);
// 4. Toʻlovchi — loglar/antifrod uchun
log({ payer, memo, human, lt: tx.lt, hash: tx.hash });
Idempotentlik: toʻlovni ikki marta hisobga olmaslik uchun lt + hash boʻyicha dedup
Polling har doim oʻzi bilan oʻzi kesishadi: jadval boʻyicha ishga tushadi, ishdan chiqadi, qayta urinadi, soʻrov oynalari bir-birining ustiga tushadi. Dedupsiz siz ertami-kechmi bitta toʻlovni ikki marta hisobga olasiz.
TONda har bir tranzaksiya logical time (lt) + hash juftligi bilan yagona tarzda identifikatsiya qilinadi. Bu — sizning tabiiy idempotentlik kalitingiz:
const key = `${tx.lt}:${tx.hash}`;
if (await seen.has(key)) continue; // allaqachon qayta ishlangan — chiqamiz
await creditInvoice(invoice, human); // hisobga olish
await seen.add(key); // oʻsha DB tranzaksiyasida qayd qilamiz
Kalit sifatida oʻtkazmadagi query_id ni ham ishlatish mumkin, ammo (lt, hash) jetton-hamyonning har qanday kiruvchi tranzaksiyasi uchun, jumladan forward_ton_amount = 0 holati uchun ham ishlaydi. Qayta ishlangan kalitlarni bazada doimiy saqlab boring va hisobga olishni yagonalik tekshiruvi bilan bitta DB tranzaksiyasida bajaring — shunda parallel workerlar dublikat yaratmaydi.
Vaqti-vaqti bilan rekonsiliatsiya qilib turish kerak: har N daqiqada jetton-hamyonning haqiqiy balansini (get_jetton_balance orqali — u manzilni on-chain hisoblaydi va balansni bitta chaqiruvda qaytaradi) hisobga olingan barcha toʻlovlar summasi bilan solishtiring. Nomuvofiqlik — qayerdadir oʻtkazma oʻtkazib yuborilgani yoki ikki marta hisoblangani haqidagi signal; u hatto bitta polling-sikl nosozlik bergan holatni ham ochib beradi.
Yuk ostida barqaror throughput: ommaviy limitlar oʻrniga oʻz kalitingiz
Toʻlov pollingi — bu doimiy, tekis soʻrovlar oqimi: N ta hamyon × soʻrov chastotasi. Va aynan shu yerda ommaviy infratuzilma tor joyga aylanadi.
Global configdagi ommaviy liteserverlar — umumiy va limitlangan: yuk ostida not ready deb javob beradi yoki ADNL-taymautga ketadi. Kalitsiz ommaviy HTTP-API lar (toncenter, tonapi.io) sekundiga taxminan 1 ta soʻrovni koʻtaradi va limitdan oshganda halol HTTP 429 «Too Many Requests» qaytaradi. Eʼtibor bering: limitning haqiqiy kodi — aynan 429, TON-hamjamiyat boʻylab yurgan mem «228» emas, u umuman API kodi emas. Har necha soniyada tarixni soʻrab turadigan kassa uchun bu limitlar — oʻtkazib yuborilgan va kechikkan hisobga olishlar demakdir. Oʻz kaliti bor provayderni qanday tanlash — alohida tahlilda.
Toʻlovlarni aniqlash uchun kerak boʻladigan barcha oʻqish vositalari — get_transactions, run_get_method, get_jetton_balance, get_jetton_info, parse_address, get_account_state — lokalda bepul mavjud: npx -y @tonnode/mcp. @tonnode/mcp paketi — open source (MIT) va TONning nativ ADNL-protokoli orqali, HTTP-qatlamlarsiz ishlaydi. Polling prodga oʻtganda va kafolatlangan throughput kerak boʻlganda, oʻz kalitli hosted-endpoint ulanadi:
{
"mcpServers": {
"ton": {
"type": "http",
"url": "https://mcp.tonnode.io/mcp",
"headers": { "Authorization": "Bearer tn_live_…" }
}
}
}
Tariflar faqat daqiqadagi soʻrovlar limiti bilan farqlanadi — 16 ta vositaning barchasi har birida mavjud:
- Hobby — abadiy bepul, 60 soʻrov/daq. Kalit tizimga kirgandan soʻng darhol beriladi, kartasiz.
- Pro — $29/oy, 300 soʻrov/daq.
- Scale — $199/oy, 1200 soʻrov/daq.
Toʻlov pollingini boshlash uchun bepul 60 soʻrov/daq 429 xatosiz barqaror soʻrov yuborib turish uchun yetarli. Agar shu asosda toʻlovlarni oʻzi tahlil qiladigan AI-agent qurayotgan boʻlsangiz — bu qanday tashkil etilgani TONdagi AI-agentlar uchun MCP boʻyicha qoʻllanmada koʻrib chiqilgan.
Bepul Hobby kalitidan boshlang — toʻlov pollingi uchun 60 req/min, kartasiz: tonnode.io/dashboard?plan=hobby. Yuk oshganda va bitta worker yetmay qolganda — Pro va Scale limitlarini solishtiring.
TONda USDTni ishonchli aniqlash boʻyicha chek-list:
- Asosiy hamyon balansini emas, oluvchining jetton-hamyoni tarixini
get_transactionsorqali soʻrang. - Bu jetton-hamyonning manzilini haqiqiy USD₮ masterida (
EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs)get_wallet_address(run_get_method) on-chain chaqiruvidan oling — shunda unga kelgan har qanday oʻtkazma kafolatli ravishda haqiqiy USDT boʻladi. - Op
0x178d4519bilan kiruvchiinternal_transferlarni qidiring — aynan ularforward_ton_amountdan qatʼi nazar jetton-hamyon tarixida har doim qayd etilgan boʻladi.transfer_notification(0x7362d09c) esa asosiy hamyonga va faqatforward_ton_amount > 0boʻlgandagina ketadi. - Toʻlovchi manzilini
frommaydonidan, summani esaamount(raw) dan oling. decimalsniget_jetton_infodan oling. USDT = 6, 9 emas.- Invoysni
forward_payloaddagi izoh boʻyicha moslashtiring (op0x00000000+ UTF-8). (lt, hash)boʻyicha dedup — qatʼiy bir marta hisobga oling.- Barqaror throughputni ommaviy limitlar bilan emas, oʻz kalitingiz bilan ushlab turing.
Agentingizga TONga yoʻl oching
16 ta MCP-vosita: oʻqish, nokastodial svoplar, krosscheyn va hamyonlar. Bepul tarif — 60 soʻrov/daq, karta kerak emas.