B Botex Archive

Extensions / Tronado

Tronado 1.0.2

Toman top-ups through the Tronado gateway. The customer enters an amount in toman, pays by bank card in Tronado's mini-app, Tronado sends the equivalent TRX to your wallet, and the bot credits the wallet once the order is accepted. Paid orders are watched for disputes and reversed if annulled.

stable 6 installs

Install

Install it

php bin/console ext:install Tronado

Update it later

php bin/console ext:update Tronado

Remove it

php bin/console ext:remove Tronado

Add --dry-run to any of these to see exactly which files would change before anything is written.

Readme

Tronado

Toman top-ups through the [Tronado](https://t.me/TronadoSupp) gateway. The

customer enters an amount in toman and pays by bank card inside Tronado's

mini-app. Tronado sends the equivalent TRX to your TRON wallet, and the

bot credits the customer's wallet once Tronado accepts the order.

It registers itself as a payment method (tronado) with core's top-up

system. It shows up in /topup, can be switched off from **/admin →

Top-ups → 💳 Payment methods**, and the money it brings in is attributed to

it on the financial report.

Installing

php bin/console ext:install Tronado

Two settings are required. Both come from Tronado: get an API key from

Tronado support (@TronadoSupp) once your business is verified.

php bin/console ext:set Tronado api_key        "YOUR-TRONADO-API-KEY"
php bin/console ext:set Tronado wallet_address "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

wallet_address is the TRON address Tronado pays the TRX to. It is checked

against the shape of a TRON address (T + 33 base58 characters). A value

that doesn't fit is treated as not set and stops the method, because a

typo here would send every customer's money to somebody else.

The API key can live in .env instead. ext:settings Tronado prints

the setting in full, so .env is the better home on a shared server:

TRONADO_API_KEY=your-tronado-api-key

The setting wins when both are present. If you move the key to .env,

clear the old setting or it keeps being used:

php bin/console ext:reset Tronado api_key

The payment method's line in /admin → Top-ups → 💳 Payment methods

says which one is in use (API key from .env or API key from settings).

A key exported by systemd or the shell works too. Restart the job worker

after changing .env (php bin/console jobs:service --restart), because

it reads .env only when it starts.

Then switch the method on in /admin → Top-ups → 💳 Payment methods.

The line under its name says what is missing while it is not configured.

Finally, make sure the job worker is running (`php bin/console

jobs:work, or jobs:work --run-service`). It is what notices that an order

was paid, and what watches paid orders for disputes. Without it, orders

are still credited, but only when the customer presses 🔄 بررسی پرداخت.

Wallet currency

Tronado prices everything in toman. The customer always types toman, and

the wallet is credited in its own unit:

| WALLET_CURRENCY | wallet_unit (auto) | 500,000 toman credits |

| --- | --- | --- |

| IRT / TOMAN | toman | 500,000 |

| IRR | rial | 5,000,000 |

| anything else | — | method reports itself unusable |

Set wallet_unit to toman or rial to override the guess.

What the customer sees

User: /topup → 💳 پرداخت با ترونادو
Bot:  💳 افزایش موجودی با ترونادو
      ⬇️ حداقل : 50,000 تومان
      ⬆️ حداکثر : 500,000 تومان
      💱 قیمت هر ترون : 70,000 تومان
      💵 چه مبلغی به کیف پول شما اضافه شود؟ ...
User: ۵۰۰,۰۰۰
Bot:  💳 سفارش ترونادو #12
      ➕ افزایش موجودی : 500,000 تومان
      💰 مبلغ قابل پرداخت (با کارمزد) : 596,270 تومان
      ⏳ مهلت پرداخت : 2026-10-31 14:20 (به وقت تهران)
      📍 وضعیت : ⏳ در انتظار پرداخت
                                     [💳 پرداخت]
                                  [🔄 بررسی پرداخت]

💳 پرداخت opens https://t.me/tronado_robot/customerpayment?startapp=<token>.

That is Tronado's payment page, inside Telegram, and the customer does not

need to start Tronado's bot. The same message updates itself as the order

moves: receipt under review, accepted, rejected. When the wallet is

credited, the customer also gets a separate message with the new balance.

The "amount to pay" line is Tronado's own estimate (EstimatedTomanAmount),

which includes Tronado's fee when the customer pays it.

How much is credited

The amount the customer asked for. The order is sized as toman ÷

Tronado's TRX price, which gives the TronAmount Tronado invoices. When

Tronado accepts it, that same toman amount is credited.

This is what Tronado's documentation says to credit when the fee is split

between you and the customer. It also matches the two documented fields

at both extremes, up to Tronado's rounding:

  • wage_from_business = 0: credit TomanAmountWithoutWage.
  • wage_from_business = 100: credit UserPaidTomanAmount.

Either way, the customer is never handed Tronado's fee as a bonus.

| wage_from_business | Customer pays | You receive | Credited |

| --- | --- | --- | --- |

| 0 (default) | amount + fee | the full TRX | amount |

| 100 | about the amount | TRX minus fee | amount |

| between | in proportion | in proportion | amount |

Order statuses

| Tronado OrderStatusID | What happens |

| --- | --- |

| 20 waiting for payment | keep watching; after watch_hours the order is marked expired |

| 25 receipt sent, 27 ready to transfer | keep watching closely |

| 30 accepted (or IsPaid) | credited |

| 40 rejected, 200 cancelled | closed; the customer is told |

Expired is not final. Tronado may still accept a late payment, so the

poller stops but the customer's 🔄 بررسی پرداخت button keeps working on

an expired order. If Tronado says it was paid, it is credited then. An

expired order does not count against max_open.

Disputes

After Tronado accepts an order, the card holder can dispute it ("no

deposit", "duplicate receipt", "amount was less", ...). If Tronado accepts

the dispute, it either cancels the order or changes its amount.

So a paid order keeps being checked, hourly, for dispute_watch_days

(default 7):

  • Cancelled (annulled): with reverse_annulled on (the default), the

credit is taken back with a wallet debit. The customer and every admin

in ADMIN_IDS are told. If the customer has already spent the money,

nothing is taken (a balance never goes negative) and the admins are told

to settle it by hand.

  • Amount changed: the admins are told the old and new TRX amounts. The

balance is not changed automatically; how to treat a customer who

paid less than they were credited is a decision, not arithmetic.

Set dispute_watch_days to 0 to stop watching once an order is credited.

Tronado's own limits

By default Tronado allows each customer one order a day, **500,000

toman a day and 1,000,000 toman a month**, and counts cancellations

against them. That is why max_toman defaults to 500,000 and max_open

to 1. Raise them only after Tronado support has raised yours. When Tronado

refuses an order (a limit reached, for example), its own message is shown

to the customer, so they know why.

Settings

| Key | Default | Meaning |

| --- | --- | --- |

| api_key | "" | Tronado API key; falls back to TRONADO_API_KEY in .env |

| wallet_address | "" | TRON address Tronado pays the TRX to |

| callback_url | "" | optional CallbackUrl sent to Tronado; see below |

| wage_from_business | 0 | % of Tronado's fee you absorb (0–100) |

| wallet_unit | auto | toman, rial, or auto from WALLET_CURRENCY |

| min_toman | 50000 | smallest order, toman (at least 1,000) |

| max_toman | 500000 | largest order, toman |

| max_open | 1 | unpaid orders one customer may have open (1–5) |

| watch_hours | 72 | hours an unpaid order is polled before it is marked expired (1–168) |

| poll_seconds | 60 | how often fresh orders are checked (30–3600); older ones less often |

| dispute_watch_days | 7 | days a paid order is re-checked for disputes; 0 = off |

| reverse_annulled | true | take the credit back when an order is annulled |

| timeout | 15 | seconds to wait on Tronado (3–30) |

| support_contact | "" | e.g. @yoursupport, added to messages that send the customer to support |

Why it polls instead of using the IPN callback

Tronado confirms payments by POSTing a signed IPN to a CallbackUrl. An

extension has no public HTTP endpoint; the only one this bot has is

Telegram's webhook. So this extension asks Tronado instead, with

POST /Order/GetStatusByPaymentID. That call needs only the API key and

the PaymentID this extension chose (botex-trnd-<row id>).

callback_url is sent only when you set it. The extension doesn't need

the callback. Set it only if Tronado refuses orders without one, and use

an https URL on a domain you have registered with Tronado support.

The IPN's UserPaidTomanAmount and TomanAmountWithoutWage aren't in the

status response. That is fine, because the credit is the order's own

invoice value (see "How much is credited").

Troubleshooting

When an order can't be created, the customer sees either Tronado's own

message (a limit reached, for example) or a generic "ساخت سفارش ترونادو

انجام نشد". The generic line means the problem is yours to fix, and the

reason is in the log:

grep "Tronado" storage/logs/app-$(date +%F).log

| Log says | Fix |

| --- | --- |

| Api key is wrong or not specified | api_key is wrong, or not yet activated by Tronado support |

| No Tronado API key is set | ext:set Tronado api_key ... |

| HTTP 4xx/5xx or Request failed | Tronado is unreachable from your server, or down |

| something about the wallet or callback | register the wallet / callback domain with Tronado support |

Problems with the key, wallet or callback are logged at error level, so

they reach the admins' chat when LOG_CHAT is set. They are never shown to

the customer, who can't do anything about them.

Tronado wraps most answers in an envelope the documentation doesn't show,

and returns HTTP 200 even when it refuses something:

{"IsSuccessful": false, "Code": -1, "Message": "...", "Data": null}.

The client unwraps it and reads the documented fields from Data.

Safety

  • Money moves once. Every state change (open → paid, paid →

annulled) is a conditional UPDATE, in the same transaction as the

money it implies. The credit runs through TopUpService::credit() under

tronado:<id>:credit, the reversal under tronado:<id>:reverse. The

poll job and a customer pressing check can both see "accepted" at the

same moment: one credits, the other does nothing. A failed credit rolls

back and is retried on the next look.

  • Logic is on OrderStatusID, never on the Persian

OrderStatusTitle, which Tronado documents as display-only.

  • Field names are read case-insensitively, so a Tronado answer in

camelCase is read the same as the documented PascalCase.

  • The price endpoint is called without the API key, as documented, and

with {} as its body. IIS answers a bodiless POST with a 411 that looks

like a 404.

Tables

| Table | Holds |

| --- | --- |

| tronado_orders | one row per order, from creation to the end of its dispute window |

uninstall() drops it. Wallet entries are left alone. They are referenced

topup:tronado#<row id> for credits and tronado_order#<row id> for

reversals.

Releases

Version Published Size Files
1.0.2 current 29 Oct 2026 39.9 KB 19
1.0.1 29 Oct 2026 39.1 KB 19
1.0.0 29 Oct 2026 36.7 KB 19