By BluffKing · · Updated
A Telegram Test Charge Incident
A Telegram test led to S$1,077 in real top-ups. Separate invoice and funding ledgers, then inspect today’s initial-invoice limit and explicit exceptions.
On July 15, 2026 we were testing the Telegram Stars subscription on Telegram’s official test environment (the Bot API’s /test namespace). The bot side behaved perfectly: a 1,000 XTR test invoice was created, paid, activated and finally refunded in full. But we paid from the stable Telegram app downloaded from the App Store, the test account had no Stars, and the client opened its normal Apple in-app purchase sheet: S$29 bought 1,000 Stars that paid the test invoice, and after the test stack was torn down, 35,000 Stars for the next day’s renewal test cost S$1,048, for a total of S$1,077 in real money.
The root cause is that paying a Stars invoice crosses three ledgers: the bot’s invoices, the account’s Stars balance, and the Apple in-app purchase that funds that balance. /test isolates only the first; it does not turn the stable client into a StoreKit sandbox, and refundStarPayment returns Stars, not the cash Apple collected. Our test runbook said “the Test Server flow cannot charge real money”, which was true for the invoice, false for the top-up, and had never been checked at a real payment sheet.
Apple granted full refunds for both purchases on July 16. We did not spend or delete the 36,000 Stars, lined up Telegram’s “via App Store” top-ups with Apple’s charges to the minute, put both purchases in one support ticket asking for manual exception review, and described it honestly as a mistaken test purchase, not fraud; the decision is Apple’s, and this is a path that worked once, not a guarantee.
The rule no longer lives only in a document; it is a fail-closed server guard (enforce_telegram_test_server_money_guard in server/src/handlers/billing_configuration.rs). Non-production orders against a real provider must come from a dedicated restricted runtime; the initial invoice must be exactly 1 XTR, each run gets one initial invoice, and that budget is consumed in the same database transaction as the order; the operator must declare the balance already in the test account and confirm it is a non-store test balance, with one exception: a refunded store balance that was explicitly authorized and comes with a valid authorization ID. These are operator declarations the code cannot verify, and the guard cannot stop someone topping up by hand in the Telegram client, so any real purchase sheet during a test is cancelled, and without a trusted balance the real-provider test stops. The review also fixed two bugs: lapsed subscriptions could not be cancelled or refunded because SUBSCRIPTION_NOT_ACTIVE was treated as a hard failure, and the subscription period was hardcoded to 30 days, which made renewal tests impossible.
The lesson: test mode is a property of each ledger, so if a payment crosses several ledgers, “this is fake money” must be checked on each one. Any top-up-then-spend system, such as game coins, platform credits or token wallets, has the same gap; treat every purchase sheet that appears during a test as real money.