Refunding Telegram Stars: How refundStarPayment Actually Works

Refunding Telegram Stars: How refundStarPayment Actually Works
What you will ship. A working
/paysupportflow that lets a Telegram Stars customer ask for a refund inside the chat, an auditedrefundStarPaymentcall against the originaltelegram_payment_charge_id, an operator-side ledger entry that survives the App Store/Google Play clawback risk, and a clean rule for refunding subscription charges (which never auto-cancels the subscription — that is a separateeditUserStarSubscriptioncall). Two API arguments, one decision tree, and the cleanest customer-support story in the Stars ecosystem.
If you have a paid Stars surface live and no refund handling, you are already accruing technical debt — Autogram's scheduler can hand off the refund webhook to your bot the same way it hands off paid drops.
Prerequisites
Every step below assumes you already have:
- A bot that has accepted at least one Stars payment. No charge means no
telegram_payment_charge_idto refund against. - A persistent record of every
successful_paymentkeyed bytelegram_payment_charge_id→(user_id, tier_id, amount, paid_at). SQLite is fine; in-memory state is not. - A
/paysupportcommand handler. Telegram's Stars ToS explicitly tells users to send/paysupportto your bot when they want a refund. Bots that ignore it lose the dispute escalation path automatically. - A library on Bot API 7.4+ —
refundStarPaymentshipped with the Stars currency itself (June 2024), so anything that supportsXTRsupports refunds.
Step 1 — Decide if a refund is the right answer
Photo by Yan Krukau on Pexels
A refund is the right answer when one of the following is true. Anything else is a candidate for a credit, an apology, or a feature-request log entry — not a refund:
| Trigger | Refund? | Why |
|---|---|---|
| Promised content was not delivered (404, broken link, missing video) | Yes, full | Telegram ToS section 3.1 explicitly covers this case |
| User accidentally bought twice within a short window | Yes, the duplicate only | Goodwill move; the dupe-charge has no business value |
| Subscription renewed and user did not realize it was recurring | Yes, last period only | Refund the most recent renewal; do not chase older periods |
| Content delivered but user changed their mind | No | Per ToS: "All sales of Telegram Stars are final" once the digital good is delivered |
| User wants a partial refund of a multi-item paid post | No (impossible) | refundStarPayment takes no amount parameter — refunds are full or none |
| Charge is older than ~6 months (no documented limit, but practical) | Maybe | API does not block it, but Telegram may have already processed a clawback for it |
Useful internal rule: a refund makes the customer whole; a credit makes the next purchase nicer. When unsure, default to a credit — it preserves the audit log and keeps LTV intact. Refunds are reserved for "we did not deliver".
Step 2 — Wire up /paysupport so users can ask cleanly
When a user opens Settings → Telegram Stars → Transactions and taps any bot transaction, Telegram's UI shows a "Contact bot for refund" button that fires /paysupport. If your bot does not handle /paysupport, the user lands in a dead chat — and Telegram's escalation flow notes that "the developer refused to process a legitimate refund". That escalation can result in Telegram debiting Stars from your bot's balance later. Handle the command:
@router.message(Command("paysupport"))
async def on_paysupport(message: Message, db):
user_id = message.from_user.id
recent = await db.last_charges(user_id, limit=5)
if not recent:
return await message.answer(
"We do not have a recent Stars charge from you on file. "
"Open Settings → Telegram Stars → Transactions to see the bot transaction ID."
)
buttons = [
[InlineKeyboardButton(
text=f"Refund {c.amount}⭐ from {c.paid_at:%Y-%m-%d}",
callback_data=f"refund:{c.charge_id}",
)] for c in recent
]
await message.answer(
"Pick the charge you would like a refund for. "
"Refunds return the full original amount; partial refunds are not supported.",
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
)
Two nuances to internalize:
- Always show amount and date before taking action. Stars purchases blur together and a one-tap refund of the wrong charge spawns a second ticket.
- Never auto-refund without inline confirmation. Operators who ship "every
/paysupport= immediate refund" find their bot drained inside a week by users testing the flow.
Step 3 — Call refundStarPayment
Photo by Towfiqu barbhuiya on Pexels
Two arguments, one Bot API call, one Boolean response. The minimal handler for the inline button from Step 2:
@router.callback_query(F.data.startswith("refund:"))
async def on_refund_confirm(query: CallbackQuery, bot: Bot, db):
charge_id = query.data.removeprefix("refund:")
charge = await db.get_charge(charge_id)
if not charge or charge.user_id != query.from_user.id:
return await query.answer("Charge not found.", show_alert=True)
if charge.refunded_at:
return await query.answer("Already refunded.", show_alert=True)
try:
await bot.refund_star_payment(
user_id=charge.user_id,
telegram_payment_charge_id=charge_id,
)
except TelegramBadRequest as e:
# CHARGE_ALREADY_REFUNDED, USER_ID_INVALID, CHARGE_ID_EMPTY, USER_BOT_REQUIRED
await db.log_refund_failure(charge_id, str(e))
return await query.answer(f"Refund failed: {e.message}", show_alert=True)
await db.mark_refunded(charge_id, refunded_by=query.from_user.id)
await query.message.edit_text(
f"Refunded {charge.amount}⭐ for charge {charge_id[:12]}…. "
f"The Stars are back in your wallet immediately."
)
Behavior to plan around:
- Immediate, synchronous. User's Stars wallet credited and bot's balance debited atomically. No pending state.
- Four documented errors:
CHARGE_ALREADY_REFUNDED,CHARGE_ID_EMPTY,USER_BOT_REQUIRED,USER_ID_INVALID. Anything else is transport or rate-limit — retry with backoff. - No partial refunds. Sold a 1,000-Star bundle and user disputes 200 Stars? Refund full + new charge for the kept items. Plan SKU sizing accordingly.
- Refunding a subscription charge does not cancel the subscription. Next 30-day renewal still fires unless you also call
editUserStarSubscription(is_canceled=True)against the sametelegram_payment_charge_id. Refund first, cancel second.
Step 4 — Reconcile the refund in your books
Photo by Mikhail Nilov on Pexels
The bot side is one call; the books side is three things to keep aligned:
- Your internal ledger. Mark the row
refunded_at = now(), refunded_by = <admin user_id>. Never delete the original charge — refunds are debits against an existing credit. - The Stars dashboard balance. Refunds appear immediately in Stars-out. If you withdraw to TON on a schedule, a refund can push withdrawable balance below the 500-Star floor.
- The clawback risk. If the user later disputes via App Store or Google Play, Telegram retains the right to debit additional Stars from your balance to cover the platform refund. Keep float against high-value charges.
For any finance handoff, the row your accountant needs is: refund_id, original_charge_id, user_id, amount, original_paid_at, refunded_at, reason_code. The Bot API does not return a refund_id — synthesize one from (charge_id + refunded_at).
Common mistakes
Four mistakes that turn a clean refund into a ticket spiral:
- Refunding without persisting the result. Call
refundStarPaymentbut skip writingrefunded_at, and the user's next/paysupportshows the same charge as refundable. Second call returnsCHARGE_ALREADY_REFUNDEDand the user sees an error. - Refunding a subscription charge and forgetting
editUserStarSubscription. User expected "stop billing me"; you delivered "last month back" but left the subscription armed. Next renewal in 30 days = angrier ticket. - Auto-refunding from
/paysupportwith no inline confirmation. Some users tap to test. Treat the inline button as the contract. - Treating "no eligibility window" as "refund anytime is safe". The API does not block old charges, but Telegram may already have settled the original purchase with Apple/Google. Refunding then can leave your bot's balance net-negative.
Related reading
- Telegram Star Subscriptions Setup: A Recurring-Revenue How-To — the parent piece. Refunding subscription charges starts there: every refund needs a
telegram_payment_charge_idyou persisted fromsuccessful_payment. - Telegram Stars Monetization: A 2026 Toolkit for Channel Owners — the broader Stars surface. Refund handling is the operational baseline that lets the rest of the toolkit work without burning trust.
- Telegram Channel Bot for Creators: A Five-Hour-a-Week Playbook — solo-creator refund framing: when one ticket a week is normal, when it is a content-quality signal.
- Telegram Bot API Rate Limits at Scale — the throttle ceiling that also applies to refund flows during a viral-bug spike.
FAQ
Can I refund only part of a Stars payment?
No. refundStarPayment takes only user_id and telegram_payment_charge_id — no amount parameter, no partial refunds. Workaround: refund in full, then re-invoice for the kept items; or grant a Stars-equivalent in-bot credit for the disputed portion.
Is there a time limit on Telegram Stars refunds?
Telegram does not document a hard eligibility window. The only API blocker is CHARGE_ALREADY_REFUNDED. In practice, refunds older than ~3–6 months become risky because Telegram may have already settled the original purchase with Apple or Google, opening you to a downstream clawback debit.
Does refunding a subscription charge cancel the subscription?
No. refundStarPayment returns Stars from one charge only. The renewal cycle keeps running. To stop future renewals, also call editUserStarSubscription(user_id, telegram_payment_charge_id, is_canceled=True) against the same charge ID. Refund first, cancel second.
What happens to the user's Stars after a refund?
Immediately credited back to the user's Stars balance. They can spend them on any other Stars surface, withdraw via Fragment where supported, or — in the case of an Apple/Google clawback — see them disappear when the platform refund completes.
Do I have to handle /paysupport?
Yes, in practice. Telegram's UI directs users to /paysupport from Settings → Stars. A bot that ignores it leaves the user with no in-chat resolution; the user can then escalate to Telegram, who may debit Stars from your balance to cover the refund. Handle it even with just a "we will reply within 24 hours" response.
Can a user dispute a Stars purchase through Apple or Google?
Yes — Stars are bought via in-app purchase, so platform-level refunds are possible. If a user does this after you delivered the digital good, Telegram can debit equivalent Stars from your bot's balance to compensate. Keep a Stars float against high-value charges.
Bottom line
Refunds are a two-argument API call wrapped in a four-step operational discipline: decide the refund is right, give the user a clean /paysupport flow, call refundStarPayment with confirmation, reconcile the books. The technical surface is tiny; the business surface is everything around it. Ship the /paysupport handler before you ship your next paid drop — the trust dividend compounds and the clawback risk shrinks.
Plug Autogram into your bot and let your scheduler handle paid drops on the same calendar that runs refund webhooks — one operational surface for both.
Image credits
- Hero image — Photo by Yan Krukau on Pexels
- Inline image #1 — Photo by Towfiqu barbhuiya on Pexels
- Inline image #2 — Photo by Mikhail Nilov on Pexels
Related Posts

How to Add a Telegram Mini App to Your Channel (No-Code Guide)
Learn how to attach a Telegram Mini App to your channel and start accepting Stars payments — without writing a single line of code. A practical operator guide for 2026.

TON to Fiat for Channel Operators: Fragment Withdrawal Walkthrough
Step-by-step guide to withdrawing Telegram channel revenue — ad TON and Stars — via Fragment, converting on a crypto exchange, and banking the proceeds.

Telegram Star Subscriptions Setup: A Recurring-Revenue How-To
End-to-end setup for Telegram Star Subscriptions — invoice links, recurring webhooks, charge-ID persistence, and clean cancel/resume flow with editUserStarSubscription.
Subscribe to our newsletter
Get the latest Telegram growth tips, automation strategies, and platform updates delivered to your inbox.
Or follow our Telegram channel